From 9bfd06707d52b0e9da024b8a17b496ec6d8dcecb Mon Sep 17 00:00:00 2001 From: skulidropek <66840575+skulidropek@users.noreply.github.com> Date: Thu, 18 Jun 2026 09:56:15 +0000 Subject: [PATCH 01/10] feat(api): add OpenAPI contract and fetch client --- bun.lock | 56 +- package.json | 1 + packages/api/openapi.json | 9197 +++++++++++++++++ packages/api/src/api/openapi.ts | 773 ++ packages/api/src/http.ts | 54 + packages/api/tests/openapi.test.ts | 25 + packages/app/package.json | 5 +- packages/app/src/web/api-auth-schema.ts | 13 + packages/app/src/web/api-create-project.ts | 27 +- packages/app/src/web/api-database.ts | 75 +- packages/app/src/web/api-project-core.ts | 87 +- packages/app/src/web/api-prompts.ts | 33 +- packages/app/src/web/api-schema.ts | 2 + packages/app/src/web/api-share.ts | 15 +- packages/app/src/web/api-skills.ts | 36 +- packages/app/src/web/api-tasks.ts | 39 +- packages/app/src/web/api.ts | 163 +- .../app/src/web/generated/openapi-paths.ts | 4293 ++++++++ packages/app/src/web/openapi-client.ts | 226 + scripts/write-openapi.ts | 11 + 20 files changed, 14894 insertions(+), 237 deletions(-) create mode 100644 packages/api/openapi.json create mode 100644 packages/api/src/api/openapi.ts create mode 100644 packages/api/tests/openapi.test.ts create mode 100644 packages/app/src/web/generated/openapi-paths.ts create mode 100644 packages/app/src/web/openapi-client.ts create mode 100644 scripts/write-openapi.ts diff --git a/bun.lock b/bun.lock index 6f94f9c3..59c81af7 100644 --- a/bun.lock +++ b/bun.lock @@ -43,7 +43,7 @@ }, "packages/app": { "name": "@prover-coder-ai/docker-git", - "version": "1.3.8", + "version": "1.3.10", "bin": { "docker-git": "dist/src/docker-git/main.js", }, @@ -64,6 +64,7 @@ "@gridland/web": "0.4.3", "@prover-coder-ai/docker-git-session-sync": "workspace:*", "effect": "^3.21.3", + "openapi-fetch": "^0.17.0", "react": "19.2.4", "react-dom": "19.2.4", "react-reconciler": "^0.33.0", @@ -102,6 +103,7 @@ "fast-check": "4.8.0", "globals": "^17.6.0", "jscpd": "^5.0.10", + "openapi-typescript": "^7.13.0", "typescript": "^6.0.3", "typescript-eslint": "^8.61.1", "vite": "^8.0.16", @@ -153,7 +155,7 @@ }, "packages/docker-git-session-sync": { "name": "@prover-coder-ai/docker-git-session-sync", - "version": "1.0.64", + "version": "1.0.66", "bin": { "docker-git-session-sync": "dist/docker-git-session-sync.js", }, @@ -660,6 +662,12 @@ "@prover-coder-ai/eslint-plugin-suggest-members": ["@prover-coder-ai/eslint-plugin-suggest-members@0.0.26", "", { "dependencies": { "@effect/platform": "^0.96.0", "@effect/platform-node": "^0.106.0", "@effect/schema": "^0.75.5", "@typescript-eslint/utils": "8.57.2", "effect": "^3.21.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <7.0.0" } }, "sha512-RWl1jYZTMK1p0L6GA7VXvTrtiNkbQyjkgk3mvz0Vv7ImTrctDOLFfNIRoJmhU+e5irj1u5uK2p9QoZtRzi4ILQ=="], + "@redocly/ajv": ["@redocly/ajv@8.11.2", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2", "uri-js-replace": "^1.0.1" } }, "sha512-io1JpnwtIcvojV7QKDUSIuMN/ikdOUd1ReEnUnMKGfDVridQZ31J0MmIuqwuRjWDZfmvr+Q0MqCcfHM2gTivOg=="], + + "@redocly/config": ["@redocly/config@0.22.0", "", {}, "sha512-gAy93Ddo01Z3bHuVdPWfCwzgfaYgMdaZPcfL7JZ7hWJoK9V0lXDbigTWkhiPFAaLWzbOJ+kbUQG1+XwIm0KRGQ=="], + + "@redocly/openapi-core": ["@redocly/openapi-core@1.34.15", "", { "dependencies": { "@redocly/ajv": "8.11.2", "@redocly/config": "0.22.0", "colorette": "1.4.0", "https-proxy-agent": "7.0.6", "js-levenshtein": "1.1.6", "js-yaml": "4.1.1", "minimatch": "5.1.9", "pluralize": "8.0.0", "yaml-ast-parser": "0.0.43" } }, "sha512-HAwCnNyKcs5XGQqms+9t7OdAPM/5TDstmhF+0i7tdCFato2QKuYIlyWETwkXd8c5zbltr1oB+6y9NTeQLr2d6Q=="], + "@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.0.3", "", { "os": "android", "cpu": "arm64" }, "sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw=="], "@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.0.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-PcAhP+ynjURNyy8SKGl5DQP94aGuB/7JrXJb/t7P+hanXvQVMWzUvRRhBAcg/lNRadBhoUPqSoP4xw5tR/KBEA=="], @@ -846,6 +854,8 @@ "acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "8.16.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="], + "agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="], + "ajv": ["ajv@6.14.0", "", { "dependencies": { "fast-deep-equal": "3.1.3", "fast-json-stable-stringify": "2.1.0", "json-schema-traverse": "0.4.1", "uri-js": "4.4.1" } }, "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw=="], "ansi-colors": ["ansi-colors@4.1.3", "", {}, "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw=="], @@ -958,6 +968,8 @@ "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], + "colorette": ["colorette@1.4.0", "", {}, "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g=="], + "colors": ["colors@1.4.0", "", {}, "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA=="], "commander": ["commander@5.1.0", "", {}, "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg=="], @@ -1232,6 +1244,8 @@ "htmlparser2": ["htmlparser2@10.0.0", "", { "dependencies": { "domelementtype": "2.3.0", "domhandler": "5.0.3", "domutils": "3.2.2", "entities": "6.0.1" } }, "sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g=="], + "https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="], + "human-id": ["human-id@4.1.3", "", { "bin": { "human-id": "dist/cli.js" } }, "sha512-tsYlhAYpjCKa//8rXZ9DqKEawhPoSytweBC2eNvcaDK+57RZLHGqNs3PZTQO6yekLFSuvA6AlnAfrw1uBvtb+Q=="], "human-signals": ["human-signals@1.1.1", "", {}, "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw=="], @@ -1246,6 +1260,8 @@ "indent-string": ["indent-string@5.0.0", "", {}, "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg=="], + "index-to-position": ["index-to-position@1.2.0", "", {}, "sha512-Yg7+ztRkqslMAS2iFaU+Oa4KTSidr63OsFGlOrJoW981kIYO3CGCS3wA95P1mUi/IVSJkn0D479KTJpVpvFNuw=="], + "ini": ["ini@4.1.3", "", {}, "sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg=="], "internal-slot": ["internal-slot@1.1.0", "", { "dependencies": { "es-errors": "1.3.0", "hasown": "2.0.2", "side-channel": "1.1.0" } }, "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw=="], @@ -1354,6 +1370,8 @@ "jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="], + "js-levenshtein": ["js-levenshtein@1.1.6", "", {}, "sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g=="], + "js-stringify": ["js-stringify@1.0.2", "", {}, "sha512-rtS5ATOo2Q5k1G+DADISilDA6lv79zIiwFd6CcjuIxGKLFm5C+RLImRscVap9k55i+MOZwgliw+NejvkLuGD5g=="], "js-tokens": ["js-tokens@10.0.0", "", {}, "sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q=="], @@ -1524,6 +1542,12 @@ "onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="], + "openapi-fetch": ["openapi-fetch@0.17.0", "", { "dependencies": { "openapi-typescript-helpers": "^0.1.0" } }, "sha512-PsbZR1wAPcG91eEthKhN+Zn92FMHxv+/faECIwjXdxfTODGSGegYv0sc1Olz+HYPvKOuoXfp+0pA2XVt2cI0Ig=="], + + "openapi-typescript": ["openapi-typescript@7.13.0", "", { "dependencies": { "@redocly/openapi-core": "^1.34.6", "ansi-colors": "^4.1.3", "change-case": "^5.4.4", "parse-json": "^8.3.0", "supports-color": "^10.2.2", "yargs-parser": "^21.1.1" }, "peerDependencies": { "typescript": "^5.x" }, "bin": { "openapi-typescript": "bin/cli.js" } }, "sha512-EFP392gcqXS7ntPvbhBzbF8TyBA+baIYEm791Hy5YkjDYKTnk/Tn5OQeKm5BIZvJihpp8Zzr4hzx0Irde1LNGQ=="], + + "openapi-typescript-helpers": ["openapi-typescript-helpers@0.1.0", "", {}, "sha512-OKTGPthhivLw/fHz6c3OPtg72vi86qaMlqbJuVJ23qOvQ+53uw1n7HdmkJFibloF7QEjDrDkzJiOJuockM/ljw=="], + "optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "0.1.4", "fast-levenshtein": "2.0.6", "levn": "0.4.1", "prelude-ls": "1.2.1", "type-check": "0.4.0", "word-wrap": "1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="], "outdent": ["outdent@0.5.0", "", {}, "sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q=="], @@ -1548,7 +1572,7 @@ "parse-entities": ["parse-entities@2.0.0", "", { "dependencies": { "character-entities": "1.2.4", "character-entities-legacy": "1.1.4", "character-reference-invalid": "1.1.4", "is-alphanumerical": "1.0.4", "is-decimal": "1.0.4", "is-hexadecimal": "1.0.4" } }, "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ=="], - "parse-json": ["parse-json@5.2.0", "", { "dependencies": { "@babel/code-frame": "7.27.1", "error-ex": "1.3.4", "json-parse-even-better-errors": "2.3.1", "lines-and-columns": "1.2.4" } }, "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg=="], + "parse-json": ["parse-json@8.3.0", "", { "dependencies": { "@babel/code-frame": "^7.26.2", "index-to-position": "^1.1.0", "type-fest": "^4.39.1" } }, "sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ=="], "parse5": ["parse5@7.3.0", "", { "dependencies": { "entities": "6.0.1" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="], @@ -1772,7 +1796,7 @@ "structured-field-values": ["structured-field-values@2.0.4", "", {}, "sha512-5zpJXYLPwW3WYUD/D58tQjIBs10l3Yx64jZfcKGs/RH79E2t9Xm/b9+ydwdMNVSksnsIY+HR/2IlQmgo0AcTAg=="], - "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + "supports-color": ["supports-color@10.2.2", "", {}, "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g=="], "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="], @@ -1842,6 +1866,8 @@ "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "2.3.1" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="], + "uri-js-replace": ["uri-js-replace@1.0.1", "", {}, "sha512-W+C9NWNLFOoBI2QWDp4UT9pv65r2w5Cx+3sTYFvtMdDBxkKt1syCqsUdSFAChbEe1uK5TfS04wt/nGwmaeIQ0g=="], + "uri-template-router": ["uri-template-router@1.0.0", "", {}, "sha512-WKcL9ZSIEhHE3f5P4Z47Tf0nWbcgV1ISb/OBuF8YKEYi0SQOyTLCzM6B/gAKFWZhRhqA+C/Ks8UXe2qU5W0FVg=="], "url-template": ["url-template@3.1.1", "", {}, "sha512-4oszoaEKE/mQOtAmdMWqIRHmkxWkUZMnXFnjQ5i01CuRSK3uluxcH1MRVVVWmhlnzT1SCDfKxxficm2G37qzCA=="], @@ -1900,6 +1926,10 @@ "yaml": ["yaml@2.8.2", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A=="], + "yaml-ast-parser": ["yaml-ast-parser@0.0.43", "", {}, "sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A=="], + + "yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="], + "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], "yoga-layout": ["yoga-layout@3.2.1", "", {}, "sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ=="], @@ -2006,6 +2036,10 @@ "@prover-coder-ai/eslint-plugin-suggest-members/effect": ["effect@3.21.2", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "fast-check": "^3.23.1" } }, "sha512-rXd2FGDM8KdjSIrc+mqEELo7ScW7xTVxEf1iInmPSpIde9/nyGuFM710cjTo7/EreGXiUX2MOonPpprbz2XHCg=="], + "@redocly/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + + "@redocly/openapi-core/minimatch": ["minimatch@5.1.9", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw=="], + "@rolldown/binding-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.4", "", { "dependencies": { "@tybys/wasm-util": "^0.10.1" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" } }, "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow=="], "@ton-ai-core/vibecode-linter/ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "3.1.3", "fast-uri": "3.1.0", "json-schema-traverse": "1.0.0", "require-from-string": "2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="], @@ -2028,6 +2062,8 @@ "@vitest/eslint-plugin/@typescript-eslint/utils": ["@typescript-eslint/utils@8.61.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.61.0", "@typescript-eslint/types": "8.61.0", "@typescript-eslint/typescript-estree": "8.61.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-3bzFt7ImFMW/jVYwJamDoe/dMOdFLSC6pom6rRjdh4SZJEYupyMzem8e7vKZLclLfpHjlwSAXOUxtKxGXUiLqA=="], + "chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + "effect/fast-check": ["fast-check@3.23.2", "", { "dependencies": { "pure-rand": "6.1.0" } }, "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A=="], "encoding-sniffer/iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": "2.1.2" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], @@ -2082,6 +2118,8 @@ "is-expression/acorn": ["acorn@7.4.1", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A=="], + "istanbul-lib-report/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + "jest-util/@types/node": ["@types/node@24.12.0", "", { "dependencies": { "undici-types": "7.16.0" } }, "sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ=="], "jest-util/ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="], @@ -2100,12 +2138,18 @@ "normalize-package-data/semver": ["semver@5.7.2", "", { "bin": { "semver": "bin/semver" } }, "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g=="], + "openapi-typescript/typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], + + "parse-json/type-fest": ["type-fest@4.41.0", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="], + "parse5/entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="], "path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], "pretty-format/ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], + "read-pkg/parse-json": ["parse-json@5.2.0", "", { "dependencies": { "@babel/code-frame": "7.27.1", "error-ex": "1.3.4", "json-parse-even-better-errors": "2.3.1", "lines-and-columns": "1.2.4" } }, "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg=="], + "read-pkg/type-fest": ["type-fest@0.6.0", "", {}, "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg=="], "read-pkg-up/find-up": ["find-up@4.1.0", "", { "dependencies": { "locate-path": "5.0.0", "path-exists": "4.0.0" } }, "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw=="], @@ -2268,6 +2312,8 @@ "@prover-coder-ai/eslint-plugin-suggest-members/effect/fast-check": ["fast-check@3.23.2", "", { "dependencies": { "pure-rand": "6.1.0" } }, "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A=="], + "@redocly/openapi-core/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "1.0.2" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], + "@ton-ai-core/vibecode-linter/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], "@ton-ai-core/vibecode-linter/effect/fast-check": ["fast-check@3.23.2", "", { "dependencies": { "pure-rand": "6.1.0" } }, "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A=="], @@ -2458,6 +2504,8 @@ "@prover-coder-ai/eslint-plugin-suggest-members/effect/fast-check/pure-rand": ["pure-rand@6.1.0", "", {}, "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA=="], + "@redocly/openapi-core/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + "@ton-ai-core/vibecode-linter/effect/fast-check/pure-rand": ["pure-rand@6.1.0", "", {}, "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA=="], "@ton-ai-core/vibecode-linter/jscpd/fs-extra/jsonfile": ["jsonfile@6.2.0", "", { "dependencies": { "universalify": "2.0.1" }, "optionalDependencies": { "graceful-fs": "4.2.11" } }, "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg=="], diff --git a/package.json b/package.json index b1a942d8..a83bfa4f 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ "dev": "bun run --cwd packages/app dev", "web:dev": "bun run --cwd packages/app dev:web", "web:build": "bun run --cwd packages/app build:web", + "web:generate-api": "bun run --cwd packages/app generate:api", "web:preview": "bun run --cwd packages/app preview:web", "web:serve": "bun run --cwd packages/app serve:web", "lint": "bun run --filter @prover-coder-ai/docker-git-terminal lint && bun run --filter @prover-coder-ai/docker-git lint && bun run --filter @effect-template/lib lint", diff --git a/packages/api/openapi.json b/packages/api/openapi.json new file mode 100644 index 00000000..f48847b1 --- /dev/null +++ b/packages/api/openapi.json @@ -0,0 +1,9197 @@ +{ + "openapi": "3.1.0", + "info": { + "title": "docker-git API", + "version": "1.0.0", + "description": "Effect contract for docker-git JSON REST endpoints." + }, + "paths": { + "/health": { + "get": { + "tags": [ + "core" + ], + "operationId": "core.health", + "parameters": [], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "cwd", + "ok", + "projectsRoot", + "revision" + ], + "properties": { + "cwd": { + "type": "string" + }, + "ok": { + "type": "boolean" + }, + "projectsRoot": { + "type": "string" + }, + "revision": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + } + } + } + }, + "400": { + "description": "The request did not match the expected schema", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/HttpApiDecodeError" + }, + { + "type": "object", + "required": [ + "error", + "message" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + } + } + } + }, + "/projects": { + "get": { + "tags": [ + "projects" + ], + "operationId": "projects.listProjects", + "parameters": [], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "projects" + ], + "properties": { + "projects": { + "type": "array", + "items": { + "type": "object", + "required": [ + "displayName", + "id", + "projectKey", + "repoRef", + "repoUrl", + "sshSessions", + "startedAtEpochMs", + "startedAtIso", + "status", + "statusLabel" + ], + "properties": { + "clonedOnHostname": { + "type": "string" + }, + "containerName": { + "type": "string" + }, + "displayName": { + "type": "string" + }, + "id": { + "type": "string" + }, + "projectKey": { + "type": "string" + }, + "repoRef": { + "type": "string" + }, + "repoUrl": { + "type": "string" + }, + "sshSessions": { + "type": "number" + }, + "startedAtEpochMs": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ] + }, + "startedAtIso": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "status": { + "type": "string", + "enum": [ + "running", + "stopped", + "unknown" + ] + }, + "statusLabel": { + "type": "string" + } + }, + "additionalProperties": false + } + } + }, + "additionalProperties": false + } + } + } + }, + "400": { + "description": "The request did not match the expected schema", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/HttpApiDecodeError" + }, + { + "type": "object", + "required": [ + "error", + "message" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + } + } + }, + "post": { + "tags": [ + "projects" + ], + "operationId": "projects.createProject", + "parameters": [], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "project" + ], + "properties": { + "ok": { + "type": "boolean" + }, + "project": { + "type": "object", + "required": [ + "containerName", + "displayName", + "id", + "projectKey", + "repoRef", + "repoUrl", + "sshSessions", + "startedAtEpochMs", + "startedAtIso", + "status", + "statusLabel", + "authorizedKeysExists", + "authorizedKeysPath", + "codexAuthPath", + "codexHome", + "envGlobalPath", + "envProjectPath", + "gpu", + "projectDir", + "serviceName", + "sshCommand", + "sshPort", + "sshUser", + "targetDir" + ], + "properties": { + "clonedOnHostname": { + "type": "string" + }, + "containerName": { + "type": "string" + }, + "displayName": { + "type": "string" + }, + "id": { + "type": "string" + }, + "projectKey": { + "type": "string" + }, + "repoRef": { + "type": "string" + }, + "repoUrl": { + "type": "string" + }, + "sshSessions": { + "type": "number" + }, + "startedAtEpochMs": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ] + }, + "startedAtIso": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "status": { + "type": "string", + "enum": [ + "running", + "stopped", + "unknown" + ] + }, + "statusLabel": { + "type": "string" + }, + "authorizedKeysExists": { + "type": "boolean" + }, + "authorizedKeysPath": { + "type": "string" + }, + "codexAuthPath": { + "type": "string" + }, + "codexHome": { + "type": "string" + }, + "envGlobalPath": { + "type": "string" + }, + "envProjectPath": { + "type": "string" + }, + "gpu": { + "type": "string", + "enum": [ + "none", + "all" + ] + }, + "projectDir": { + "type": "string" + }, + "serviceName": { + "type": "string" + }, + "sshCommand": { + "type": "string" + }, + "sshPort": { + "type": "number" + }, + "sshUser": { + "type": "string" + }, + "targetDir": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "202": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "accepted", + "cursor", + "projectId" + ], + "properties": { + "accepted": { + "type": "boolean", + "enum": [ + true + ] + }, + "cursor": { + "type": "number" + }, + "projectId": { + "type": "string" + } + }, + "additionalProperties": false + } + } + } + }, + "400": { + "description": "The request did not match the expected schema", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/HttpApiDecodeError" + }, + { + "type": "object", + "required": [ + "error", + "message" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [], + "properties": { + "repoUrl": { + "type": "string" + }, + "repoRef": { + "type": "string" + }, + "targetDir": { + "type": "string" + }, + "sshPort": { + "type": "string" + }, + "sshUser": { + "type": "string" + }, + "containerName": { + "type": "string" + }, + "serviceName": { + "type": "string" + }, + "volumeName": { + "type": "string" + }, + "secretsRoot": { + "type": "string" + }, + "authorizedKeysPath": { + "type": "string" + }, + "authorizedKeysContents": { + "type": "string" + }, + "useManagedAuthorizedKeys": { + "type": "boolean" + }, + "envGlobalPath": { + "type": "string" + }, + "envProjectPath": { + "type": "string" + }, + "codexAuthPath": { + "type": "string" + }, + "codexHome": { + "type": "string" + }, + "cpuLimit": { + "type": "string" + }, + "ramLimit": { + "type": "string" + }, + "playwrightCpuLimit": { + "type": "string" + }, + "playwrightRamLimit": { + "type": "string" + }, + "gpu": { + "type": "string", + "enum": [ + "none", + "all" + ] + }, + "dockerNetworkMode": { + "type": "string" + }, + "dockerSharedNetworkName": { + "type": "string" + }, + "enableMcpPlaywright": { + "type": "boolean" + }, + "outDir": { + "type": "string" + }, + "gitTokenLabel": { + "type": "string" + }, + "skipGithubAuth": { + "type": "boolean" + }, + "codexTokenLabel": { + "type": "string" + }, + "claudeTokenLabel": { + "type": "string" + }, + "geminiTokenLabel": { + "type": "string" + }, + "grokTokenLabel": { + "type": "string" + }, + "agentAutoMode": { + "type": "string" + }, + "up": { + "type": "boolean" + }, + "openSsh": { + "type": "boolean" + }, + "force": { + "type": "boolean" + }, + "forceEnv": { + "type": "boolean" + }, + "waitForClone": { + "type": "boolean" + }, + "async": { + "type": "boolean" + }, + "clonedOnHostname": { + "type": "string", + "description": "a string matching the pattern ^(?:[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?)(?:\\.[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?)*$", + "title": "maxLength(253)", + "minLength": 1, + "maxLength": 253, + "pattern": "^(?:[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?)(?:\\.[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?)*$" + } + }, + "additionalProperties": false + } + } + }, + "required": true + } + } + }, + "/projects/apply-all": { + "post": { + "tags": [ + "projects" + ], + "operationId": "projects.applyAllProjects", + "parameters": [], + "security": [], + "responses": { + "204": { + "description": "Success" + }, + "400": { + "description": "The request did not match the expected schema", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/HttpApiDecodeError" + }, + { + "type": "object", + "required": [ + "error", + "message" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [], + "properties": { + "activeOnly": { + "type": "boolean" + } + }, + "additionalProperties": false + } + } + }, + "required": true + } + } + }, + "/projects/down-all": { + "post": { + "tags": [ + "projects" + ], + "operationId": "projects.downAllProjects", + "parameters": [], + "security": [], + "responses": { + "204": { + "description": "Success" + }, + "400": { + "description": "The request did not match the expected schema", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/HttpApiDecodeError" + }, + { + "type": "object", + "required": [ + "error", + "message" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + } + } + } + }, + "/projects/{projectId}": { + "get": { + "tags": [ + "projects" + ], + "operationId": "projects.getProject", + "parameters": [ + { + "name": "projectId", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "project" + ], + "properties": { + "ok": { + "type": "boolean" + }, + "project": { + "type": "object", + "required": [ + "containerName", + "displayName", + "id", + "projectKey", + "repoRef", + "repoUrl", + "sshSessions", + "startedAtEpochMs", + "startedAtIso", + "status", + "statusLabel", + "authorizedKeysExists", + "authorizedKeysPath", + "codexAuthPath", + "codexHome", + "envGlobalPath", + "envProjectPath", + "gpu", + "projectDir", + "serviceName", + "sshCommand", + "sshPort", + "sshUser", + "targetDir" + ], + "properties": { + "clonedOnHostname": { + "type": "string" + }, + "containerName": { + "type": "string" + }, + "displayName": { + "type": "string" + }, + "id": { + "type": "string" + }, + "projectKey": { + "type": "string" + }, + "repoRef": { + "type": "string" + }, + "repoUrl": { + "type": "string" + }, + "sshSessions": { + "type": "number" + }, + "startedAtEpochMs": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ] + }, + "startedAtIso": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "status": { + "type": "string", + "enum": [ + "running", + "stopped", + "unknown" + ] + }, + "statusLabel": { + "type": "string" + }, + "authorizedKeysExists": { + "type": "boolean" + }, + "authorizedKeysPath": { + "type": "string" + }, + "codexAuthPath": { + "type": "string" + }, + "codexHome": { + "type": "string" + }, + "envGlobalPath": { + "type": "string" + }, + "envProjectPath": { + "type": "string" + }, + "gpu": { + "type": "string", + "enum": [ + "none", + "all" + ] + }, + "projectDir": { + "type": "string" + }, + "serviceName": { + "type": "string" + }, + "sshCommand": { + "type": "string" + }, + "sshPort": { + "type": "number" + }, + "sshUser": { + "type": "string" + }, + "targetDir": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "400": { + "description": "The request did not match the expected schema", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/HttpApiDecodeError" + }, + { + "type": "object", + "required": [ + "error", + "message" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + } + } + }, + "delete": { + "tags": [ + "projects" + ], + "operationId": "projects.deleteProject", + "parameters": [ + { + "name": "projectId", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + } + ], + "security": [], + "responses": { + "204": { + "description": "Success" + }, + "400": { + "description": "The request did not match the expected schema", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/HttpApiDecodeError" + }, + { + "type": "object", + "required": [ + "error", + "message" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + } + } + } + }, + "/projects/{projectId}/down": { + "post": { + "tags": [ + "projects" + ], + "operationId": "projects.downProject", + "parameters": [ + { + "name": "projectId", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + } + ], + "security": [], + "responses": { + "204": { + "description": "Success" + }, + "400": { + "description": "The request did not match the expected schema", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/HttpApiDecodeError" + }, + { + "type": "object", + "required": [ + "error", + "message" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + } + } + } + }, + "/projects/{projectId}/apply": { + "post": { + "tags": [ + "projects" + ], + "operationId": "projects.applyProject", + "parameters": [ + { + "name": "projectId", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "project" + ], + "properties": { + "ok": { + "type": "boolean" + }, + "project": { + "type": "object", + "required": [ + "containerName", + "displayName", + "id", + "projectKey", + "repoRef", + "repoUrl", + "sshSessions", + "startedAtEpochMs", + "startedAtIso", + "status", + "statusLabel", + "authorizedKeysExists", + "authorizedKeysPath", + "codexAuthPath", + "codexHome", + "envGlobalPath", + "envProjectPath", + "gpu", + "projectDir", + "serviceName", + "sshCommand", + "sshPort", + "sshUser", + "targetDir" + ], + "properties": { + "clonedOnHostname": { + "type": "string" + }, + "containerName": { + "type": "string" + }, + "displayName": { + "type": "string" + }, + "id": { + "type": "string" + }, + "projectKey": { + "type": "string" + }, + "repoRef": { + "type": "string" + }, + "repoUrl": { + "type": "string" + }, + "sshSessions": { + "type": "number" + }, + "startedAtEpochMs": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ] + }, + "startedAtIso": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "status": { + "type": "string", + "enum": [ + "running", + "stopped", + "unknown" + ] + }, + "statusLabel": { + "type": "string" + }, + "authorizedKeysExists": { + "type": "boolean" + }, + "authorizedKeysPath": { + "type": "string" + }, + "codexAuthPath": { + "type": "string" + }, + "codexHome": { + "type": "string" + }, + "envGlobalPath": { + "type": "string" + }, + "envProjectPath": { + "type": "string" + }, + "gpu": { + "type": "string", + "enum": [ + "none", + "all" + ] + }, + "projectDir": { + "type": "string" + }, + "serviceName": { + "type": "string" + }, + "sshCommand": { + "type": "string" + }, + "sshPort": { + "type": "number" + }, + "sshUser": { + "type": "string" + }, + "targetDir": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "400": { + "description": "The request did not match the expected schema", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/HttpApiDecodeError" + }, + { + "type": "object", + "required": [ + "error", + "message" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [], + "properties": { + "cpuLimit": { + "type": "string" + }, + "ramLimit": { + "type": "string" + }, + "playwrightCpuLimit": { + "type": "string" + }, + "playwrightRamLimit": { + "type": "string" + }, + "gpu": { + "type": "string", + "enum": [ + "none", + "all" + ] + } + }, + "additionalProperties": false + } + } + }, + "required": true + } + } + }, + "/projects/{projectId}/up": { + "post": { + "tags": [ + "projects" + ], + "operationId": "projects.upProject", + "parameters": [ + { + "name": "projectId", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "project" + ], + "properties": { + "ok": { + "type": "boolean" + }, + "project": { + "type": "object", + "required": [ + "containerName", + "displayName", + "id", + "projectKey", + "repoRef", + "repoUrl", + "sshSessions", + "startedAtEpochMs", + "startedAtIso", + "status", + "statusLabel", + "authorizedKeysExists", + "authorizedKeysPath", + "codexAuthPath", + "codexHome", + "envGlobalPath", + "envProjectPath", + "gpu", + "projectDir", + "serviceName", + "sshCommand", + "sshPort", + "sshUser", + "targetDir" + ], + "properties": { + "clonedOnHostname": { + "type": "string" + }, + "containerName": { + "type": "string" + }, + "displayName": { + "type": "string" + }, + "id": { + "type": "string" + }, + "projectKey": { + "type": "string" + }, + "repoRef": { + "type": "string" + }, + "repoUrl": { + "type": "string" + }, + "sshSessions": { + "type": "number" + }, + "startedAtEpochMs": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ] + }, + "startedAtIso": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "status": { + "type": "string", + "enum": [ + "running", + "stopped", + "unknown" + ] + }, + "statusLabel": { + "type": "string" + }, + "authorizedKeysExists": { + "type": "boolean" + }, + "authorizedKeysPath": { + "type": "string" + }, + "codexAuthPath": { + "type": "string" + }, + "codexHome": { + "type": "string" + }, + "envGlobalPath": { + "type": "string" + }, + "envProjectPath": { + "type": "string" + }, + "gpu": { + "type": "string", + "enum": [ + "none", + "all" + ] + }, + "projectDir": { + "type": "string" + }, + "serviceName": { + "type": "string" + }, + "sshCommand": { + "type": "string" + }, + "sshPort": { + "type": "number" + }, + "sshUser": { + "type": "string" + }, + "targetDir": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "400": { + "description": "The request did not match the expected schema", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/HttpApiDecodeError" + }, + { + "type": "object", + "required": [ + "error", + "message" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [], + "properties": { + "authorizedKeysContents": { + "type": "string" + }, + "useManagedAuthorizedKeys": { + "type": "boolean" + } + }, + "additionalProperties": false + } + } + }, + "required": true + } + } + }, + "/projects/{projectId}/resume": { + "post": { + "tags": [ + "projects" + ], + "operationId": "projects.resumeProject", + "parameters": [ + { + "name": "projectId", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "project" + ], + "properties": { + "ok": { + "type": "boolean" + }, + "project": { + "type": "object", + "required": [ + "containerName", + "displayName", + "id", + "projectKey", + "repoRef", + "repoUrl", + "sshSessions", + "startedAtEpochMs", + "startedAtIso", + "status", + "statusLabel", + "authorizedKeysExists", + "authorizedKeysPath", + "codexAuthPath", + "codexHome", + "envGlobalPath", + "envProjectPath", + "gpu", + "projectDir", + "serviceName", + "sshCommand", + "sshPort", + "sshUser", + "targetDir" + ], + "properties": { + "clonedOnHostname": { + "type": "string" + }, + "containerName": { + "type": "string" + }, + "displayName": { + "type": "string" + }, + "id": { + "type": "string" + }, + "projectKey": { + "type": "string" + }, + "repoRef": { + "type": "string" + }, + "repoUrl": { + "type": "string" + }, + "sshSessions": { + "type": "number" + }, + "startedAtEpochMs": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ] + }, + "startedAtIso": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "status": { + "type": "string", + "enum": [ + "running", + "stopped", + "unknown" + ] + }, + "statusLabel": { + "type": "string" + }, + "authorizedKeysExists": { + "type": "boolean" + }, + "authorizedKeysPath": { + "type": "string" + }, + "codexAuthPath": { + "type": "string" + }, + "codexHome": { + "type": "string" + }, + "envGlobalPath": { + "type": "string" + }, + "envProjectPath": { + "type": "string" + }, + "gpu": { + "type": "string", + "enum": [ + "none", + "all" + ] + }, + "projectDir": { + "type": "string" + }, + "serviceName": { + "type": "string" + }, + "sshCommand": { + "type": "string" + }, + "sshPort": { + "type": "number" + }, + "sshUser": { + "type": "string" + }, + "targetDir": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "400": { + "description": "The request did not match the expected schema", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/HttpApiDecodeError" + }, + { + "type": "object", + "required": [ + "error", + "message" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + } + } + } + }, + "/projects/{projectId}/suspend": { + "post": { + "tags": [ + "projects" + ], + "operationId": "projects.suspendProject", + "parameters": [ + { + "name": "projectId", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "project" + ], + "properties": { + "ok": { + "type": "boolean" + }, + "project": { + "type": "object", + "required": [ + "containerName", + "displayName", + "id", + "projectKey", + "repoRef", + "repoUrl", + "sshSessions", + "startedAtEpochMs", + "startedAtIso", + "status", + "statusLabel", + "authorizedKeysExists", + "authorizedKeysPath", + "codexAuthPath", + "codexHome", + "envGlobalPath", + "envProjectPath", + "gpu", + "projectDir", + "serviceName", + "sshCommand", + "sshPort", + "sshUser", + "targetDir" + ], + "properties": { + "clonedOnHostname": { + "type": "string" + }, + "containerName": { + "type": "string" + }, + "displayName": { + "type": "string" + }, + "id": { + "type": "string" + }, + "projectKey": { + "type": "string" + }, + "repoRef": { + "type": "string" + }, + "repoUrl": { + "type": "string" + }, + "sshSessions": { + "type": "number" + }, + "startedAtEpochMs": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ] + }, + "startedAtIso": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "status": { + "type": "string", + "enum": [ + "running", + "stopped", + "unknown" + ] + }, + "statusLabel": { + "type": "string" + }, + "authorizedKeysExists": { + "type": "boolean" + }, + "authorizedKeysPath": { + "type": "string" + }, + "codexAuthPath": { + "type": "string" + }, + "codexHome": { + "type": "string" + }, + "envGlobalPath": { + "type": "string" + }, + "envProjectPath": { + "type": "string" + }, + "gpu": { + "type": "string", + "enum": [ + "none", + "all" + ] + }, + "projectDir": { + "type": "string" + }, + "serviceName": { + "type": "string" + }, + "sshCommand": { + "type": "string" + }, + "sshPort": { + "type": "number" + }, + "sshUser": { + "type": "string" + }, + "targetDir": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "400": { + "description": "The request did not match the expected schema", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/HttpApiDecodeError" + }, + { + "type": "object", + "required": [ + "error", + "message" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + } + } + } + }, + "/projects/{projectId}/ps": { + "get": { + "tags": [ + "projects" + ], + "operationId": "projects.projectPs", + "parameters": [ + { + "name": "projectId", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "output" + ], + "properties": { + "output": { + "type": "string" + } + }, + "additionalProperties": false + } + } + } + }, + "400": { + "description": "The request did not match the expected schema", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/HttpApiDecodeError" + }, + { + "type": "object", + "required": [ + "error", + "message" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + } + } + } + }, + "/projects/{projectId}/logs": { + "get": { + "tags": [ + "projects" + ], + "operationId": "projects.projectLogs", + "parameters": [ + { + "name": "projectId", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "output" + ], + "properties": { + "output": { + "type": "string" + } + }, + "additionalProperties": false + } + } + } + }, + "400": { + "description": "The request did not match the expected schema", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/HttpApiDecodeError" + }, + { + "type": "object", + "required": [ + "error", + "message" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + } + } + } + }, + "/projects/{projectId}/ports": { + "get": { + "tags": [ + "projectPorts" + ], + "operationId": "projectPorts.listProjectPorts", + "parameters": [ + { + "name": "projectId", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "forwards" + ], + "properties": { + "forwards": { + "type": "array", + "items": { + "type": "object", + "required": [ + "bindHost", + "containerName", + "createdAt", + "hostPort", + "id", + "projectId", + "projectKey", + "proxyPath", + "publicHost", + "status", + "targetContainerName", + "targetPort", + "url" + ], + "properties": { + "bindHost": { + "type": "string" + }, + "containerName": { + "type": "string" + }, + "createdAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "hostPort": { + "type": "number" + }, + "id": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "projectKey": { + "type": "string" + }, + "proxyPath": { + "type": "string" + }, + "publicHost": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "running", + "stopped", + "unknown" + ] + }, + "targetContainerName": { + "type": "string" + }, + "targetPort": { + "type": "number" + }, + "url": { + "type": "string" + } + }, + "additionalProperties": false + } + } + }, + "additionalProperties": false + } + } + } + }, + "400": { + "description": "The request did not match the expected schema", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/HttpApiDecodeError" + }, + { + "type": "object", + "required": [ + "error", + "message" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + } + } + }, + "post": { + "tags": [ + "projectPorts" + ], + "operationId": "projectPorts.createProjectPort", + "parameters": [ + { + "name": "projectId", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "forward" + ], + "properties": { + "forward": { + "type": "object", + "required": [ + "bindHost", + "containerName", + "createdAt", + "hostPort", + "id", + "projectId", + "projectKey", + "proxyPath", + "publicHost", + "status", + "targetContainerName", + "targetPort", + "url" + ], + "properties": { + "bindHost": { + "type": "string" + }, + "containerName": { + "type": "string" + }, + "createdAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "hostPort": { + "type": "number" + }, + "id": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "projectKey": { + "type": "string" + }, + "proxyPath": { + "type": "string" + }, + "publicHost": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "running", + "stopped", + "unknown" + ] + }, + "targetContainerName": { + "type": "string" + }, + "targetPort": { + "type": "number" + }, + "url": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "400": { + "description": "The request did not match the expected schema", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/HttpApiDecodeError" + }, + { + "type": "object", + "required": [ + "error", + "message" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "targetPort" + ], + "properties": { + "hostPort": { + "type": "number" + }, + "targetPort": { + "type": "number" + } + }, + "additionalProperties": false + } + } + }, + "required": true + } + } + }, + "/projects/{projectId}/ports/{targetPort}": { + "delete": { + "tags": [ + "projectPorts" + ], + "operationId": "projectPorts.deleteProjectPort", + "parameters": [ + { + "name": "projectId", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + }, + { + "name": "targetPort", + "in": "path", + "schema": { + "$ref": "#/components/schemas/NumberFromString" + }, + "required": true + } + ], + "security": [], + "responses": { + "204": { + "description": "Success" + }, + "400": { + "description": "The request did not match the expected schema", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/HttpApiDecodeError" + }, + { + "type": "object", + "required": [ + "error", + "message" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + } + } + } + }, + "/projects/{projectId}/browser": { + "get": { + "tags": [ + "projectBrowser" + ], + "operationId": "projectBrowser.readProjectBrowser", + "parameters": [ + { + "name": "projectId", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "browser" + ], + "properties": { + "browser": { + "type": "object", + "required": [ + "cdpPath", + "cdpUrl", + "containerName", + "noVncPath", + "noVncUrl", + "projectId", + "projectKey", + "status" + ], + "properties": { + "cdpPath": { + "type": "string" + }, + "cdpUrl": { + "type": "string" + }, + "containerName": { + "type": "string" + }, + "noVncPath": { + "type": "string" + }, + "noVncUrl": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "projectKey": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "running", + "stopped", + "missing", + "unknown" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "400": { + "description": "The request did not match the expected schema", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/HttpApiDecodeError" + }, + { + "type": "object", + "required": [ + "error", + "message" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + } + } + } + }, + "/projects/{projectId}/browser/start": { + "post": { + "tags": [ + "projectBrowser" + ], + "operationId": "projectBrowser.startProjectBrowser", + "parameters": [ + { + "name": "projectId", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "browser" + ], + "properties": { + "browser": { + "type": "object", + "required": [ + "cdpPath", + "cdpUrl", + "containerName", + "noVncPath", + "noVncUrl", + "projectId", + "projectKey", + "status" + ], + "properties": { + "cdpPath": { + "type": "string" + }, + "cdpUrl": { + "type": "string" + }, + "containerName": { + "type": "string" + }, + "noVncPath": { + "type": "string" + }, + "noVncUrl": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "projectKey": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "running", + "stopped", + "missing", + "unknown" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "400": { + "description": "The request did not match the expected schema", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/HttpApiDecodeError" + }, + { + "type": "object", + "required": [ + "error", + "message" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + } + } + } + }, + "/projects/{projectId}/databases/profiles": { + "get": { + "tags": [ + "projectDatabases" + ], + "operationId": "projectDatabases.listDatabaseProfiles", + "parameters": [ + { + "name": "projectId", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "profiles" + ], + "properties": { + "profiles": { + "type": "array", + "items": { + "type": "object", + "required": [ + "createdAt", + "database", + "engine", + "host", + "id", + "label", + "maskedConnectionString", + "port", + "updatedAt", + "user" + ], + "properties": { + "createdAt": { + "type": "string" + }, + "database": { + "type": "string" + }, + "engine": { + "type": "string", + "enum": [ + "postgres", + "mysql", + "mariadb" + ] + }, + "host": { + "type": "string" + }, + "id": { + "type": "string" + }, + "label": { + "type": "string" + }, + "maskedConnectionString": { + "type": "string" + }, + "port": { + "type": "number" + }, + "updatedAt": { + "type": "string" + }, + "user": { + "type": "string" + } + }, + "additionalProperties": false + } + } + }, + "additionalProperties": false + } + } + } + }, + "400": { + "description": "The request did not match the expected schema", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/HttpApiDecodeError" + }, + { + "type": "object", + "required": [ + "error", + "message" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + } + } + }, + "post": { + "tags": [ + "projectDatabases" + ], + "operationId": "projectDatabases.saveDatabaseProfile", + "parameters": [ + { + "name": "projectId", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "profile" + ], + "properties": { + "profile": { + "type": "object", + "required": [ + "createdAt", + "database", + "engine", + "host", + "id", + "label", + "maskedConnectionString", + "port", + "updatedAt", + "user" + ], + "properties": { + "createdAt": { + "type": "string" + }, + "database": { + "type": "string" + }, + "engine": { + "type": "string", + "enum": [ + "postgres", + "mysql", + "mariadb" + ] + }, + "host": { + "type": "string" + }, + "id": { + "type": "string" + }, + "label": { + "type": "string" + }, + "maskedConnectionString": { + "type": "string" + }, + "port": { + "type": "number" + }, + "updatedAt": { + "type": "string" + }, + "user": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "400": { + "description": "The request did not match the expected schema", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/HttpApiDecodeError" + }, + { + "type": "object", + "required": [ + "error", + "message" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "connectionString" + ], + "properties": { + "connectionString": { + "type": "string" + }, + "label": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + } + } + }, + "required": true + } + } + }, + "/projects/{projectId}/databases/profiles/{profileId}": { + "delete": { + "tags": [ + "projectDatabases" + ], + "operationId": "projectDatabases.deleteDatabaseProfile", + "parameters": [ + { + "name": "projectId", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + }, + { + "name": "profileId", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + } + ], + "security": [], + "responses": { + "204": { + "description": "Success" + }, + "400": { + "description": "The request did not match the expected schema", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/HttpApiDecodeError" + }, + { + "type": "object", + "required": [ + "error", + "message" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + } + } + } + }, + "/projects/{projectId}/databases/profiles/{profileId}/expose": { + "post": { + "tags": [ + "projectDatabases" + ], + "operationId": "projectDatabases.exposeDatabaseProfile", + "parameters": [ + { + "name": "projectId", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + }, + { + "name": "profileId", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "forward" + ], + "properties": { + "forward": { + "type": "object", + "required": [ + "bindHost", + "containerName", + "createdAt", + "database", + "engine", + "externalConnectionString", + "hostPort", + "id", + "maskedExternalConnectionString", + "profileId", + "profileLabel", + "projectId", + "projectKey", + "publicHost", + "status", + "targetHost", + "targetPort" + ], + "properties": { + "bindHost": { + "type": "string" + }, + "containerName": { + "type": "string" + }, + "createdAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "database": { + "type": "string" + }, + "engine": { + "type": "string", + "enum": [ + "postgres", + "mysql", + "mariadb" + ] + }, + "externalConnectionString": { + "type": "string" + }, + "hostPort": { + "type": "number" + }, + "id": { + "type": "string" + }, + "maskedExternalConnectionString": { + "type": "string" + }, + "profileId": { + "type": "string" + }, + "profileLabel": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "projectKey": { + "type": "string" + }, + "publicHost": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "running", + "stopped", + "unknown" + ] + }, + "targetHost": { + "type": "string" + }, + "targetPort": { + "type": "number" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "400": { + "description": "The request did not match the expected schema", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/HttpApiDecodeError" + }, + { + "type": "object", + "required": [ + "error", + "message" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + } + } + }, + "delete": { + "tags": [ + "projectDatabases" + ], + "operationId": "projectDatabases.deleteDatabaseForward", + "parameters": [ + { + "name": "projectId", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + }, + { + "name": "profileId", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + } + ], + "security": [], + "responses": { + "204": { + "description": "Success" + }, + "400": { + "description": "The request did not match the expected schema", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/HttpApiDecodeError" + }, + { + "type": "object", + "required": [ + "error", + "message" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + } + } + } + }, + "/projects/{projectId}/databases/forwards": { + "get": { + "tags": [ + "projectDatabases" + ], + "operationId": "projectDatabases.listDatabaseForwards", + "parameters": [ + { + "name": "projectId", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "forwards" + ], + "properties": { + "forwards": { + "type": "array", + "items": { + "type": "object", + "required": [ + "bindHost", + "containerName", + "createdAt", + "database", + "engine", + "externalConnectionString", + "hostPort", + "id", + "maskedExternalConnectionString", + "profileId", + "profileLabel", + "projectId", + "projectKey", + "publicHost", + "status", + "targetHost", + "targetPort" + ], + "properties": { + "bindHost": { + "type": "string" + }, + "containerName": { + "type": "string" + }, + "createdAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "database": { + "type": "string" + }, + "engine": { + "type": "string", + "enum": [ + "postgres", + "mysql", + "mariadb" + ] + }, + "externalConnectionString": { + "type": "string" + }, + "hostPort": { + "type": "number" + }, + "id": { + "type": "string" + }, + "maskedExternalConnectionString": { + "type": "string" + }, + "profileId": { + "type": "string" + }, + "profileLabel": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "projectKey": { + "type": "string" + }, + "publicHost": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "running", + "stopped", + "unknown" + ] + }, + "targetHost": { + "type": "string" + }, + "targetPort": { + "type": "number" + } + }, + "additionalProperties": false + } + } + }, + "additionalProperties": false + } + } + } + }, + "400": { + "description": "The request did not match the expected schema", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/HttpApiDecodeError" + }, + { + "type": "object", + "required": [ + "error", + "message" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + } + } + } + }, + "/projects/{projectId}/databases/session": { + "get": { + "tags": [ + "projectDatabases" + ], + "operationId": "projectDatabases.readDatabaseSession", + "parameters": [ + { + "name": "projectId", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "session" + ], + "properties": { + "session": { + "type": "object", + "required": [ + "configHash", + "containerName", + "editorPath", + "editorUrl", + "projectId", + "projectKey", + "status" + ], + "properties": { + "configHash": { + "type": "string" + }, + "containerName": { + "type": "string" + }, + "editorPath": { + "type": "string" + }, + "editorUrl": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "projectKey": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "running", + "stopped", + "missing", + "unknown" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "400": { + "description": "The request did not match the expected schema", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/HttpApiDecodeError" + }, + { + "type": "object", + "required": [ + "error", + "message" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + } + } + } + }, + "/projects/{projectId}/databases/open": { + "post": { + "tags": [ + "projectDatabases" + ], + "operationId": "projectDatabases.openDatabaseEditor", + "parameters": [ + { + "name": "projectId", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "session" + ], + "properties": { + "session": { + "type": "object", + "required": [ + "configHash", + "containerName", + "editorPath", + "editorUrl", + "projectId", + "projectKey", + "status" + ], + "properties": { + "configHash": { + "type": "string" + }, + "containerName": { + "type": "string" + }, + "editorPath": { + "type": "string" + }, + "editorUrl": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "projectKey": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "running", + "stopped", + "missing", + "unknown" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "400": { + "description": "The request did not match the expected schema", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/HttpApiDecodeError" + }, + { + "type": "object", + "required": [ + "error", + "message" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + } + } + } + }, + "/projects/{projectId}/databases/restart": { + "post": { + "tags": [ + "projectDatabases" + ], + "operationId": "projectDatabases.restartDatabaseEditor", + "parameters": [ + { + "name": "projectId", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "session" + ], + "properties": { + "session": { + "type": "object", + "required": [ + "configHash", + "containerName", + "editorPath", + "editorUrl", + "projectId", + "projectKey", + "status" + ], + "properties": { + "configHash": { + "type": "string" + }, + "containerName": { + "type": "string" + }, + "editorPath": { + "type": "string" + }, + "editorUrl": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "projectKey": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "running", + "stopped", + "missing", + "unknown" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "400": { + "description": "The request did not match the expected schema", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/HttpApiDecodeError" + }, + { + "type": "object", + "required": [ + "error", + "message" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + } + } + } + }, + "/auth/github/status": { + "get": { + "tags": [ + "auth" + ], + "operationId": "auth.githubStatus", + "parameters": [], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "status" + ], + "properties": { + "ok": { + "type": "boolean" + }, + "status": { + "type": "object", + "required": [ + "summary", + "tokens" + ], + "properties": { + "summary": { + "type": "string" + }, + "tokens": { + "type": "array", + "items": { + "type": "object", + "required": [ + "key", + "label", + "login", + "status" + ], + "properties": { + "key": { + "type": "string" + }, + "label": { + "type": "string" + }, + "login": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "status": { + "type": "string", + "enum": [ + "valid", + "invalid", + "unknown" + ] + } + }, + "additionalProperties": false + } + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "400": { + "description": "The request did not match the expected schema", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/HttpApiDecodeError" + }, + { + "type": "object", + "required": [ + "error", + "message" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + } + } + } + }, + "/auth/gitlab/status": { + "get": { + "tags": [ + "auth" + ], + "operationId": "auth.gitlabStatus", + "parameters": [], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "status" + ], + "properties": { + "ok": { + "type": "boolean" + }, + "status": { + "type": "object", + "required": [ + "summary", + "tokens" + ], + "properties": { + "summary": { + "type": "string" + }, + "tokens": { + "type": "array", + "items": { + "type": "object", + "required": [ + "key", + "label", + "login", + "status" + ], + "properties": { + "key": { + "type": "string" + }, + "label": { + "type": "string" + }, + "login": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "status": { + "type": "string", + "enum": [ + "valid", + "invalid", + "unknown" + ] + } + }, + "additionalProperties": false + } + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "400": { + "description": "The request did not match the expected schema", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/HttpApiDecodeError" + }, + { + "type": "object", + "required": [ + "error", + "message" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + } + } + } + }, + "/auth/git/status": { + "get": { + "tags": [ + "auth" + ], + "operationId": "auth.gitStatus", + "parameters": [], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "status" + ], + "properties": { + "ok": { + "type": "boolean" + }, + "status": { + "type": "object", + "required": [ + "connections", + "summary" + ], + "properties": { + "connections": { + "type": "array", + "items": { + "type": "object", + "required": [ + "host", + "user" + ], + "properties": { + "host": { + "type": "string" + }, + "user": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "summary": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "400": { + "description": "The request did not match the expected schema", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/HttpApiDecodeError" + }, + { + "type": "object", + "required": [ + "error", + "message" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + } + } + } + }, + "/auth/grok/status": { + "get": { + "tags": [ + "auth" + ], + "operationId": "auth.grokStatus", + "parameters": [ + { + "name": "label", + "in": "query", + "schema": { + "type": "string" + }, + "required": false + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "status" + ], + "properties": { + "ok": { + "type": "boolean" + }, + "status": { + "type": "object", + "required": [ + "authPath", + "connected", + "label", + "message", + "method" + ], + "properties": { + "authPath": { + "type": "string" + }, + "connected": { + "type": "boolean" + }, + "label": { + "type": "string" + }, + "message": { + "type": "string" + }, + "method": { + "type": "string", + "enum": [ + "none", + "api-key", + "oauth" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "400": { + "description": "The request did not match the expected schema", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/HttpApiDecodeError" + }, + { + "type": "object", + "required": [ + "error", + "message" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + } + } + } + }, + "/auth/codex/status": { + "get": { + "tags": [ + "auth" + ], + "operationId": "auth.codexStatus", + "parameters": [ + { + "name": "label", + "in": "query", + "schema": { + "type": "string" + }, + "required": false + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "status" + ], + "properties": { + "ok": { + "type": "boolean" + }, + "status": { + "type": "object", + "required": [ + "account", + "authPath", + "label", + "message", + "present" + ], + "properties": { + "account": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "authPath": { + "type": "string" + }, + "label": { + "type": "string" + }, + "message": { + "type": "string" + }, + "present": { + "type": "boolean" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "400": { + "description": "The request did not match the expected schema", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/HttpApiDecodeError" + }, + { + "type": "object", + "required": [ + "error", + "message" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + } + } + } + }, + "/auth/github/login": { + "post": { + "tags": [ + "auth" + ], + "operationId": "auth.githubLogin", + "parameters": [], + "security": [], + "responses": { + "201": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "status" + ], + "properties": { + "ok": { + "type": "boolean" + }, + "status": { + "type": "object", + "required": [ + "summary", + "tokens" + ], + "properties": { + "summary": { + "type": "string" + }, + "tokens": { + "type": "array", + "items": { + "type": "object", + "required": [ + "key", + "label", + "login", + "status" + ], + "properties": { + "key": { + "type": "string" + }, + "label": { + "type": "string" + }, + "login": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "status": { + "type": "string", + "enum": [ + "valid", + "invalid", + "unknown" + ] + } + }, + "additionalProperties": false + } + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "400": { + "description": "The request did not match the expected schema", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/HttpApiDecodeError" + }, + { + "type": "object", + "required": [ + "error", + "message" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [], + "properties": { + "label": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "token": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "scopes": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + } + } + }, + "required": true + } + } + }, + "/auth/github/logout": { + "post": { + "tags": [ + "auth" + ], + "operationId": "auth.githubLogout", + "parameters": [], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "status" + ], + "properties": { + "ok": { + "type": "boolean" + }, + "status": { + "type": "object", + "required": [ + "summary", + "tokens" + ], + "properties": { + "summary": { + "type": "string" + }, + "tokens": { + "type": "array", + "items": { + "type": "object", + "required": [ + "key", + "label", + "login", + "status" + ], + "properties": { + "key": { + "type": "string" + }, + "label": { + "type": "string" + }, + "login": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "status": { + "type": "string", + "enum": [ + "valid", + "invalid", + "unknown" + ] + } + }, + "additionalProperties": false + } + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "400": { + "description": "The request did not match the expected schema", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/HttpApiDecodeError" + }, + { + "type": "object", + "required": [ + "error", + "message" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [], + "properties": { + "label": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + } + } + }, + "required": true + } + } + }, + "/auth/gitlab/login": { + "post": { + "tags": [ + "auth" + ], + "operationId": "auth.gitlabLogin", + "parameters": [], + "security": [], + "responses": { + "201": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "status" + ], + "properties": { + "ok": { + "type": "boolean" + }, + "status": { + "type": "object", + "required": [ + "summary", + "tokens" + ], + "properties": { + "summary": { + "type": "string" + }, + "tokens": { + "type": "array", + "items": { + "type": "object", + "required": [ + "key", + "label", + "login", + "status" + ], + "properties": { + "key": { + "type": "string" + }, + "label": { + "type": "string" + }, + "login": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "status": { + "type": "string", + "enum": [ + "valid", + "invalid", + "unknown" + ] + } + }, + "additionalProperties": false + } + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "400": { + "description": "The request did not match the expected schema", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/HttpApiDecodeError" + }, + { + "type": "object", + "required": [ + "error", + "message" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [], + "properties": { + "label": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "token": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + } + } + }, + "required": true + } + } + }, + "/auth/gitlab/logout": { + "post": { + "tags": [ + "auth" + ], + "operationId": "auth.gitlabLogout", + "parameters": [], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "status" + ], + "properties": { + "ok": { + "type": "boolean" + }, + "status": { + "type": "object", + "required": [ + "summary", + "tokens" + ], + "properties": { + "summary": { + "type": "string" + }, + "tokens": { + "type": "array", + "items": { + "type": "object", + "required": [ + "key", + "label", + "login", + "status" + ], + "properties": { + "key": { + "type": "string" + }, + "label": { + "type": "string" + }, + "login": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "status": { + "type": "string", + "enum": [ + "valid", + "invalid", + "unknown" + ] + } + }, + "additionalProperties": false + } + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "400": { + "description": "The request did not match the expected schema", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/HttpApiDecodeError" + }, + { + "type": "object", + "required": [ + "error", + "message" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [], + "properties": { + "label": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + } + } + }, + "required": true + } + } + }, + "/auth/git/login": { + "post": { + "tags": [ + "auth" + ], + "operationId": "auth.gitLogin", + "parameters": [], + "security": [], + "responses": { + "201": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "status" + ], + "properties": { + "ok": { + "type": "boolean" + }, + "status": { + "type": "object", + "required": [ + "connections", + "summary" + ], + "properties": { + "connections": { + "type": "array", + "items": { + "type": "object", + "required": [ + "host", + "user" + ], + "properties": { + "host": { + "type": "string" + }, + "user": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "summary": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "400": { + "description": "The request did not match the expected schema", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/HttpApiDecodeError" + }, + { + "type": "object", + "required": [ + "error", + "message" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "host" + ], + "properties": { + "host": { + "type": "string" + }, + "token": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "user": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + } + } + }, + "required": true + } + } + }, + "/auth/git/logout": { + "post": { + "tags": [ + "auth" + ], + "operationId": "auth.gitLogout", + "parameters": [], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "status" + ], + "properties": { + "ok": { + "type": "boolean" + }, + "status": { + "type": "object", + "required": [ + "connections", + "summary" + ], + "properties": { + "connections": { + "type": "array", + "items": { + "type": "object", + "required": [ + "host", + "user" + ], + "properties": { + "host": { + "type": "string" + }, + "user": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "summary": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "400": { + "description": "The request did not match the expected schema", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/HttpApiDecodeError" + }, + { + "type": "object", + "required": [ + "error", + "message" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "host" + ], + "properties": { + "host": { + "type": "string" + } + }, + "additionalProperties": false + } + } + }, + "required": true + } + } + }, + "/auth/menu": { + "get": { + "tags": [ + "auth" + ], + "operationId": "auth.authMenu", + "parameters": [], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "snapshot" + ], + "properties": { + "ok": { + "type": "boolean" + }, + "snapshot": { + "type": "object", + "required": [ + "claudeAuthEntries", + "claudeAuthPath", + "geminiAuthEntries", + "geminiAuthPath", + "gitTokenEntries", + "gitUserEntries", + "githubTokenEntries", + "globalEnvPath", + "totalEntries" + ], + "properties": { + "claudeAuthEntries": { + "type": "number" + }, + "claudeAuthPath": { + "type": "string" + }, + "codexAuthEntries": { + "type": "number" + }, + "codexAuthPath": { + "type": "string" + }, + "geminiAuthEntries": { + "type": "number" + }, + "geminiAuthPath": { + "type": "string" + }, + "gitTokenEntries": { + "type": "number" + }, + "gitUserEntries": { + "type": "number" + }, + "githubTokenEntries": { + "type": "number" + }, + "globalEnvPath": { + "type": "string" + }, + "grokAuthEntries": { + "type": "number" + }, + "grokAuthPath": { + "type": "string" + }, + "totalEntries": { + "type": "number" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "400": { + "description": "The request did not match the expected schema", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/HttpApiDecodeError" + }, + { + "type": "object", + "required": [ + "error", + "message" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + } + } + }, + "post": { + "tags": [ + "auth" + ], + "operationId": "auth.authMenuAction", + "parameters": [], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "snapshot" + ], + "properties": { + "ok": { + "type": "boolean" + }, + "snapshot": { + "type": "object", + "required": [ + "claudeAuthEntries", + "claudeAuthPath", + "geminiAuthEntries", + "geminiAuthPath", + "gitTokenEntries", + "gitUserEntries", + "githubTokenEntries", + "globalEnvPath", + "totalEntries" + ], + "properties": { + "claudeAuthEntries": { + "type": "number" + }, + "claudeAuthPath": { + "type": "string" + }, + "codexAuthEntries": { + "type": "number" + }, + "codexAuthPath": { + "type": "string" + }, + "geminiAuthEntries": { + "type": "number" + }, + "geminiAuthPath": { + "type": "string" + }, + "gitTokenEntries": { + "type": "number" + }, + "gitUserEntries": { + "type": "number" + }, + "githubTokenEntries": { + "type": "number" + }, + "globalEnvPath": { + "type": "string" + }, + "grokAuthEntries": { + "type": "number" + }, + "grokAuthPath": { + "type": "string" + }, + "totalEntries": { + "type": "number" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "400": { + "description": "The request did not match the expected schema", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/HttpApiDecodeError" + }, + { + "type": "object", + "required": [ + "error", + "message" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "flow" + ], + "properties": { + "flow": { + "type": "string", + "enum": [ + "GithubRemove", + "GitSet", + "GitRemove", + "ClaudeLogout", + "GeminiApiKey", + "GeminiLogout", + "GrokApiKey", + "GrokLogout" + ] + }, + "label": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "token": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "user": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "apiKey": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + } + } + }, + "required": true + } + } + }, + "/auth/terminal-sessions": { + "post": { + "tags": [ + "auth" + ], + "operationId": "auth.authTerminalSession", + "parameters": [], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "session" + ], + "properties": { + "ok": { + "type": "boolean" + }, + "session": { + "type": "object", + "required": [ + "createdAt", + "id", + "projectId", + "sshCommand", + "status" + ], + "properties": { + "attachedClients": { + "type": "number" + }, + "closedAt": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "exitCode": { + "type": "number" + }, + "id": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "signal": { + "type": "number" + }, + "sshCommand": { + "type": "string" + }, + "startedAt": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "ready", + "attached", + "exited", + "failed" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "400": { + "description": "The request did not match the expected schema", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/HttpApiDecodeError" + }, + { + "type": "object", + "required": [ + "error", + "message" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "flow" + ], + "properties": { + "flow": { + "type": "string", + "enum": [ + "ClaudeOauth", + "GeminiOauth", + "GrokOauth" + ] + }, + "label": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + } + } + }, + "required": true + } + } + }, + "/auth/codex/import": { + "post": { + "tags": [ + "auth" + ], + "operationId": "auth.codexImport", + "parameters": [], + "security": [], + "responses": { + "201": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "status" + ], + "properties": { + "ok": { + "type": "boolean" + }, + "status": { + "type": "object", + "required": [ + "account", + "authPath", + "label", + "message", + "present" + ], + "properties": { + "account": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "authPath": { + "type": "string" + }, + "label": { + "type": "string" + }, + "message": { + "type": "string" + }, + "present": { + "type": "boolean" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "400": { + "description": "The request did not match the expected schema", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/HttpApiDecodeError" + }, + { + "type": "object", + "required": [ + "error", + "message" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "authText" + ], + "properties": { + "label": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "authText": { + "type": "string" + } + }, + "additionalProperties": false + } + } + }, + "required": true + } + } + }, + "/auth/codex/logout": { + "post": { + "tags": [ + "auth" + ], + "operationId": "auth.codexLogout", + "parameters": [], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "status" + ], + "properties": { + "ok": { + "type": "boolean" + }, + "status": { + "type": "object", + "required": [ + "account", + "authPath", + "label", + "message", + "present" + ], + "properties": { + "account": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "authPath": { + "type": "string" + }, + "label": { + "type": "string" + }, + "message": { + "type": "string" + }, + "present": { + "type": "boolean" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "400": { + "description": "The request did not match the expected schema", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/HttpApiDecodeError" + }, + { + "type": "object", + "required": [ + "error", + "message" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [], + "properties": { + "label": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + } + } + }, + "required": true + } + } + }, + "/auth/grok/logout": { + "post": { + "tags": [ + "auth" + ], + "operationId": "auth.grokLogout", + "parameters": [], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "status" + ], + "properties": { + "ok": { + "type": "boolean" + }, + "status": { + "type": "object", + "required": [ + "authPath", + "connected", + "label", + "message", + "method" + ], + "properties": { + "authPath": { + "type": "string" + }, + "connected": { + "type": "boolean" + }, + "label": { + "type": "string" + }, + "message": { + "type": "string" + }, + "method": { + "type": "string", + "enum": [ + "none", + "api-key", + "oauth" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "400": { + "description": "The request did not match the expected schema", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/HttpApiDecodeError" + }, + { + "type": "object", + "required": [ + "error", + "message" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [], + "properties": { + "label": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + } + } + }, + "required": true + } + } + }, + "/projects/{projectId}/auth/menu": { + "get": { + "tags": [ + "projectAuth" + ], + "operationId": "projectAuth.projectAuth", + "parameters": [ + { + "name": "projectId", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "snapshot" + ], + "properties": { + "ok": { + "type": "boolean" + }, + "snapshot": { + "type": "object", + "required": [ + "activeClaudeLabel", + "activeGeminiLabel", + "activeGitLabel", + "activeGithubLabel", + "activeGrokLabel", + "claudeAuthEntries", + "claudeAuthPath", + "envGlobalPath", + "envProjectPath", + "geminiAuthEntries", + "geminiAuthPath", + "gitTokenEntries", + "githubTokenEntries", + "projectDir", + "projectName", + "totalEntries" + ], + "properties": { + "activeClaudeLabel": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "activeGeminiLabel": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "activeGitLabel": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "activeGithubLabel": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "activeGrokLabel": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "claudeAuthEntries": { + "type": "number" + }, + "claudeAuthPath": { + "type": "string" + }, + "codexAuthEntries": { + "type": "number" + }, + "codexAuthPath": { + "type": "string" + }, + "envGlobalPath": { + "type": "string" + }, + "envProjectPath": { + "type": "string" + }, + "geminiAuthEntries": { + "type": "number" + }, + "geminiAuthPath": { + "type": "string" + }, + "gitTokenEntries": { + "type": "number" + }, + "githubTokenEntries": { + "type": "number" + }, + "grokAuthEntries": { + "type": "number" + }, + "grokAuthPath": { + "type": "string" + }, + "projectDir": { + "type": "string" + }, + "projectName": { + "type": "string" + }, + "totalEntries": { + "type": "number" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "400": { + "description": "The request did not match the expected schema", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/HttpApiDecodeError" + }, + { + "type": "object", + "required": [ + "error", + "message" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + } + } + }, + "post": { + "tags": [ + "projectAuth" + ], + "operationId": "projectAuth.projectAuthAction", + "parameters": [ + { + "name": "projectId", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "snapshot" + ], + "properties": { + "ok": { + "type": "boolean" + }, + "snapshot": { + "type": "object", + "required": [ + "activeClaudeLabel", + "activeGeminiLabel", + "activeGitLabel", + "activeGithubLabel", + "activeGrokLabel", + "claudeAuthEntries", + "claudeAuthPath", + "envGlobalPath", + "envProjectPath", + "geminiAuthEntries", + "geminiAuthPath", + "gitTokenEntries", + "githubTokenEntries", + "projectDir", + "projectName", + "totalEntries" + ], + "properties": { + "activeClaudeLabel": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "activeGeminiLabel": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "activeGitLabel": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "activeGithubLabel": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "activeGrokLabel": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "claudeAuthEntries": { + "type": "number" + }, + "claudeAuthPath": { + "type": "string" + }, + "codexAuthEntries": { + "type": "number" + }, + "codexAuthPath": { + "type": "string" + }, + "envGlobalPath": { + "type": "string" + }, + "envProjectPath": { + "type": "string" + }, + "geminiAuthEntries": { + "type": "number" + }, + "geminiAuthPath": { + "type": "string" + }, + "gitTokenEntries": { + "type": "number" + }, + "githubTokenEntries": { + "type": "number" + }, + "grokAuthEntries": { + "type": "number" + }, + "grokAuthPath": { + "type": "string" + }, + "projectDir": { + "type": "string" + }, + "projectName": { + "type": "string" + }, + "totalEntries": { + "type": "number" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "400": { + "description": "The request did not match the expected schema", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/HttpApiDecodeError" + }, + { + "type": "object", + "required": [ + "error", + "message" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "flow" + ], + "properties": { + "flow": { + "type": "string", + "enum": [ + "ProjectGithubConnect", + "ProjectGithubDisconnect", + "ProjectGitConnect", + "ProjectGitDisconnect", + "ProjectClaudeConnect", + "ProjectClaudeDisconnect", + "ProjectGeminiConnect", + "ProjectGeminiDisconnect", + "ProjectGrokConnect", + "ProjectGrokDisconnect" + ] + }, + "label": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + } + } + }, + "required": true + } + } + }, + "/projects/by-key/{projectKey}/terminal-sessions": { + "post": { + "tags": [ + "terminal" + ], + "operationId": "terminal.createTerminalByKey", + "parameters": [ + { + "name": "projectKey", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "project", + "session" + ], + "properties": { + "ok": { + "type": "boolean" + }, + "project": { + "type": "object", + "required": [ + "containerName", + "displayName", + "id", + "projectKey", + "repoRef", + "repoUrl", + "sshSessions", + "startedAtEpochMs", + "startedAtIso", + "status", + "statusLabel", + "authorizedKeysExists", + "authorizedKeysPath", + "codexAuthPath", + "codexHome", + "envGlobalPath", + "envProjectPath", + "gpu", + "projectDir", + "serviceName", + "sshCommand", + "sshPort", + "sshUser", + "targetDir" + ], + "properties": { + "clonedOnHostname": { + "type": "string" + }, + "containerName": { + "type": "string" + }, + "displayName": { + "type": "string" + }, + "id": { + "type": "string" + }, + "projectKey": { + "type": "string" + }, + "repoRef": { + "type": "string" + }, + "repoUrl": { + "type": "string" + }, + "sshSessions": { + "type": "number" + }, + "startedAtEpochMs": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ] + }, + "startedAtIso": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "status": { + "type": "string", + "enum": [ + "running", + "stopped", + "unknown" + ] + }, + "statusLabel": { + "type": "string" + }, + "authorizedKeysExists": { + "type": "boolean" + }, + "authorizedKeysPath": { + "type": "string" + }, + "codexAuthPath": { + "type": "string" + }, + "codexHome": { + "type": "string" + }, + "envGlobalPath": { + "type": "string" + }, + "envProjectPath": { + "type": "string" + }, + "gpu": { + "type": "string", + "enum": [ + "none", + "all" + ] + }, + "projectDir": { + "type": "string" + }, + "serviceName": { + "type": "string" + }, + "sshCommand": { + "type": "string" + }, + "sshPort": { + "type": "number" + }, + "sshUser": { + "type": "string" + }, + "targetDir": { + "type": "string" + } + }, + "additionalProperties": false + }, + "session": { + "type": "object", + "required": [ + "createdAt", + "id", + "projectId", + "sshCommand", + "status" + ], + "properties": { + "attachedClients": { + "type": "number" + }, + "closedAt": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "exitCode": { + "type": "number" + }, + "id": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "signal": { + "type": "number" + }, + "sshCommand": { + "type": "string" + }, + "startedAt": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "ready", + "attached", + "exited", + "failed" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "400": { + "description": "The request did not match the expected schema", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/HttpApiDecodeError" + }, + { + "type": "object", + "required": [ + "error", + "message" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + } + } + }, + "get": { + "tags": [ + "terminal" + ], + "operationId": "terminal.listTerminalsByKey", + "parameters": [ + { + "name": "projectKey", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "activeSessionId", + "sessions" + ], + "properties": { + "activeSessionId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "sessions": { + "type": "array", + "items": { + "type": "object", + "required": [ + "createdAt", + "id", + "projectId", + "sshCommand", + "status" + ], + "properties": { + "attachedClients": { + "type": "number" + }, + "closedAt": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "exitCode": { + "type": "number" + }, + "id": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "signal": { + "type": "number" + }, + "sshCommand": { + "type": "string" + }, + "startedAt": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "ready", + "attached", + "exited", + "failed" + ] + } + }, + "additionalProperties": false + } + } + }, + "additionalProperties": false + } + } + } + }, + "400": { + "description": "The request did not match the expected schema", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/HttpApiDecodeError" + }, + { + "type": "object", + "required": [ + "error", + "message" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + } + } + } + }, + "/projects/by-key/{projectKey}/terminal-sessions/start": { + "post": { + "tags": [ + "terminal" + ], + "operationId": "terminal.startTerminalByKey", + "parameters": [ + { + "name": "projectKey", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + } + ], + "security": [], + "responses": { + "202": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "accepted", + "cursor", + "projectId", + "requestId" + ], + "properties": { + "accepted": { + "type": "boolean", + "enum": [ + true + ] + }, + "cursor": { + "type": "number" + }, + "projectId": { + "type": "string" + }, + "requestId": { + "type": "string" + } + }, + "additionalProperties": false + } + } + } + }, + "400": { + "description": "The request did not match the expected schema", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/HttpApiDecodeError" + }, + { + "type": "object", + "required": [ + "error", + "message" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "requestId" + ], + "properties": { + "requestId": { + "$ref": "#/components/schemas/UUID" + } + }, + "additionalProperties": false + } + } + }, + "required": true + } + } + }, + "/projects/by-key/{projectKey}/terminal-sessions/{sessionId}": { + "get": { + "tags": [ + "terminal" + ], + "operationId": "terminal.getTerminalByKey", + "parameters": [ + { + "name": "projectKey", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + }, + { + "name": "sessionId", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "session" + ], + "properties": { + "session": { + "type": "object", + "required": [ + "createdAt", + "id", + "projectId", + "sshCommand", + "status" + ], + "properties": { + "attachedClients": { + "type": "number" + }, + "closedAt": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "exitCode": { + "type": "number" + }, + "id": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "signal": { + "type": "number" + }, + "sshCommand": { + "type": "string" + }, + "startedAt": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "ready", + "attached", + "exited", + "failed" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "400": { + "description": "The request did not match the expected schema", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/HttpApiDecodeError" + }, + { + "type": "object", + "required": [ + "error", + "message" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + } + } + }, + "delete": { + "tags": [ + "terminal" + ], + "operationId": "terminal.deleteTerminalByKey", + "parameters": [ + { + "name": "projectKey", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + }, + { + "name": "sessionId", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + } + ], + "security": [], + "responses": { + "204": { + "description": "Success" + }, + "400": { + "description": "The request did not match the expected schema", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/HttpApiDecodeError" + }, + { + "type": "object", + "required": [ + "error", + "message" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + } + } + } + }, + "/projects/by-key/{projectKey}/terminal-sessions/active": { + "put": { + "tags": [ + "terminal" + ], + "operationId": "terminal.setActiveTerminalByKey", + "parameters": [ + { + "name": "projectKey", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "session" + ], + "properties": { + "session": { + "type": "object", + "required": [ + "createdAt", + "id", + "projectId", + "sshCommand", + "status" + ], + "properties": { + "attachedClients": { + "type": "number" + }, + "closedAt": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "exitCode": { + "type": "number" + }, + "id": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "signal": { + "type": "number" + }, + "sshCommand": { + "type": "string" + }, + "startedAt": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "ready", + "attached", + "exited", + "failed" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "400": { + "description": "The request did not match the expected schema", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/HttpApiDecodeError" + }, + { + "type": "object", + "required": [ + "error", + "message" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "sessionId" + ], + "properties": { + "sessionId": { + "type": "string" + } + }, + "additionalProperties": false + } + } + }, + "required": true + } + } + }, + "/terminal-sessions/{sessionId}": { + "get": { + "tags": [ + "terminal" + ], + "operationId": "terminal.lookupTerminal", + "parameters": [ + { + "name": "sessionId", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "projectDisplayName", + "projectKey", + "session" + ], + "properties": { + "projectDisplayName": { + "type": "string" + }, + "projectKey": { + "type": "string" + }, + "session": { + "type": "object", + "required": [ + "createdAt", + "id", + "projectId", + "sshCommand", + "status" + ], + "properties": { + "attachedClients": { + "type": "number" + }, + "closedAt": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "exitCode": { + "type": "number" + }, + "id": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "signal": { + "type": "number" + }, + "sshCommand": { + "type": "string" + }, + "startedAt": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "ready", + "attached", + "exited", + "failed" + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "400": { + "description": "The request did not match the expected schema", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/HttpApiDecodeError" + }, + { + "type": "object", + "required": [ + "error", + "message" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + } + } + } + }, + "/auth/terminal-sessions/{sessionId}": { + "delete": { + "tags": [ + "terminal" + ], + "operationId": "terminal.deleteAuthTerminal", + "parameters": [ + { + "name": "sessionId", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + } + ], + "security": [], + "responses": { + "204": { + "description": "Success" + }, + "400": { + "description": "The request did not match the expected schema", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/HttpApiDecodeError" + }, + { + "type": "object", + "required": [ + "error", + "message" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + } + } + } + }, + "/projects/{projectId}/prompts": { + "get": { + "tags": [ + "prompts" + ], + "operationId": "prompts.listPrompts", + "parameters": [ + { + "name": "projectId", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "snapshot" + ], + "properties": { + "ok": { + "type": "boolean" + }, + "snapshot": { + "type": "object", + "required": [ + "projectDir", + "projectId", + "projectKey", + "prompts" + ], + "properties": { + "projectDir": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "projectKey": { + "type": "string" + }, + "prompts": { + "type": "array", + "items": { + "type": "object", + "required": [ + "absolutePath", + "bytes", + "content", + "exists", + "fileName", + "kind", + "relativePath" + ], + "properties": { + "absolutePath": { + "type": "string" + }, + "bytes": { + "type": "number" + }, + "content": { + "type": "string" + }, + "exists": { + "type": "boolean" + }, + "fileName": { + "type": "string" + }, + "kind": { + "type": "string", + "enum": [ + "claude", + "codex", + "gemini", + "grok" + ] + }, + "relativePath": { + "type": "string" + } + }, + "additionalProperties": false + } + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "400": { + "description": "The request did not match the expected schema", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/HttpApiDecodeError" + }, + { + "type": "object", + "required": [ + "error", + "message" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + } + } + } + }, + "/projects/{projectId}/prompts/{kind}": { + "put": { + "tags": [ + "prompts" + ], + "operationId": "prompts.writePrompt", + "parameters": [ + { + "name": "projectId", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + }, + { + "name": "kind", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "prompt", + "snapshot" + ], + "properties": { + "ok": { + "type": "boolean" + }, + "prompt": { + "type": "object", + "required": [ + "absolutePath", + "bytes", + "content", + "exists", + "fileName", + "kind", + "relativePath" + ], + "properties": { + "absolutePath": { + "type": "string" + }, + "bytes": { + "type": "number" + }, + "content": { + "type": "string" + }, + "exists": { + "type": "boolean" + }, + "fileName": { + "type": "string" + }, + "kind": { + "type": "string", + "enum": [ + "claude", + "codex", + "gemini", + "grok" + ] + }, + "relativePath": { + "type": "string" + } + }, + "additionalProperties": false + }, + "snapshot": { + "type": "object", + "required": [ + "projectDir", + "projectId", + "projectKey", + "prompts" + ], + "properties": { + "projectDir": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "projectKey": { + "type": "string" + }, + "prompts": { + "type": "array", + "items": { + "type": "object", + "required": [ + "absolutePath", + "bytes", + "content", + "exists", + "fileName", + "kind", + "relativePath" + ], + "properties": { + "absolutePath": { + "type": "string" + }, + "bytes": { + "type": "number" + }, + "content": { + "type": "string" + }, + "exists": { + "type": "boolean" + }, + "fileName": { + "type": "string" + }, + "kind": { + "type": "string", + "enum": [ + "claude", + "codex", + "gemini", + "grok" + ] + }, + "relativePath": { + "type": "string" + } + }, + "additionalProperties": false + } + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "400": { + "description": "The request did not match the expected schema", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/HttpApiDecodeError" + }, + { + "type": "object", + "required": [ + "error", + "message" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "content" + ], + "properties": { + "content": { + "type": "string" + } + }, + "additionalProperties": false + } + } + }, + "required": true + } + }, + "delete": { + "tags": [ + "prompts" + ], + "operationId": "prompts.deletePrompt", + "parameters": [ + { + "name": "projectId", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + }, + { + "name": "kind", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "snapshot" + ], + "properties": { + "ok": { + "type": "boolean" + }, + "snapshot": { + "type": "object", + "required": [ + "projectDir", + "projectId", + "projectKey", + "prompts" + ], + "properties": { + "projectDir": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "projectKey": { + "type": "string" + }, + "prompts": { + "type": "array", + "items": { + "type": "object", + "required": [ + "absolutePath", + "bytes", + "content", + "exists", + "fileName", + "kind", + "relativePath" + ], + "properties": { + "absolutePath": { + "type": "string" + }, + "bytes": { + "type": "number" + }, + "content": { + "type": "string" + }, + "exists": { + "type": "boolean" + }, + "fileName": { + "type": "string" + }, + "kind": { + "type": "string", + "enum": [ + "claude", + "codex", + "gemini", + "grok" + ] + }, + "relativePath": { + "type": "string" + } + }, + "additionalProperties": false + } + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "400": { + "description": "The request did not match the expected schema", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/HttpApiDecodeError" + }, + { + "type": "object", + "required": [ + "error", + "message" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + } + } + } + }, + "/projects/{projectId}/skills": { + "get": { + "tags": [ + "skills" + ], + "operationId": "skills.listSkills", + "parameters": [ + { + "name": "projectId", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "snapshot" + ], + "properties": { + "ok": { + "type": "boolean" + }, + "snapshot": { + "type": "object", + "required": [ + "projectDir", + "projectId", + "projectKey", + "scopes", + "skills" + ], + "properties": { + "projectDir": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "projectKey": { + "type": "string" + }, + "scopes": { + "type": "array", + "items": { + "type": "object", + "required": [ + "absoluteRoot", + "relativeRoot", + "scope" + ], + "properties": { + "absoluteRoot": { + "type": "string" + }, + "relativeRoot": { + "type": "string" + }, + "scope": { + "type": "string", + "enum": [ + "skills", + "agents/skills", + "agents/.skills", + "claude/skills", + "codex/skills", + "gemini/skills", + "grok/skills" + ] + } + }, + "additionalProperties": false + } + }, + "skills": { + "type": "array", + "items": { + "type": "object", + "required": [ + "absolutePath", + "bytes", + "content", + "id", + "name", + "relativePath", + "scope", + "updatedAtIso" + ], + "properties": { + "absolutePath": { + "type": "string" + }, + "bytes": { + "type": "number" + }, + "content": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "relativePath": { + "type": "string" + }, + "scope": { + "type": "string", + "enum": [ + "skills", + "agents/skills", + "agents/.skills", + "claude/skills", + "codex/skills", + "gemini/skills", + "grok/skills" + ] + }, + "updatedAtIso": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + } + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "400": { + "description": "The request did not match the expected schema", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/HttpApiDecodeError" + }, + { + "type": "object", + "required": [ + "error", + "message" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + } + } + }, + "post": { + "tags": [ + "skills" + ], + "operationId": "skills.writeSkill", + "parameters": [ + { + "name": "projectId", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "skill", + "snapshot" + ], + "properties": { + "ok": { + "type": "boolean" + }, + "skill": { + "type": "object", + "required": [ + "absolutePath", + "bytes", + "content", + "id", + "name", + "relativePath", + "scope", + "updatedAtIso" + ], + "properties": { + "absolutePath": { + "type": "string" + }, + "bytes": { + "type": "number" + }, + "content": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "relativePath": { + "type": "string" + }, + "scope": { + "type": "string", + "enum": [ + "skills", + "agents/skills", + "agents/.skills", + "claude/skills", + "codex/skills", + "gemini/skills", + "grok/skills" + ] + }, + "updatedAtIso": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + "snapshot": { + "type": "object", + "required": [ + "projectDir", + "projectId", + "projectKey", + "scopes", + "skills" + ], + "properties": { + "projectDir": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "projectKey": { + "type": "string" + }, + "scopes": { + "type": "array", + "items": { + "type": "object", + "required": [ + "absoluteRoot", + "relativeRoot", + "scope" + ], + "properties": { + "absoluteRoot": { + "type": "string" + }, + "relativeRoot": { + "type": "string" + }, + "scope": { + "type": "string", + "enum": [ + "skills", + "agents/skills", + "agents/.skills", + "claude/skills", + "codex/skills", + "gemini/skills", + "grok/skills" + ] + } + }, + "additionalProperties": false + } + }, + "skills": { + "type": "array", + "items": { + "type": "object", + "required": [ + "absolutePath", + "bytes", + "content", + "id", + "name", + "relativePath", + "scope", + "updatedAtIso" + ], + "properties": { + "absolutePath": { + "type": "string" + }, + "bytes": { + "type": "number" + }, + "content": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "relativePath": { + "type": "string" + }, + "scope": { + "type": "string", + "enum": [ + "skills", + "agents/skills", + "agents/.skills", + "claude/skills", + "codex/skills", + "gemini/skills", + "grok/skills" + ] + }, + "updatedAtIso": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + } + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "400": { + "description": "The request did not match the expected schema", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/HttpApiDecodeError" + }, + { + "type": "object", + "required": [ + "error", + "message" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "scope", + "name", + "content" + ], + "properties": { + "scope": { + "type": "string", + "enum": [ + "skills", + "agents/skills", + "agents/.skills", + "claude/skills", + "codex/skills", + "gemini/skills", + "grok/skills" + ] + }, + "name": { + "type": "string" + }, + "content": { + "type": "string" + } + }, + "additionalProperties": false + } + } + }, + "required": true + } + } + }, + "/projects/{projectId}/skills/{scopeId}/{name}": { + "delete": { + "tags": [ + "skills" + ], + "operationId": "skills.deleteSkill", + "parameters": [ + { + "name": "projectId", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + }, + { + "name": "scopeId", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + }, + { + "name": "name", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "snapshot" + ], + "properties": { + "ok": { + "type": "boolean" + }, + "snapshot": { + "type": "object", + "required": [ + "projectDir", + "projectId", + "projectKey", + "scopes", + "skills" + ], + "properties": { + "projectDir": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "projectKey": { + "type": "string" + }, + "scopes": { + "type": "array", + "items": { + "type": "object", + "required": [ + "absoluteRoot", + "relativeRoot", + "scope" + ], + "properties": { + "absoluteRoot": { + "type": "string" + }, + "relativeRoot": { + "type": "string" + }, + "scope": { + "type": "string", + "enum": [ + "skills", + "agents/skills", + "agents/.skills", + "claude/skills", + "codex/skills", + "gemini/skills", + "grok/skills" + ] + } + }, + "additionalProperties": false + } + }, + "skills": { + "type": "array", + "items": { + "type": "object", + "required": [ + "absolutePath", + "bytes", + "content", + "id", + "name", + "relativePath", + "scope", + "updatedAtIso" + ], + "properties": { + "absolutePath": { + "type": "string" + }, + "bytes": { + "type": "number" + }, + "content": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "relativePath": { + "type": "string" + }, + "scope": { + "type": "string", + "enum": [ + "skills", + "agents/skills", + "agents/.skills", + "claude/skills", + "codex/skills", + "gemini/skills", + "grok/skills" + ] + }, + "updatedAtIso": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + } + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "400": { + "description": "The request did not match the expected schema", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/HttpApiDecodeError" + }, + { + "type": "object", + "required": [ + "error", + "message" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + } + } + } + }, + "/projects/{projectId}/tasks": { + "get": { + "tags": [ + "tasks" + ], + "operationId": "tasks.listTasks", + "parameters": [ + { + "name": "projectId", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + }, + { + "name": "includeDefault", + "in": "query", + "schema": { + "type": "string" + }, + "required": false + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "snapshot" + ], + "properties": { + "snapshot": { + "type": "object", + "required": [ + "containerName", + "generatedAt", + "projectId", + "sshConnections", + "tasks" + ], + "properties": { + "containerName": { + "type": "string" + }, + "generatedAt": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "sshConnections": { + "type": "number" + }, + "tasks": { + "type": "array", + "items": { + "type": "object", + "required": [ + "command", + "etime", + "etimes", + "kind", + "logAvailable", + "pid", + "ppid", + "tty", + "user" + ], + "properties": { + "command": { + "type": "string" + }, + "elapsed": { + "type": "string" + }, + "etime": { + "type": "string" + }, + "etimes": { + "type": "number" + }, + "kind": { + "type": "string", + "enum": [ + "ssh", + "web-terminal", + "agent", + "background", + "system" + ] + }, + "logAvailable": { + "type": "boolean" + }, + "managedId": { + "type": "string" + }, + "pid": { + "type": "number" + }, + "ppid": { + "type": "number" + }, + "tty": { + "type": "string" + }, + "user": { + "type": "string" + } + }, + "additionalProperties": false + } + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "400": { + "description": "The request did not match the expected schema", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/HttpApiDecodeError" + }, + { + "type": "object", + "required": [ + "error", + "message" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + } + } + } + }, + "/projects/{projectId}/tasks/{pid}/stop": { + "post": { + "tags": [ + "tasks" + ], + "operationId": "tasks.stopTask", + "parameters": [ + { + "name": "projectId", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + }, + { + "name": "pid", + "in": "path", + "schema": { + "$ref": "#/components/schemas/NumberFromString" + }, + "required": true + } + ], + "security": [], + "responses": { + "204": { + "description": "Success" + }, + "400": { + "description": "The request did not match the expected schema", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/HttpApiDecodeError" + }, + { + "type": "object", + "required": [ + "error", + "message" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + } + } + } + }, + "/projects/{projectId}/tasks/{pid}/logs": { + "get": { + "tags": [ + "tasks" + ], + "operationId": "tasks.taskLogs", + "parameters": [ + { + "name": "projectId", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + }, + { + "name": "pid", + "in": "path", + "schema": { + "$ref": "#/components/schemas/NumberFromString" + }, + "required": true + }, + { + "name": "lines", + "in": "query", + "schema": { + "type": "string" + }, + "required": false + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "output" + ], + "properties": { + "output": { + "type": "string" + } + }, + "additionalProperties": false + } + } + } + }, + "400": { + "description": "The request did not match the expected schema", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/HttpApiDecodeError" + }, + { + "type": "object", + "required": [ + "error", + "message" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + } + } + } + }, + "/cloudflare-tunnels/panel": { + "get": { + "tags": [ + "sharing" + ], + "operationId": "sharing.readPanelCloudflareTunnel", + "parameters": [], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "tunnel" + ], + "properties": { + "tunnel": { + "anyOf": [ + { + "type": "object", + "required": [ + "error", + "id", + "logTail", + "panelUrl", + "publicUrl", + "startedAt", + "status", + "stoppedAt" + ], + "properties": { + "error": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "id": { + "type": "string" + }, + "logTail": { + "type": "array", + "items": { + "type": "string" + } + }, + "panelUrl": { + "type": "string" + }, + "publicUrl": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "startedAt": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "starting", + "running", + "stopped", + "failed" + ] + }, + "stoppedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + } + } + } + }, + "400": { + "description": "The request did not match the expected schema", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/HttpApiDecodeError" + }, + { + "type": "object", + "required": [ + "error", + "message" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + } + } + }, + "post": { + "tags": [ + "sharing" + ], + "operationId": "sharing.startPanelCloudflareTunnel", + "parameters": [], + "security": [], + "responses": { + "202": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "tunnel" + ], + "properties": { + "tunnel": { + "anyOf": [ + { + "type": "object", + "required": [ + "error", + "id", + "logTail", + "panelUrl", + "publicUrl", + "startedAt", + "status", + "stoppedAt" + ], + "properties": { + "error": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "id": { + "type": "string" + }, + "logTail": { + "type": "array", + "items": { + "type": "string" + } + }, + "panelUrl": { + "type": "string" + }, + "publicUrl": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "startedAt": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "starting", + "running", + "stopped", + "failed" + ] + }, + "stoppedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + } + } + } + }, + "400": { + "description": "The request did not match the expected schema", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/HttpApiDecodeError" + }, + { + "type": "object", + "required": [ + "error", + "message" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "panelUrl" + ], + "properties": { + "panelUrl": { + "type": "string" + } + }, + "additionalProperties": false + } + } + }, + "required": true + } + }, + "delete": { + "tags": [ + "sharing" + ], + "operationId": "sharing.stopPanelCloudflareTunnel", + "parameters": [], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "tunnel" + ], + "properties": { + "tunnel": { + "anyOf": [ + { + "type": "object", + "required": [ + "error", + "id", + "logTail", + "panelUrl", + "publicUrl", + "startedAt", + "status", + "stoppedAt" + ], + "properties": { + "error": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "id": { + "type": "string" + }, + "logTail": { + "type": "array", + "items": { + "type": "string" + } + }, + "panelUrl": { + "type": "string" + }, + "publicUrl": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "startedAt": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "starting", + "running", + "stopped", + "failed" + ] + }, + "stoppedAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false + } + } + } + }, + "400": { + "description": "The request did not match the expected schema", + "content": { + "application/json": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/HttpApiDecodeError" + }, + { + "type": "object", + "required": [ + "error", + "message" + ], + "properties": { + "error": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "additionalProperties": false + } + ] + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "HttpApiDecodeError": { + "type": "object", + "required": [ + "issues", + "message", + "_tag" + ], + "properties": { + "issues": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Issue" + } + }, + "message": { + "type": "string" + }, + "_tag": { + "type": "string", + "enum": [ + "HttpApiDecodeError" + ] + } + }, + "additionalProperties": false, + "description": "The request did not match the expected schema" + }, + "Issue": { + "type": "object", + "required": [ + "_tag", + "path", + "message" + ], + "properties": { + "_tag": { + "type": "string", + "enum": [ + "Pointer", + "Unexpected", + "Missing", + "Composite", + "Refinement", + "Transformation", + "Type", + "Forbidden" + ], + "description": "The tag identifying the type of parse issue" + }, + "path": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PropertyKey" + }, + "description": "The path to the property where the issue occurred" + }, + "message": { + "type": "string", + "description": "A descriptive message explaining the issue" + } + }, + "additionalProperties": false, + "description": "Represents an error encountered while parsing a value to match the schema" + }, + "PropertyKey": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "object", + "required": [ + "_tag", + "key" + ], + "properties": { + "_tag": { + "type": "string", + "enum": [ + "symbol" + ] + }, + "key": { + "type": "string" + } + }, + "additionalProperties": false, + "description": "an object to be decoded into a globally shared symbol" + } + ] + }, + "NumberFromString": { + "type": "string", + "description": "a string to be decoded into a number" + }, + "UUID": { + "type": "string", + "description": "a Universally Unique Identifier", + "format": "uuid", + "pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$" + } + }, + "securitySchemes": {} + }, + "security": [], + "tags": [ + { + "name": "core" + }, + { + "name": "projects" + }, + { + "name": "projectPorts" + }, + { + "name": "projectBrowser" + }, + { + "name": "projectDatabases" + }, + { + "name": "auth" + }, + { + "name": "projectAuth" + }, + { + "name": "terminal" + }, + { + "name": "prompts" + }, + { + "name": "skills" + }, + { + "name": "tasks" + }, + { + "name": "sharing" + } + ] +} diff --git a/packages/api/src/api/openapi.ts b/packages/api/src/api/openapi.ts new file mode 100644 index 00000000..17d58598 --- /dev/null +++ b/packages/api/src/api/openapi.ts @@ -0,0 +1,773 @@ +import * as HttpApi from "@effect/platform/HttpApi" +import * as HttpApiEndpoint from "@effect/platform/HttpApiEndpoint" +import * as HttpApiGroup from "@effect/platform/HttpApiGroup" +import * as HttpApiSchema from "@effect/platform/HttpApiSchema" +import * as OpenApi from "@effect/platform/OpenApi" +import * as Schema from "effect/Schema" + +import { + ActiveProjectTerminalSessionRequestSchema, + ApplyAllRequestSchema, + ApplyProjectRequestSchema, + AuthMenuRequestSchema, + AuthTerminalSessionRequestSchema, + CodexAuthImportRequestSchema, + CodexAuthLogoutRequestSchema, + CreateProjectRequestSchema, + GitAuthLoginRequestSchema, + GitAuthLogoutRequestSchema, + GithubAuthLoginRequestSchema, + GithubAuthLogoutRequestSchema, + GitlabAuthLoginRequestSchema, + GitlabAuthLogoutRequestSchema, + GrokAuthLogoutRequestSchema, + ProjectAuthRequestSchema, + ProjectBrowserSessionSchema, + ProjectDatabaseForwardSchema, + ProjectDatabaseProfileRequestSchema, + ProjectDatabaseProfileSchema, + ProjectDatabaseSessionSchema, + ProjectPortForwardRequestSchema, + ProjectSkillUpdateRequestSchema, + StartPanelCloudflareTunnelRequestSchema, + StartProjectTerminalSessionRequestSchema, + UpProjectRequestSchema +} from "./schema.js" + +const NullableStringSchema = Schema.NullOr(Schema.String) +const OptionalOkSchema = Schema.optional(Schema.Boolean) + +const ProjectIdParam = HttpApiSchema.param("projectId", Schema.String) +const ProjectKeyParam = HttpApiSchema.param("projectKey", Schema.String) +const SessionIdParam = HttpApiSchema.param("sessionId", Schema.String) +const TargetPortParam = HttpApiSchema.param("targetPort", Schema.NumberFromString) +const ProfileIdParam = HttpApiSchema.param("profileId", Schema.String) +const PromptKindParam = HttpApiSchema.param("kind", Schema.String) +const ScopeIdParam = HttpApiSchema.param("scopeId", Schema.String) +const SkillNameParam = HttpApiSchema.param("name", Schema.String) +const PidParam = HttpApiSchema.param("pid", Schema.NumberFromString) + +export const EmptyResponseSchema = Schema.Void + +export const HealthResponseSchema = Schema.Struct({ + cwd: Schema.String, + ok: Schema.Boolean, + projectsRoot: Schema.String, + revision: NullableStringSchema +}) + +export const ProjectStatusSchema = Schema.Literal("running", "stopped", "unknown") + +const ProjectSummaryFields = { + clonedOnHostname: Schema.optional(Schema.String), + containerName: Schema.optional(Schema.String), + displayName: Schema.String, + id: Schema.String, + projectKey: Schema.String, + repoRef: Schema.String, + repoUrl: Schema.String, + sshSessions: Schema.Number, + startedAtEpochMs: Schema.NullOr(Schema.Number), + startedAtIso: NullableStringSchema, + status: ProjectStatusSchema, + statusLabel: Schema.String +} + +export const ProjectSummarySchema = Schema.Struct(ProjectSummaryFields) + +export const ProjectDetailsSchema = Schema.Struct({ + ...ProjectSummaryFields, + authorizedKeysExists: Schema.Boolean, + authorizedKeysPath: Schema.String, + codexAuthPath: Schema.String, + codexHome: Schema.String, + containerName: Schema.String, + envGlobalPath: Schema.String, + envProjectPath: Schema.String, + gpu: Schema.Literal("none", "all"), + projectDir: Schema.String, + serviceName: Schema.String, + sshCommand: Schema.String, + sshPort: Schema.Number, + sshUser: Schema.String, + targetDir: Schema.String +}) + +export const ProjectsResponseSchema = Schema.Struct({ + projects: Schema.Array(ProjectSummarySchema) +}) + +export const ProjectResponseSchema = Schema.Struct({ + ok: OptionalOkSchema, + project: ProjectDetailsSchema +}) + +export const CreateProjectAcceptedResponseSchema = Schema.Struct({ + accepted: Schema.Literal(true), + cursor: Schema.Number, + projectId: Schema.String +}) + +export const StartProjectTerminalSessionAcceptedResponseSchema = Schema.Struct({ + accepted: Schema.Literal(true), + cursor: Schema.Number, + projectId: Schema.String, + requestId: Schema.String +}) + +export const OutputResponseSchema = Schema.Struct({ + output: Schema.String +}) + +export const ProjectPortForwardStatusSchema = Schema.Literal("running", "stopped", "unknown") + +export const ProjectPortForwardSchema = Schema.Struct({ + bindHost: Schema.String, + containerName: Schema.String, + createdAt: NullableStringSchema, + hostPort: Schema.Number, + id: Schema.String, + projectId: Schema.String, + projectKey: Schema.String, + proxyPath: Schema.String, + publicHost: Schema.String, + status: ProjectPortForwardStatusSchema, + targetContainerName: Schema.String, + targetPort: Schema.Number, + url: Schema.String +}) + +export const ProjectPortForwardsResponseSchema = Schema.Struct({ + forwards: Schema.Array(ProjectPortForwardSchema) +}) + +export const ProjectPortForwardResponseSchema = Schema.Struct({ + forward: ProjectPortForwardSchema +}) + +export const ProjectBrowserResponseSchema = Schema.Struct({ + browser: ProjectBrowserSessionSchema +}) + +export const PanelCloudflareTunnelStatusSchema = Schema.Literal("starting", "running", "stopped", "failed") + +export const PanelCloudflareTunnelSessionSchema = Schema.Struct({ + error: NullableStringSchema, + id: Schema.String, + logTail: Schema.Array(Schema.String), + panelUrl: Schema.String, + publicUrl: NullableStringSchema, + startedAt: Schema.String, + status: PanelCloudflareTunnelStatusSchema, + stoppedAt: NullableStringSchema +}) + +export const PanelCloudflareTunnelResponseSchema = Schema.Struct({ + tunnel: Schema.NullOr(PanelCloudflareTunnelSessionSchema) +}) + +export const ProjectDatabaseProfilesResponseSchema = Schema.Struct({ + profiles: Schema.Array(ProjectDatabaseProfileSchema) +}) + +export const ProjectDatabaseProfileResponseSchema = Schema.Struct({ + profile: ProjectDatabaseProfileSchema +}) + +export const ProjectDatabaseSessionResponseSchema = Schema.Struct({ + session: ProjectDatabaseSessionSchema +}) + +export const ProjectDatabaseForwardsResponseSchema = Schema.Struct({ + forwards: Schema.Array(ProjectDatabaseForwardSchema) +}) + +export const ProjectDatabaseForwardResponseSchema = Schema.Struct({ + forward: ProjectDatabaseForwardSchema +}) + +export const GithubTokenStatusSchema = Schema.Struct({ + key: Schema.String, + label: Schema.String, + login: NullableStringSchema, + status: Schema.Literal("valid", "invalid", "unknown") +}) + +export const GithubAuthStatusSchema = Schema.Struct({ + summary: Schema.String, + tokens: Schema.Array(GithubTokenStatusSchema) +}) + +export const GithubStatusResponseSchema = Schema.Struct({ + ok: OptionalOkSchema, + status: GithubAuthStatusSchema +}) + +export const GitlabTokenStatusSchema = Schema.Struct({ + key: Schema.String, + label: Schema.String, + login: NullableStringSchema, + status: Schema.Literal("valid", "invalid", "unknown") +}) + +export const GitlabAuthStatusSchema = Schema.Struct({ + summary: Schema.String, + tokens: Schema.Array(GitlabTokenStatusSchema) +}) + +export const GitlabStatusResponseSchema = Schema.Struct({ + ok: OptionalOkSchema, + status: GitlabAuthStatusSchema +}) + +export const GitAuthConnectionStatusSchema = Schema.Struct({ + host: Schema.String, + user: Schema.String +}) + +export const GitAuthStatusSchema = Schema.Struct({ + connections: Schema.Array(GitAuthConnectionStatusSchema), + summary: Schema.String +}) + +export const GitStatusResponseSchema = Schema.Struct({ + ok: OptionalOkSchema, + status: GitAuthStatusSchema +}) + +export const CodexAuthStatusSchema = Schema.Struct({ + account: NullableStringSchema, + authPath: Schema.String, + label: Schema.String, + message: Schema.String, + present: Schema.Boolean +}) + +export const CodexStatusResponseSchema = Schema.Struct({ + ok: OptionalOkSchema, + status: CodexAuthStatusSchema +}) + +export const GrokAuthStatusSchema = Schema.Struct({ + authPath: Schema.String, + connected: Schema.Boolean, + label: Schema.String, + message: Schema.String, + method: Schema.Literal("none", "api-key", "oauth") +}) + +export const GrokStatusResponseSchema = Schema.Struct({ + ok: OptionalOkSchema, + status: GrokAuthStatusSchema +}) + +export const AuthSnapshotSchema = Schema.Struct({ + claudeAuthEntries: Schema.Number, + claudeAuthPath: Schema.String, + codexAuthEntries: Schema.optionalWith(Schema.Number, { default: () => 0 }), + codexAuthPath: Schema.optionalWith(Schema.String, { default: () => "" }), + geminiAuthEntries: Schema.Number, + geminiAuthPath: Schema.String, + gitTokenEntries: Schema.Number, + gitUserEntries: Schema.Number, + githubTokenEntries: Schema.Number, + globalEnvPath: Schema.String, + grokAuthEntries: Schema.optionalWith(Schema.Number, { default: () => 0 }), + grokAuthPath: Schema.optionalWith(Schema.String, { default: () => "" }), + totalEntries: Schema.Number +}) + +export const AuthSnapshotResponseSchema = Schema.Struct({ + ok: OptionalOkSchema, + snapshot: AuthSnapshotSchema +}) + +export const ProjectAuthSnapshotSchema = Schema.Struct({ + activeClaudeLabel: NullableStringSchema, + activeGeminiLabel: NullableStringSchema, + activeGitLabel: NullableStringSchema, + activeGithubLabel: NullableStringSchema, + activeGrokLabel: NullableStringSchema, + claudeAuthEntries: Schema.Number, + claudeAuthPath: Schema.String, + codexAuthEntries: Schema.optionalWith(Schema.Number, { default: () => 0 }), + codexAuthPath: Schema.optionalWith(Schema.String, { default: () => "" }), + envGlobalPath: Schema.String, + envProjectPath: Schema.String, + geminiAuthEntries: Schema.Number, + geminiAuthPath: Schema.String, + gitTokenEntries: Schema.Number, + githubTokenEntries: Schema.Number, + grokAuthEntries: Schema.optionalWith(Schema.Number, { default: () => 0 }), + grokAuthPath: Schema.optionalWith(Schema.String, { default: () => "" }), + projectDir: Schema.String, + projectName: Schema.String, + totalEntries: Schema.Number +}) + +export const ProjectAuthSnapshotResponseSchema = Schema.Struct({ + ok: OptionalOkSchema, + snapshot: ProjectAuthSnapshotSchema +}) + +export const TerminalSessionSchema = Schema.Struct({ + attachedClients: Schema.optional(Schema.Number), + closedAt: Schema.optional(Schema.String), + createdAt: Schema.String, + exitCode: Schema.optional(Schema.Number), + id: Schema.String, + projectId: Schema.String, + signal: Schema.optional(Schema.Number), + sshCommand: Schema.String, + startedAt: Schema.optional(Schema.String), + status: Schema.Literal("ready", "attached", "exited", "failed") +}) + +export const TerminalSessionResponseSchema = Schema.Struct({ + ok: OptionalOkSchema, + project: ProjectDetailsSchema, + session: TerminalSessionSchema +}) + +export const ProjectTerminalSessionsResponseSchema = Schema.Struct({ + activeSessionId: NullableStringSchema, + sessions: Schema.Array(TerminalSessionSchema) +}) + +export const ProjectTerminalSessionResponseSchema = Schema.Struct({ + session: TerminalSessionSchema +}) + +export const TerminalSessionLookupResponseSchema = Schema.Struct({ + projectDisplayName: Schema.String, + projectKey: Schema.String, + session: TerminalSessionSchema +}) + +export const AuthTerminalSessionResponseSchema = Schema.Struct({ + ok: OptionalOkSchema, + session: TerminalSessionSchema +}) + +export const ProjectPromptKindSchema = Schema.Literal("claude", "codex", "gemini", "grok") + +export const ProjectPromptFileSchema = Schema.Struct({ + absolutePath: Schema.String, + bytes: Schema.Number, + content: Schema.String, + exists: Schema.Boolean, + fileName: Schema.String, + kind: ProjectPromptKindSchema, + relativePath: Schema.String +}) + +export const ProjectPromptsSnapshotSchema = Schema.Struct({ + projectDir: Schema.String, + projectId: Schema.String, + projectKey: Schema.String, + prompts: Schema.Array(ProjectPromptFileSchema) +}) + +export const ProjectPromptsResponseSchema = Schema.Struct({ + ok: OptionalOkSchema, + snapshot: ProjectPromptsSnapshotSchema +}) + +export const ProjectPromptUpdateResponseSchema = Schema.Struct({ + ok: OptionalOkSchema, + prompt: ProjectPromptFileSchema, + snapshot: ProjectPromptsSnapshotSchema +}) + +export const ProjectSkillScopeSchema = Schema.Literal( + "skills", + "agents/skills", + "agents/.skills", + "claude/skills", + "codex/skills", + "gemini/skills", + "grok/skills" +) + +export const ProjectSkillFileSchema = Schema.Struct({ + absolutePath: Schema.String, + bytes: Schema.Number, + content: Schema.String, + id: Schema.String, + name: Schema.String, + relativePath: Schema.String, + scope: ProjectSkillScopeSchema, + updatedAtIso: NullableStringSchema +}) + +export const ProjectSkillScopeInfoSchema = Schema.Struct({ + absoluteRoot: Schema.String, + relativeRoot: Schema.String, + scope: ProjectSkillScopeSchema +}) + +export const ProjectSkillsSnapshotSchema = Schema.Struct({ + projectDir: Schema.String, + projectId: Schema.String, + projectKey: Schema.String, + scopes: Schema.Array(ProjectSkillScopeInfoSchema), + skills: Schema.Array(ProjectSkillFileSchema) +}) + +export const ProjectSkillsResponseSchema = Schema.Struct({ + ok: OptionalOkSchema, + snapshot: ProjectSkillsSnapshotSchema +}) + +export const ProjectSkillUpdateResponseSchema = Schema.Struct({ + ok: OptionalOkSchema, + skill: ProjectSkillFileSchema, + snapshot: ProjectSkillsSnapshotSchema +}) + +export const ContainerTaskKindSchema = Schema.Literal("ssh", "web-terminal", "agent", "background", "system") + +export const ContainerTaskSchema = Schema.Struct({ + command: Schema.String, + elapsed: Schema.optional(Schema.String), + etime: Schema.String, + etimes: Schema.Number, + kind: ContainerTaskKindSchema, + logAvailable: Schema.Boolean, + managedId: Schema.optional(Schema.String), + pid: Schema.Number, + ppid: Schema.Number, + tty: Schema.String, + user: Schema.String +}) + +export const ContainerTaskSnapshotSchema = Schema.Struct({ + containerName: Schema.String, + generatedAt: Schema.String, + projectId: Schema.String, + sshConnections: Schema.Number, + tasks: Schema.Array(ContainerTaskSchema) +}) + +export const ContainerTaskSnapshotResponseSchema = Schema.Struct({ + snapshot: ContainerTaskSnapshotSchema +}) + +const QueryIncludeDefaultSchema = Schema.Struct({ + includeDefault: Schema.optional(Schema.String) +}) + +const QueryLabelSchema = Schema.Struct({ + label: Schema.optional(Schema.String) +}) + +const QueryLinesSchema = Schema.Struct({ + lines: Schema.optional(Schema.String) +}) + +const ApiErrorResponseSchema = Schema.Struct({ + error: Schema.String, + message: Schema.String +}) + +const endpoint = { + del: HttpApiEndpoint.del, + get: HttpApiEndpoint.get, + post: HttpApiEndpoint.post, + put: HttpApiEndpoint.put +} + +const CoreGroup = HttpApiGroup.make("core").add( + endpoint.get("health", "/health").addSuccess(HealthResponseSchema) +) + +const ProjectsGroup = HttpApiGroup.make("projects") + .add(endpoint.get("listProjects", "/projects").addSuccess(ProjectsResponseSchema)) + .add( + endpoint.post("createProject", "/projects") + .setPayload(CreateProjectRequestSchema) + .addSuccess(ProjectResponseSchema) + .addSuccess(CreateProjectAcceptedResponseSchema, { status: 202 }) + ) + .add( + endpoint.post("applyAllProjects", "/projects/apply-all") + .setPayload(ApplyAllRequestSchema) + .addSuccess(EmptyResponseSchema) + ) + .add(endpoint.post("downAllProjects", "/projects/down-all").addSuccess(EmptyResponseSchema)) + .add(endpoint.get("getProject")`/projects/${ProjectIdParam}`.addSuccess(ProjectResponseSchema)) + .add(endpoint.del("deleteProject")`/projects/${ProjectIdParam}`.addSuccess(EmptyResponseSchema)) + .add(endpoint.post("downProject")`/projects/${ProjectIdParam}/down`.addSuccess(EmptyResponseSchema)) + .add( + endpoint.post("applyProject")`/projects/${ProjectIdParam}/apply` + .setPayload(ApplyProjectRequestSchema) + .addSuccess(ProjectResponseSchema) + ) + .add( + endpoint.post("upProject")`/projects/${ProjectIdParam}/up` + .setPayload(UpProjectRequestSchema) + .addSuccess(ProjectResponseSchema) + ) + .add(endpoint.post("resumeProject")`/projects/${ProjectIdParam}/resume`.addSuccess(ProjectResponseSchema)) + .add(endpoint.post("suspendProject")`/projects/${ProjectIdParam}/suspend`.addSuccess(ProjectResponseSchema)) + .add(endpoint.get("projectPs")`/projects/${ProjectIdParam}/ps`.addSuccess(OutputResponseSchema)) + .add(endpoint.get("projectLogs")`/projects/${ProjectIdParam}/logs`.addSuccess(OutputResponseSchema)) + +const ProjectPortsGroup = HttpApiGroup.make("projectPorts") + .add(endpoint.get("listProjectPorts")`/projects/${ProjectIdParam}/ports`.addSuccess(ProjectPortForwardsResponseSchema)) + .add( + endpoint.post("createProjectPort")`/projects/${ProjectIdParam}/ports` + .setPayload(ProjectPortForwardRequestSchema) + .addSuccess(ProjectPortForwardResponseSchema) + ) + .add( + endpoint.del("deleteProjectPort")`/projects/${ProjectIdParam}/ports/${TargetPortParam}` + .addSuccess(EmptyResponseSchema) + ) + +const ProjectBrowserGroup = HttpApiGroup.make("projectBrowser") + .add(endpoint.get("readProjectBrowser")`/projects/${ProjectIdParam}/browser`.addSuccess(ProjectBrowserResponseSchema)) + .add( + endpoint.post("startProjectBrowser")`/projects/${ProjectIdParam}/browser/start` + .addSuccess(ProjectBrowserResponseSchema) + ) + +const ProjectDatabasesGroup = HttpApiGroup.make("projectDatabases") + .add( + endpoint.get("listDatabaseProfiles")`/projects/${ProjectIdParam}/databases/profiles` + .addSuccess(ProjectDatabaseProfilesResponseSchema) + ) + .add( + endpoint.post("saveDatabaseProfile")`/projects/${ProjectIdParam}/databases/profiles` + .setPayload(ProjectDatabaseProfileRequestSchema) + .addSuccess(ProjectDatabaseProfileResponseSchema) + ) + .add( + endpoint.del("deleteDatabaseProfile")`/projects/${ProjectIdParam}/databases/profiles/${ProfileIdParam}` + .addSuccess(EmptyResponseSchema) + ) + .add( + endpoint.post("exposeDatabaseProfile")`/projects/${ProjectIdParam}/databases/profiles/${ProfileIdParam}/expose` + .addSuccess(ProjectDatabaseForwardResponseSchema) + ) + .add( + endpoint.del("deleteDatabaseForward")`/projects/${ProjectIdParam}/databases/profiles/${ProfileIdParam}/expose` + .addSuccess(EmptyResponseSchema) + ) + .add( + endpoint.get("listDatabaseForwards")`/projects/${ProjectIdParam}/databases/forwards` + .addSuccess(ProjectDatabaseForwardsResponseSchema) + ) + .add( + endpoint.get("readDatabaseSession")`/projects/${ProjectIdParam}/databases/session` + .addSuccess(ProjectDatabaseSessionResponseSchema) + ) + .add( + endpoint.post("openDatabaseEditor")`/projects/${ProjectIdParam}/databases/open` + .addSuccess(ProjectDatabaseSessionResponseSchema) + ) + .add( + endpoint.post("restartDatabaseEditor")`/projects/${ProjectIdParam}/databases/restart` + .addSuccess(ProjectDatabaseSessionResponseSchema) + ) + +const AuthGroup = HttpApiGroup.make("auth") + .add(endpoint.get("githubStatus", "/auth/github/status").addSuccess(GithubStatusResponseSchema)) + .add(endpoint.get("gitlabStatus", "/auth/gitlab/status").addSuccess(GitlabStatusResponseSchema)) + .add(endpoint.get("gitStatus", "/auth/git/status").addSuccess(GitStatusResponseSchema)) + .add( + endpoint.get("grokStatus", "/auth/grok/status") + .setUrlParams(QueryLabelSchema) + .addSuccess(GrokStatusResponseSchema) + ) + .add( + endpoint.get("codexStatus", "/auth/codex/status") + .setUrlParams(QueryLabelSchema) + .addSuccess(CodexStatusResponseSchema) + ) + .add( + endpoint.post("githubLogin", "/auth/github/login") + .setPayload(GithubAuthLoginRequestSchema) + .addSuccess(GithubStatusResponseSchema, { status: 201 }) + ) + .add( + endpoint.post("githubLogout", "/auth/github/logout") + .setPayload(GithubAuthLogoutRequestSchema) + .addSuccess(GithubStatusResponseSchema) + ) + .add( + endpoint.post("gitlabLogin", "/auth/gitlab/login") + .setPayload(GitlabAuthLoginRequestSchema) + .addSuccess(GitlabStatusResponseSchema, { status: 201 }) + ) + .add( + endpoint.post("gitlabLogout", "/auth/gitlab/logout") + .setPayload(GitlabAuthLogoutRequestSchema) + .addSuccess(GitlabStatusResponseSchema) + ) + .add( + endpoint.post("gitLogin", "/auth/git/login") + .setPayload(GitAuthLoginRequestSchema) + .addSuccess(GitStatusResponseSchema, { status: 201 }) + ) + .add( + endpoint.post("gitLogout", "/auth/git/logout") + .setPayload(GitAuthLogoutRequestSchema) + .addSuccess(GitStatusResponseSchema) + ) + .add(endpoint.get("authMenu", "/auth/menu").addSuccess(AuthSnapshotResponseSchema)) + .add( + endpoint.post("authMenuAction", "/auth/menu") + .setPayload(AuthMenuRequestSchema) + .addSuccess(AuthSnapshotResponseSchema) + ) + .add( + endpoint.post("authTerminalSession", "/auth/terminal-sessions") + .setPayload(AuthTerminalSessionRequestSchema) + .addSuccess(AuthTerminalSessionResponseSchema) + ) + .add( + endpoint.post("codexImport", "/auth/codex/import") + .setPayload(CodexAuthImportRequestSchema) + .addSuccess(CodexStatusResponseSchema, { status: 201 }) + ) + .add( + endpoint.post("codexLogout", "/auth/codex/logout") + .setPayload(CodexAuthLogoutRequestSchema) + .addSuccess(CodexStatusResponseSchema) + ) + .add( + endpoint.post("grokLogout", "/auth/grok/logout") + .setPayload(GrokAuthLogoutRequestSchema) + .addSuccess(GrokStatusResponseSchema) + ) + +const ProjectAuthGroup = HttpApiGroup.make("projectAuth") + .add(endpoint.get("projectAuth")`/projects/${ProjectIdParam}/auth/menu`.addSuccess(ProjectAuthSnapshotResponseSchema)) + .add( + endpoint.post("projectAuthAction")`/projects/${ProjectIdParam}/auth/menu` + .setPayload(ProjectAuthRequestSchema) + .addSuccess(ProjectAuthSnapshotResponseSchema) + ) + +const TerminalGroup = HttpApiGroup.make("terminal") + .add( + endpoint.post("createTerminalByKey")`/projects/by-key/${ProjectKeyParam}/terminal-sessions` + .addSuccess(TerminalSessionResponseSchema) + ) + .add( + endpoint.post("startTerminalByKey")`/projects/by-key/${ProjectKeyParam}/terminal-sessions/start` + .setPayload(StartProjectTerminalSessionRequestSchema) + .addSuccess(StartProjectTerminalSessionAcceptedResponseSchema, { status: 202 }) + ) + .add( + endpoint.get("listTerminalsByKey")`/projects/by-key/${ProjectKeyParam}/terminal-sessions` + .addSuccess(ProjectTerminalSessionsResponseSchema) + ) + .add( + endpoint.get("getTerminalByKey")`/projects/by-key/${ProjectKeyParam}/terminal-sessions/${SessionIdParam}` + .addSuccess(ProjectTerminalSessionResponseSchema) + ) + .add( + endpoint.del("deleteTerminalByKey")`/projects/by-key/${ProjectKeyParam}/terminal-sessions/${SessionIdParam}` + .addSuccess(EmptyResponseSchema) + ) + .add( + endpoint.put("setActiveTerminalByKey")`/projects/by-key/${ProjectKeyParam}/terminal-sessions/active` + .setPayload(ActiveProjectTerminalSessionRequestSchema) + .addSuccess(ProjectTerminalSessionResponseSchema) + ) + .add(endpoint.get("lookupTerminal")`/terminal-sessions/${SessionIdParam}`.addSuccess(TerminalSessionLookupResponseSchema)) + .add( + endpoint.del("deleteAuthTerminal")`/auth/terminal-sessions/${SessionIdParam}`.addSuccess(EmptyResponseSchema) + ) + +const PromptsGroup = HttpApiGroup.make("prompts") + .add(endpoint.get("listPrompts")`/projects/${ProjectIdParam}/prompts`.addSuccess(ProjectPromptsResponseSchema)) + .add( + endpoint.put("writePrompt")`/projects/${ProjectIdParam}/prompts/${PromptKindParam}` + .setPayload(Schema.Struct({ content: Schema.String })) + .addSuccess(ProjectPromptUpdateResponseSchema) + ) + .add( + endpoint.del("deletePrompt")`/projects/${ProjectIdParam}/prompts/${PromptKindParam}` + .addSuccess(ProjectPromptsResponseSchema) + ) + +const SkillsGroup = HttpApiGroup.make("skills") + .add(endpoint.get("listSkills")`/projects/${ProjectIdParam}/skills`.addSuccess(ProjectSkillsResponseSchema)) + .add( + endpoint.post("writeSkill")`/projects/${ProjectIdParam}/skills` + .setPayload(ProjectSkillUpdateRequestSchema) + .addSuccess(ProjectSkillUpdateResponseSchema) + ) + .add( + endpoint.del("deleteSkill")`/projects/${ProjectIdParam}/skills/${ScopeIdParam}/${SkillNameParam}` + .addSuccess(ProjectSkillsResponseSchema) + ) + +const TasksGroup = HttpApiGroup.make("tasks") + .add( + endpoint.get("listTasks")`/projects/${ProjectIdParam}/tasks` + .setUrlParams(QueryIncludeDefaultSchema) + .addSuccess(ContainerTaskSnapshotResponseSchema) + ) + .add(endpoint.post("stopTask")`/projects/${ProjectIdParam}/tasks/${PidParam}/stop`.addSuccess(EmptyResponseSchema)) + .add( + endpoint.get("taskLogs")`/projects/${ProjectIdParam}/tasks/${PidParam}/logs` + .setUrlParams(QueryLinesSchema) + .addSuccess(OutputResponseSchema) + ) + +const SharingGroup = HttpApiGroup.make("sharing") + .add(endpoint.get("readPanelCloudflareTunnel", "/cloudflare-tunnels/panel").addSuccess(PanelCloudflareTunnelResponseSchema)) + .add( + endpoint.post("startPanelCloudflareTunnel", "/cloudflare-tunnels/panel") + .setPayload(StartPanelCloudflareTunnelRequestSchema) + .addSuccess(PanelCloudflareTunnelResponseSchema, { status: 202 }) + ) + .add(endpoint.del("stopPanelCloudflareTunnel", "/cloudflare-tunnels/panel").addSuccess(PanelCloudflareTunnelResponseSchema)) + +export const DockerGitApi = HttpApi.make("docker-git") + .annotate(OpenApi.Title, "docker-git API") + .annotate(OpenApi.Version, "1.0.0") + .annotate(OpenApi.Description, "Effect contract for docker-git JSON REST endpoints.") + .addError(ApiErrorResponseSchema, { status: 400 }) + .add(CoreGroup) + .add(ProjectsGroup) + .add(ProjectPortsGroup) + .add(ProjectBrowserGroup) + .add(ProjectDatabasesGroup) + .add(AuthGroup) + .add(ProjectAuthGroup) + .add(TerminalGroup) + .add(PromptsGroup) + .add(SkillsGroup) + .add(TasksGroup) + .add(SharingGroup) + +/** + * Builds the OpenAPI document from the Effect HttpApi contract. + * + * @returns OpenAPI 3.1 specification for JSON REST endpoints. + * + * @pure true - deterministic projection from the static Effect contract. + * @effect none + * @invariant every documented path is derived from DockerGitApi, not hand-written JSON. + * @precondition DockerGitApi is importable without starting the HTTP server. + * @postcondition the returned spec has openapi = "3.1.0". + * @complexity O(n) time / O(n) space where n is the number of endpoints and schemas. + * @throws Never. + */ +// CHANGE: derive Swagger/OpenAPI from the Effect HttpApi contract. +// WHY: frontend clients must be generated from one typed REST contract. +// QUOTE(ТЗ): "Надо сделать REST API нормальный на базе Effect и использовать Swagger." +// REF: user-message-2026-06-18-openapi-fetch +// SOURCE: https://openapi-ts.dev/openapi-fetch/ +// FORMAT THEOREM: forall endpoint e in DockerGitApi, e is represented in buildDockerGitOpenApi().paths. +// PURITY: CORE +// EFFECT: none +// INVARIANT: spec = OpenApi.fromApi(DockerGitApi). +// COMPLEXITY: O(n)/O(n) +export const buildDockerGitOpenApi = (): OpenApi.OpenAPISpec => + OpenApi.fromApi(DockerGitApi, { additionalPropertiesStrategy: "strict" }) diff --git a/packages/api/src/http.ts b/packages/api/src/http.ts index 0118efac..94c4752c 100644 --- a/packages/api/src/http.ts +++ b/packages/api/src/http.ts @@ -46,6 +46,7 @@ import { UpProjectRequestSchema } from "./api/schema.js" import type { UpProjectRequestInput } from "./api/schema.js" +import { buildDockerGitOpenApi } from "./api/openapi.js" import { defaultProjectsRoot } from "@effect-template/lib/usecases/menu-helpers" import { resolveWorkspaceRoot } from "@effect-template/lib/shell/workspace-root" import { @@ -300,6 +301,51 @@ const textResponse = (data: string, contentType: string, status = 200) => ) ) +/** + * Renders a Swagger UI document that loads the generated OpenAPI contract. + * + * @returns HTML document for interactive API documentation. + * + * @pure true - deterministic string construction. + * @effect none + * @invariant the document references the relative openapi.json path. + * @precondition Swagger UI CDN assets are reachable by the browser. + * @postcondition direct /docs and proxied /api/docs both resolve their adjacent OpenAPI document. + * @complexity O(1) time / O(1) space. + * @throws Never. + */ +// CHANGE: expose browser-readable Swagger UI for the Effect REST contract. +// WHY: generated clients and humans must inspect the same OpenAPI document. +// QUOTE(ТЗ): "использовать Swagger" +// REF: user-message-2026-06-18-openapi-fetch +// SOURCE: n/a +// FORMAT THEOREM: docsPath(d) = p -> openApiPath(d) = sibling(p, "openapi.json") +// PURITY: CORE +// EFFECT: none +// INVARIANT: Swagger UI reads the relative OpenAPI document. +// COMPLEXITY: O(1)/O(1) +const renderSwaggerDocsHtml = (): string => ` + +
+ + +