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 => ` + + + + + docker-git API + + + +
+ + + +` + const binaryResponse = (data: Uint8Array, contentType: string, status = 200) => Effect.succeed( HttpServerResponse.setStatus( @@ -941,6 +987,14 @@ const skillerConnectResponse = ( export const makeRouter = () => { const withCoreRoutes = HttpRouter.empty.pipe( + HttpRouter.get( + "/openapi.json", + jsonResponse(buildDockerGitOpenApi(), 200).pipe(Effect.catchAll(errorResponse)) + ), + HttpRouter.get( + "/docs", + textResponse(renderSwaggerDocsHtml(), "text/html; charset=utf-8").pipe(Effect.catchAll(errorResponse)) + ), HttpRouter.get( "/health", Effect.gen(function*(_) { diff --git a/packages/api/tests/openapi.test.ts b/packages/api/tests/openapi.test.ts new file mode 100644 index 00000000..2c49bf6e --- /dev/null +++ b/packages/api/tests/openapi.test.ts @@ -0,0 +1,25 @@ +import { describe, expect, it } from "@effect/vitest" +import { Effect } from "effect" + +import { buildDockerGitOpenApi } from "../src/api/openapi.js" + +describe("openapi contract", () => { + it.effect("documents generated REST paths from the Effect HttpApi contract", () => + Effect.sync(() => { + const spec = buildDockerGitOpenApi() + const paths = spec.paths ?? {} + + expect(spec.openapi).toBe("3.1.0") + expect(paths["/health"]).toBeDefined() + expect(paths["/projects"]).toBeDefined() + expect(paths["/projects/{projectId}"]).toBeDefined() + expect(paths["/auth/git/status"]).toBeDefined() + expect(paths["/auth/gitlab/status"]).toBeDefined() + expect(paths["/auth/codex/status"]).toBeDefined() + expect(paths["/auth/grok/status"]).toBeDefined() + expect(paths["/auth/codex/login"]).toBeUndefined() + expect(paths["/projects/{projectId}/auth/menu"]).toBeDefined() + expect(paths["/projects/{projectId}/auth"]).toBeUndefined() + expect(Object.keys(paths)).toHaveLength(54) + })) +}) diff --git a/packages/app/package.json b/packages/app/package.json index 2256e2c5..57df0c32 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -18,6 +18,7 @@ "build:app": "vite build --ssr src/app/main.ts", "build:web": "vite build --config vite.web.config.ts", "build:web:strict": "bun ../../scripts/ci/check-web-build-output.mjs", + "generate:api": "bun ../../scripts/write-openapi.ts && openapi-typescript ../../packages/api/openapi.json -o src/web/generated/openapi-paths.ts", "prepack": "bun run build:docker-git", "dev": "vite build --watch --ssr src/app/main.ts", "dev:web": "vite --config vite.web.config.ts", @@ -64,7 +65,6 @@ "homepage": "https://github.com/ProverCoderAI/docker-git#readme", "packageManager": "bun@1.3.11", "dependencies": { - "@prover-coder-ai/docker-git-session-sync": "workspace:*", "@effect/cli": "^0.75.2", "@effect/cluster": "^0.59.0", "@effect/experimental": "^0.60.0", @@ -79,7 +79,9 @@ "@effect/workflow": "^0.18.2", "@gridland/bun": "0.4.3", "@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", @@ -118,6 +120,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", diff --git a/packages/app/src/web/api-auth-schema.ts b/packages/app/src/web/api-auth-schema.ts index d15eec9d..a51b8a32 100644 --- a/packages/app/src/web/api-auth-schema.ts +++ b/packages/app/src/web/api-auth-schema.ts @@ -23,6 +23,19 @@ export const GithubStatusResponseSchema = Schema.Struct({ status: GithubAuthStatusSchema }) +export const CodexAuthStatusSchema = Schema.Struct({ + account: NullableString, + authPath: Schema.String, + label: Schema.String, + message: Schema.String, + present: Schema.Boolean +}) + +export const CodexStatusResponseSchema = Schema.Struct({ + ok: Schema.optional(Schema.Boolean), + status: CodexAuthStatusSchema +}) + const AuthProviderSnapshotFields = { claudeAuthEntries: Schema.Number, claudeAuthPath: Schema.String, diff --git a/packages/app/src/web/api-create-project.ts b/packages/app/src/web/api-create-project.ts index a2ae250c..6eb44f11 100644 --- a/packages/app/src/web/api-create-project.ts +++ b/packages/app/src/web/api-create-project.ts @@ -1,13 +1,26 @@ import type { Effect } from "effect" -import { requestJson } from "./api-http.js" import { CreateProjectAcceptedResponseSchema } from "./api-schema.js" import type { CreateProjectAcceptedResponse, CreateProjectDraft } from "./api-schema.js" +import { openApiJsonSchema } from "./openapi-client.js" + +const createProjectAcceptedBody = (draft: CreateProjectDraft) => ({ + async: true, + cpuLimit: draft.cpuLimit, + enableMcpPlaywright: draft.enableMcpPlaywright, + force: draft.force, + forceEnv: draft.forceEnv, + gpu: draft.gpu, + openSsh: false, + outDir: draft.outDir, + ramLimit: draft.ramLimit, + repoRef: draft.repoRef, + repoUrl: draft.repoUrl, + up: draft.up, + useManagedAuthorizedKeys: true +}) export const startCreateProject = (draft: CreateProjectDraft): Effect.Effect => - requestJson( - "POST", - "/projects", - CreateProjectAcceptedResponseSchema, - { ...draft, async: true, openSsh: false, useManagedAuthorizedKeys: true } - ) + openApiJsonSchema(CreateProjectAcceptedResponseSchema, (client) => client.POST("/projects", { + body: createProjectAcceptedBody(draft) + })) diff --git a/packages/app/src/web/api-database.ts b/packages/app/src/web/api-database.ts index d45cd0bf..38019b33 100644 --- a/packages/app/src/web/api-database.ts +++ b/packages/app/src/web/api-database.ts @@ -1,6 +1,5 @@ import { Effect } from "effect" -import { requestJson, requestText } from "./api-http.js" import { ProjectDatabaseForwardResponseSchema, ProjectDatabaseForwardsResponseSchema, @@ -9,33 +8,24 @@ import { ProjectDatabaseSessionResponseSchema } from "./api-schema.js" import type { ProjectDatabaseForward, ProjectDatabaseSession } from "./api-schema.js" +import { openApiJsonSchema, openApiVoid } from "./openapi-client.js" export const projectDatabaseEditorUrl = (session: ProjectDatabaseSession): string => session.editorPath export const projectDatabaseExternalUrl = (forward: ProjectDatabaseForward): string => `${forward.publicHost}:${forward.hostPort}` -const projectDatabaseProfilePath = (projectId: string, profileId: string): string => - `/projects/${encodeURIComponent(projectId)}/databases/profiles/${encodeURIComponent(profileId)}` - -const projectDatabaseForwardPath = (projectId: string, profileId: string): string => - `${projectDatabaseProfilePath(projectId, profileId)}/expose` - export const loadProjectDatabaseProfiles = (projectId: string) => - requestJson( - "GET", - `/projects/${encodeURIComponent(projectId)}/databases/profiles`, - ProjectDatabaseProfilesResponseSchema - ).pipe( + openApiJsonSchema(ProjectDatabaseProfilesResponseSchema, (client) => client.GET("/projects/{projectId}/databases/profiles", { + params: { path: { projectId } } + })).pipe( Effect.map((response) => response.profiles) ) export const loadProjectDatabaseForwards = (projectId: string) => - requestJson( - "GET", - `/projects/${encodeURIComponent(projectId)}/databases/forwards`, - ProjectDatabaseForwardsResponseSchema - ).pipe( + openApiJsonSchema(ProjectDatabaseForwardsResponseSchema, (client) => client.GET("/projects/{projectId}/databases/forwards", { + params: { path: { projectId } } + })).pipe( Effect.map((response) => response.forwards) ) @@ -44,60 +34,55 @@ export const saveProjectDatabaseProfile = ( connectionString: string, label: string | null ) => - requestJson( - "POST", - `/projects/${encodeURIComponent(projectId)}/databases/profiles`, - ProjectDatabaseProfileResponseSchema, - { connectionString, label } - ).pipe( + openApiJsonSchema(ProjectDatabaseProfileResponseSchema, (client) => client.POST("/projects/{projectId}/databases/profiles", { + body: { connectionString, label }, + params: { path: { projectId } } + })).pipe( Effect.map((response) => response.profile) ) export const deleteProjectDatabaseProfile = ( projectId: string, profileId: string -) => requestText("DELETE", projectDatabaseProfilePath(projectId, profileId)).pipe(Effect.asVoid) +) => openApiVoid((client) => client.DELETE("/projects/{projectId}/databases/profiles/{profileId}", { + params: { path: { profileId, projectId } } +})) export const exposeProjectDatabaseProfile = ( projectId: string, profileId: string ) => - requestJson( - "POST", - `/projects/${encodeURIComponent(projectId)}/databases/profiles/${encodeURIComponent(profileId)}/expose`, - ProjectDatabaseForwardResponseSchema - ).pipe( + openApiJsonSchema(ProjectDatabaseForwardResponseSchema, (client) => + client.POST("/projects/{projectId}/databases/profiles/{profileId}/expose", { + params: { path: { profileId, projectId } } + })).pipe( Effect.map((response) => response.forward) ) export const deleteProjectDatabaseForward = ( projectId: string, profileId: string -) => requestText("DELETE", projectDatabaseForwardPath(projectId, profileId)).pipe(Effect.asVoid) +) => openApiVoid((client) => client.DELETE("/projects/{projectId}/databases/profiles/{profileId}/expose", { + params: { path: { profileId, projectId } } +})) export const loadProjectDatabaseSession = (projectId: string) => - requestJson( - "GET", - `/projects/${encodeURIComponent(projectId)}/databases/session`, - ProjectDatabaseSessionResponseSchema - ).pipe( + openApiJsonSchema(ProjectDatabaseSessionResponseSchema, (client) => client.GET("/projects/{projectId}/databases/session", { + params: { path: { projectId } } + })).pipe( Effect.map((response) => response.session) ) export const openProjectDatabaseEditor = (projectId: string) => - requestJson( - "POST", - `/projects/${encodeURIComponent(projectId)}/databases/open`, - ProjectDatabaseSessionResponseSchema - ).pipe( + openApiJsonSchema(ProjectDatabaseSessionResponseSchema, (client) => client.POST("/projects/{projectId}/databases/open", { + params: { path: { projectId } } + })).pipe( Effect.map((response) => response.session) ) export const restartProjectDatabaseEditor = (projectId: string) => - requestJson( - "POST", - `/projects/${encodeURIComponent(projectId)}/databases/restart`, - ProjectDatabaseSessionResponseSchema - ).pipe( + openApiJsonSchema(ProjectDatabaseSessionResponseSchema, (client) => client.POST("/projects/{projectId}/databases/restart", { + params: { path: { projectId } } + })).pipe( Effect.map((response) => response.session) ) diff --git a/packages/app/src/web/api-project-core.ts b/packages/app/src/web/api-project-core.ts index a12f2f75..57706770 100644 --- a/packages/app/src/web/api-project-core.ts +++ b/packages/app/src/web/api-project-core.ts @@ -1,26 +1,60 @@ import { Effect } from "effect" import type { ApplyProjectRequest, ProjectResourceLimitRequest } from "../shared/project-resource-request.js" -import { requestJson } from "./api-http.js" import type { CreateProjectDraft } from "./api-schema.js" import { OutputResponseSchema, ProjectResponseSchema } from "./api-schema.js" +import { openApiJsonSchema } from "./openapi-client.js" export type { ApplyProjectRequest, ProjectResourceLimitRequest } from "../shared/project-resource-request.js" type CreateProjectRequestDraft = CreateProjectDraft & ProjectResourceLimitRequest +const optionalProjectResourceFields = (request: ProjectResourceLimitRequest) => ({ + ...(request.playwrightCpuLimit === undefined ? {} : { playwrightCpuLimit: request.playwrightCpuLimit }), + ...(request.playwrightRamLimit === undefined ? {} : { playwrightRamLimit: request.playwrightRamLimit }) +}) + +const applyProjectBody = (request: ApplyProjectRequest | undefined) => ({ + ...(request?.cpuLimit === undefined ? {} : { cpuLimit: request.cpuLimit }), + ...(request?.gpu === undefined ? {} : { gpu: request.gpu }), + ...(request?.ramLimit === undefined ? {} : { ramLimit: request.ramLimit }), + ...(request === undefined ? {} : optionalProjectResourceFields(request)) +}) + +const createProjectBody = (draft: CreateProjectRequestDraft) => ({ + cpuLimit: draft.cpuLimit, + enableMcpPlaywright: draft.enableMcpPlaywright, + force: draft.force, + forceEnv: draft.forceEnv, + gpu: draft.gpu, + openSsh: false, + outDir: draft.outDir, + ramLimit: draft.ramLimit, + repoRef: draft.repoRef, + repoUrl: draft.repoUrl, + up: draft.up, + useManagedAuthorizedKeys: true, + ...optionalProjectResourceFields(draft) +}) + export const loadProjectDetails = (projectId: string) => - requestJson("GET", `/projects/${encodeURIComponent(projectId)}`, ProjectResponseSchema).pipe( + openApiJsonSchema(ProjectResponseSchema, (client) => client.GET("/projects/{projectId}", { + params: { path: { projectId } } + })).pipe( Effect.map((response) => response.project) ) export const loadProjectPs = (projectId: string) => - requestJson("GET", `/projects/${encodeURIComponent(projectId)}/ps`, OutputResponseSchema).pipe( + openApiJsonSchema(OutputResponseSchema, (client) => client.GET("/projects/{projectId}/ps", { + params: { path: { projectId } } + })).pipe( Effect.map((response) => response.output) ) export const loadProjectLogs = (projectId: string) => - requestJson("GET", `/projects/${encodeURIComponent(projectId)}/logs`, OutputResponseSchema).pipe( + openApiJsonSchema(OutputResponseSchema, (client) => client.GET("/projects/{projectId}/logs", { + params: { path: { projectId } } + })).pipe( Effect.map((response) => response.output) ) @@ -28,49 +62,38 @@ export const applyProject = ( projectId: string, request?: ApplyProjectRequest ) => - requestJson( - "POST", - `/projects/${encodeURIComponent(projectId)}/apply`, - ProjectResponseSchema, - request - ).pipe( + openApiJsonSchema(ProjectResponseSchema, (client) => client.POST("/projects/{projectId}/apply", { + body: applyProjectBody(request), + params: { path: { projectId } } + })).pipe( Effect.map((response) => response.project) ) export const createProject = (draft: CreateProjectRequestDraft) => - requestJson( - "POST", - "/projects", - ProjectResponseSchema, - { ...draft, openSsh: false, useManagedAuthorizedKeys: true } - ).pipe( + openApiJsonSchema(ProjectResponseSchema, (client) => client.POST("/projects", { + body: createProjectBody(draft) + })).pipe( Effect.map((response) => response.project) ) export const upProject = (projectId: string) => - requestJson( - "POST", - `/projects/${encodeURIComponent(projectId)}/up`, - ProjectResponseSchema, - { useManagedAuthorizedKeys: true } - ).pipe( + openApiJsonSchema(ProjectResponseSchema, (client) => client.POST("/projects/{projectId}/up", { + body: { useManagedAuthorizedKeys: true }, + params: { path: { projectId } } + })).pipe( Effect.map((response) => response.project) ) export const resumeProject = (projectId: string) => - requestJson( - "POST", - `/projects/${encodeURIComponent(projectId)}/resume`, - ProjectResponseSchema - ).pipe( + openApiJsonSchema(ProjectResponseSchema, (client) => client.POST("/projects/{projectId}/resume", { + params: { path: { projectId } } + })).pipe( Effect.map((response) => response.project) ) export const suspendProject = (projectId: string) => - requestJson( - "POST", - `/projects/${encodeURIComponent(projectId)}/suspend`, - ProjectResponseSchema - ).pipe( + openApiJsonSchema(ProjectResponseSchema, (client) => client.POST("/projects/{projectId}/suspend", { + params: { path: { projectId } } + })).pipe( Effect.map((response) => response.project) ) diff --git a/packages/app/src/web/api-prompts.ts b/packages/app/src/web/api-prompts.ts index a45d7a9f..c3e4a552 100644 --- a/packages/app/src/web/api-prompts.ts +++ b/packages/app/src/web/api-prompts.ts @@ -1,15 +1,16 @@ import { Effect } from "effect" -import { requestJson } from "./api-http.js" -import { ProjectPromptsResponseSchema, ProjectPromptUpdateResponseSchema } from "./api-schema.js" +import { + ProjectPromptsResponseSchema, + ProjectPromptUpdateResponseSchema +} from "./api-schema.js" import type { ProjectPromptKind } from "./api-schema.js" +import { openApiJsonSchema } from "./openapi-client.js" export const loadProjectPrompts = (projectId: string) => - requestJson( - "GET", - `/projects/${encodeURIComponent(projectId)}/prompts`, - ProjectPromptsResponseSchema - ).pipe( + openApiJsonSchema(ProjectPromptsResponseSchema, (client) => client.GET("/projects/{projectId}/prompts", { + params: { path: { projectId } } + })).pipe( Effect.map((response) => response.snapshot) ) @@ -18,12 +19,10 @@ export const writeProjectPrompt = ( kind: ProjectPromptKind, content: string ) => - requestJson( - "PUT", - `/projects/${encodeURIComponent(projectId)}/prompts/${encodeURIComponent(kind)}`, - ProjectPromptUpdateResponseSchema, - { content } - ).pipe( + openApiJsonSchema(ProjectPromptUpdateResponseSchema, (client) => client.PUT("/projects/{projectId}/prompts/{kind}", { + body: { content }, + params: { path: { kind, projectId } } + })).pipe( Effect.map((response) => response.snapshot) ) @@ -31,10 +30,8 @@ export const deleteProjectPrompt = ( projectId: string, kind: ProjectPromptKind ) => - requestJson( - "DELETE", - `/projects/${encodeURIComponent(projectId)}/prompts/${encodeURIComponent(kind)}`, - ProjectPromptsResponseSchema - ).pipe( + openApiJsonSchema(ProjectPromptsResponseSchema, (client) => client.DELETE("/projects/{projectId}/prompts/{kind}", { + params: { path: { kind, projectId } } + })).pipe( Effect.map((response) => response.snapshot) ) diff --git a/packages/app/src/web/api-schema.ts b/packages/app/src/web/api-schema.ts index 322c51c3..3d8df97c 100644 --- a/packages/app/src/web/api-schema.ts +++ b/packages/app/src/web/api-schema.ts @@ -5,6 +5,8 @@ import { NullableString } from "./api-project-schema.js" export { AuthSnapshotResponseSchema, AuthSnapshotSchema, + CodexAuthStatusSchema, + CodexStatusResponseSchema, GithubAuthStatusSchema, GithubStatusResponseSchema, GithubTokenStatusSchema, diff --git a/packages/app/src/web/api-share.ts b/packages/app/src/web/api-share.ts index e6bd4c89..d780bfc9 100644 --- a/packages/app/src/web/api-share.ts +++ b/packages/app/src/web/api-share.ts @@ -1,7 +1,7 @@ import { Effect } from "effect" -import { requestJson } from "./api-http.js" import { PanelCloudflareTunnelResponseSchema } from "./api-schema.js" +import { openApiJsonSchema } from "./openapi-client.js" /** * Reads the controller-owned panel Cloudflare tunnel session. @@ -25,7 +25,7 @@ import { PanelCloudflareTunnelResponseSchema } from "./api-schema.js" // INVARIANT: Only schema-decoded tunnel state crosses the API boundary. // COMPLEXITY: O(1) local work plus network IO. export const loadPanelCloudflareTunnel = () => - requestJson("GET", "/cloudflare-tunnels/panel", PanelCloudflareTunnelResponseSchema).pipe( + openApiJsonSchema(PanelCloudflareTunnelResponseSchema, (client) => client.GET("/cloudflare-tunnels/panel")).pipe( Effect.map((response) => response.tunnel) ) @@ -51,12 +51,9 @@ export const loadPanelCloudflareTunnel = () => // INVARIANT: Returned state is decoded by PanelCloudflareTunnelResponseSchema. // COMPLEXITY: O(1) local work plus network IO and controller-side startup. export const startPanelCloudflareTunnel = (panelUrl: string) => - requestJson( - "POST", - "/cloudflare-tunnels/panel", - PanelCloudflareTunnelResponseSchema, - { panelUrl } - ).pipe( + openApiJsonSchema(PanelCloudflareTunnelResponseSchema, (client) => client.POST("/cloudflare-tunnels/panel", { + body: { panelUrl } + })).pipe( Effect.map((response) => response.tunnel) ) @@ -82,6 +79,6 @@ export const startPanelCloudflareTunnel = (panelUrl: string) => // INVARIANT: Returned state is decoded by PanelCloudflareTunnelResponseSchema. // COMPLEXITY: O(1) local work plus network IO and controller-side cleanup. export const stopPanelCloudflareTunnel = () => - requestJson("DELETE", "/cloudflare-tunnels/panel", PanelCloudflareTunnelResponseSchema).pipe( + openApiJsonSchema(PanelCloudflareTunnelResponseSchema, (client) => client.DELETE("/cloudflare-tunnels/panel")).pipe( Effect.map((response) => response.tunnel) ) diff --git a/packages/app/src/web/api-skills.ts b/packages/app/src/web/api-skills.ts index f76c5e78..e862ba8e 100644 --- a/packages/app/src/web/api-skills.ts +++ b/packages/app/src/web/api-skills.ts @@ -1,8 +1,11 @@ import { Effect } from "effect" -import { requestJson } from "./api-http.js" -import { ProjectSkillsResponseSchema, ProjectSkillUpdateResponseSchema } from "./api-schema.js" +import { + ProjectSkillsResponseSchema, + ProjectSkillUpdateResponseSchema +} from "./api-schema.js" import type { ProjectSkillScope } from "./api-schema.js" +import { openApiJsonSchema } from "./openapi-client.js" const skillScopeIdByScope: Readonly> = { "skills": "skills", @@ -17,11 +20,9 @@ const skillScopeIdByScope: Readonly> = { export const projectSkillScopeToId = (scope: ProjectSkillScope): string => skillScopeIdByScope[scope] export const loadProjectSkills = (projectId: string) => - requestJson( - "GET", - `/projects/${encodeURIComponent(projectId)}/skills`, - ProjectSkillsResponseSchema - ).pipe( + openApiJsonSchema(ProjectSkillsResponseSchema, (client) => client.GET("/projects/{projectId}/skills", { + params: { path: { projectId } } + })).pipe( Effect.map((response) => response.snapshot) ) @@ -31,12 +32,10 @@ export const writeProjectSkill = ( name: string, content: string ) => - requestJson( - "POST", - `/projects/${encodeURIComponent(projectId)}/skills`, - ProjectSkillUpdateResponseSchema, - { scope, name, content } - ).pipe( + openApiJsonSchema(ProjectSkillUpdateResponseSchema, (client) => client.POST("/projects/{projectId}/skills", { + body: { content, name, scope }, + params: { path: { projectId } } + })).pipe( Effect.map((response) => response.snapshot) ) @@ -45,12 +44,9 @@ export const deleteProjectSkill = ( scope: ProjectSkillScope, name: string ) => - requestJson( - "DELETE", - `/projects/${encodeURIComponent(projectId)}/skills/${encodeURIComponent(projectSkillScopeToId(scope))}/${ - encodeURIComponent(name) - }`, - ProjectSkillsResponseSchema - ).pipe( + openApiJsonSchema(ProjectSkillsResponseSchema, (client) => + client.DELETE("/projects/{projectId}/skills/{scopeId}/{name}", { + params: { path: { name, projectId, scopeId: projectSkillScopeToId(scope) } } + })).pipe( Effect.map((response) => response.snapshot) ) diff --git a/packages/app/src/web/api-tasks.ts b/packages/app/src/web/api-tasks.ts index c7fc7357..5228d0c5 100644 --- a/packages/app/src/web/api-tasks.ts +++ b/packages/app/src/web/api-tasks.ts @@ -1,17 +1,18 @@ import { Effect } from "effect" -import { requestJson, requestText } from "./api-http.js" -import { ContainerTaskSnapshotResponseSchema, OutputResponseSchema } from "./api-schema.js" - -const projectTasksPath = (projectId: string, shouldIncludeDefault: boolean): string => - `/projects/${encodeURIComponent(projectId)}/tasks${shouldIncludeDefault ? "?includeDefault=true" : ""}` +import { + ContainerTaskSnapshotResponseSchema, + OutputResponseSchema +} from "./api-schema.js" +import { openApiJsonSchema, openApiVoid } from "./openapi-client.js" export const loadProjectTasks = (projectId: string, shouldIncludeDefault = false) => - requestJson( - "GET", - projectTasksPath(projectId, shouldIncludeDefault), - ContainerTaskSnapshotResponseSchema - ).pipe( + openApiJsonSchema(ContainerTaskSnapshotResponseSchema, (client) => client.GET("/projects/{projectId}/tasks", { + params: { + path: { projectId }, + query: shouldIncludeDefault ? { includeDefault: "true" } : {} + } + })).pipe( Effect.map((response) => response.snapshot) ) @@ -19,20 +20,20 @@ export const stopProjectTask = ( projectId: string, pid: number ) => - requestText( - "POST", - `/projects/${encodeURIComponent(projectId)}/tasks/${pid}/stop` - ).pipe(Effect.asVoid) + openApiVoid((client) => client.POST("/projects/{projectId}/tasks/{pid}/stop", { + params: { path: { pid: String(pid), projectId } } + })) export const loadProjectTaskLogs = ( projectId: string, pid: number, lines = 200 ) => - requestJson( - "GET", - `/projects/${encodeURIComponent(projectId)}/tasks/${pid}/logs?lines=${lines}`, - OutputResponseSchema - ).pipe( + openApiJsonSchema(OutputResponseSchema, (client) => client.GET("/projects/{projectId}/tasks/{pid}/logs", { + params: { + path: { pid: String(pid), projectId }, + query: { lines: String(lines) } + } + })).pipe( Effect.map((response) => response.output) ) diff --git a/packages/app/src/web/api.ts b/packages/app/src/web/api.ts index 73ca2dfc..be61eb0a 100644 --- a/packages/app/src/web/api.ts +++ b/packages/app/src/web/api.ts @@ -7,6 +7,7 @@ import { requestJson, requestText, requestTextStream, resolveApiBaseUrl } from " import { AuthSnapshotResponseSchema, AuthTerminalSessionResponseSchema, + CodexStatusResponseSchema, GithubStatusResponseSchema, HealthResponseSchema, ProjectAuthSnapshotResponseSchema, @@ -30,6 +31,7 @@ import type { ProjectPortForward, ProjectSummary } from "./api-schema.js" +import { openApiJsonSchema, openApiVoid } from "./openapi-client.js" export { startCreateProject } from "./api-create-project.js" export { @@ -105,8 +107,8 @@ export const sortDashboardProjects = ( export const loadDashboard = (): Effect.Effect => Effect.all({ - health: requestJson("GET", "/health", HealthResponseSchema), - projectsResponse: requestJson("GET", "/projects", ProjectsResponseSchema) + health: openApiJsonSchema(HealthResponseSchema, (client) => client.GET("/health")), + projectsResponse: openApiJsonSchema(ProjectsResponseSchema, (client) => client.GET("/projects")) }).pipe( Effect.map(({ health, projectsResponse }) => ({ apiBaseUrl: resolveApiBaseUrl(), @@ -134,16 +136,22 @@ export const openSkiller = (projectKey?: string, sessionId?: string) => ) export const loadProjectPortForwards = (projectId: string) => - requestJson("GET", `/projects/${encodeURIComponent(projectId)}/ports`, ProjectPortForwardsResponseSchema).pipe( + openApiJsonSchema(ProjectPortForwardsResponseSchema, (client) => client.GET("/projects/{projectId}/ports", { + params: { path: { projectId } } + })).pipe( Effect.map((response) => response.forwards) ) export const loadProjectBrowser = (projectId: string) => - requestJson("GET", `/projects/${encodeURIComponent(projectId)}/browser`, ProjectBrowserResponseSchema) + openApiJsonSchema(ProjectBrowserResponseSchema, (client) => client.GET("/projects/{projectId}/browser", { + params: { path: { projectId } } + })) .pipe(Effect.map((response) => response.browser)) export const startProjectBrowser = (projectId: string) => - requestJson("POST", `/projects/${encodeURIComponent(projectId)}/browser/start`, ProjectBrowserResponseSchema) + openApiJsonSchema(ProjectBrowserResponseSchema, (client) => client.POST("/projects/{projectId}/browser/start", { + params: { path: { projectId } } + })) .pipe(Effect.map((response) => response.browser)) export const createProjectPortForward = ( @@ -151,26 +159,25 @@ export const createProjectPortForward = ( targetPort: number, hostPort?: number ) => - requestJson( - "POST", - `/projects/${encodeURIComponent(projectId)}/ports`, - ProjectPortForwardResponseSchema, - hostPort === undefined ? { targetPort } : { hostPort, targetPort } - ).pipe( + openApiJsonSchema(ProjectPortForwardResponseSchema, (client) => client.POST("/projects/{projectId}/ports", { + body: hostPort === undefined ? { targetPort } : { hostPort, targetPort }, + params: { path: { projectId } } + })).pipe( Effect.map((response) => response.forward) ) export const deleteProjectPortForward = ( projectId: string, targetPort: number -) => requestText("DELETE", `/projects/${encodeURIComponent(projectId)}/ports/${targetPort}`).pipe(Effect.asVoid) +) => openApiVoid((client) => client.DELETE("/projects/{projectId}/ports/{targetPort}", { + params: { path: { projectId, targetPort: String(targetPort) } } +})) export const createProjectTerminalSession = (projectKey: string) => - requestJson( - "POST", - `/projects/by-key/${encodeURIComponent(projectKey)}/terminal-sessions`, - TerminalSessionResponseSchema - ).pipe( + openApiJsonSchema(TerminalSessionResponseSchema, (client) => + client.POST("/projects/by-key/{projectKey}/terminal-sessions", { + params: { path: { projectKey } } + })).pipe( Effect.map((response) => ({ project: response.project, session: response.session @@ -181,23 +188,19 @@ export const startProjectTerminalSession = ( projectKey: string, requestId: string ) => - requestJson( - "POST", - `/projects/by-key/${encodeURIComponent(projectKey)}/terminal-sessions/start`, - StartProjectTerminalSessionAcceptedResponseSchema, - { requestId } - ) + openApiJsonSchema(StartProjectTerminalSessionAcceptedResponseSchema, (client) => + client.POST("/projects/by-key/{projectKey}/terminal-sessions/start", { + body: { requestId }, + params: { path: { projectKey } } + })) export const createAuthTerminalSession = ( flow: "ClaudeOauth" | "GeminiOauth" | "GrokOauth", label: string | null ) => - requestJson( - "POST", - "/auth/terminal-sessions", - AuthTerminalSessionResponseSchema, - { flow, label } - ).pipe( + openApiJsonSchema(AuthTerminalSessionResponseSchema, (client) => client.POST("/auth/terminal-sessions", { + body: { flow, label } + })).pipe( Effect.map((response) => response.session) ) @@ -205,40 +208,33 @@ export const deleteProjectTerminalSession = ( projectKey: string, sessionId: string ) => - requestText( - "DELETE", - `/projects/by-key/${encodeURIComponent(projectKey)}/terminal-sessions/${encodeURIComponent(sessionId)}` - ) - .pipe( - Effect.asVoid - ) + openApiVoid((client) => client.DELETE("/projects/by-key/{projectKey}/terminal-sessions/{sessionId}", { + params: { path: { projectKey, sessionId } } + })) export const loadProjectTerminalSessions = (projectKey: string) => - requestJson( - "GET", - `/projects/by-key/${encodeURIComponent(projectKey)}/terminal-sessions`, - ProjectTerminalSessionsResponseSchema - ).pipe( + openApiJsonSchema(ProjectTerminalSessionsResponseSchema, (client) => + client.GET("/projects/by-key/{projectKey}/terminal-sessions", { + params: { path: { projectKey } } + })).pipe( Effect.map((response) => response.sessions) ) export const loadProjectTerminalWorkspace = (projectKey: string) => - requestJson( - "GET", - `/projects/by-key/${encodeURIComponent(projectKey)}/terminal-sessions`, - ProjectTerminalSessionsResponseSchema - ) + openApiJsonSchema(ProjectTerminalSessionsResponseSchema, (client) => + client.GET("/projects/by-key/{projectKey}/terminal-sessions", { + params: { path: { projectKey } } + })) export const setProjectActiveTerminalSession = ( projectKey: string, sessionId: string ) => - requestJson( - "PUT", - `/projects/by-key/${encodeURIComponent(projectKey)}/terminal-sessions/active`, - ProjectTerminalSessionResponseSchema, - { sessionId } - ).pipe( + openApiJsonSchema(ProjectTerminalSessionResponseSchema, (client) => + client.PUT("/projects/by-key/{projectKey}/terminal-sessions/active", { + body: { sessionId }, + params: { path: { projectKey } } + })).pipe( Effect.map((response) => response.session) ) @@ -246,41 +242,46 @@ export const loadProjectTerminalSession = ( projectKey: string, sessionId: string ) => - requestJson( - "GET", - `/projects/by-key/${encodeURIComponent(projectKey)}/terminal-sessions/${encodeURIComponent(sessionId)}`, - ProjectTerminalSessionResponseSchema - ).pipe( + openApiJsonSchema(ProjectTerminalSessionResponseSchema, (client) => + client.GET("/projects/by-key/{projectKey}/terminal-sessions/{sessionId}", { + params: { path: { projectKey, sessionId } } + })).pipe( Effect.map((response) => response.session) ) export const loadTerminalSessionById = (sessionId: string) => - requestJson( - "GET", - `/terminal-sessions/${encodeURIComponent(sessionId)}`, - TerminalSessionLookupResponseSchema - ) + openApiJsonSchema(TerminalSessionLookupResponseSchema, (client) => client.GET("/terminal-sessions/{sessionId}", { + params: { path: { sessionId } } + })) export const deleteTerminalSessionByPath = (path: string) => requestText("DELETE", path).pipe(Effect.asVoid) export const downProject = (projectId: string) => - requestText("POST", `/projects/${encodeURIComponent(projectId)}/down`).pipe(Effect.asVoid) + openApiVoid((client) => client.POST("/projects/{projectId}/down", { + params: { path: { projectId } } + })) export const deleteProject = (projectId: string) => - requestText("DELETE", `/projects/${encodeURIComponent(projectId)}`).pipe(Effect.asVoid) + openApiVoid((client) => client.DELETE("/projects/{projectId}", { + params: { path: { projectId } } + })) -export const downAllProjects = () => requestText("POST", "/projects/down-all").pipe(Effect.asVoid) +export const downAllProjects = () => openApiVoid((client) => client.POST("/projects/down-all")) export const applyAllProjects = (shouldApplyActiveOnly: boolean) => - requestText("POST", "/projects/apply-all", { activeOnly: shouldApplyActiveOnly }).pipe(Effect.asVoid) + openApiVoid((client) => client.POST("/projects/apply-all", { + body: { activeOnly: shouldApplyActiveOnly } + })) export const loadGithubStatus = () => - requestJson("GET", "/auth/github/status", GithubStatusResponseSchema).pipe( + openApiJsonSchema(GithubStatusResponseSchema, (client) => client.GET("/auth/github/status")).pipe( Effect.map((response) => response.status) ) export const loginGithub = (label: string | null) => - requestJson("POST", "/auth/github/login", GithubStatusResponseSchema, { label }).pipe( + openApiJsonSchema(GithubStatusResponseSchema, (client) => client.POST("/auth/github/login", { + body: { label } + })).pipe( Effect.map((response) => response.status) ) @@ -301,7 +302,9 @@ export const loginCodexStream = (label: string | null, onChunk: (chunk: string) }) export const logoutCodex = (label: string | null) => - requestText("POST", "/auth/codex/logout", { label }).pipe(Effect.asVoid) + openApiJsonSchema(CodexStatusResponseSchema, (client) => client.POST("/auth/codex/logout", { + body: { label } + })).pipe(Effect.asVoid) export const loadProjectEvents = ( projectId: string, @@ -316,21 +319,19 @@ export const loadProjectEvents = ( ) export const loadAuthSnapshot = () => - requestJson("GET", "/auth/menu", AuthSnapshotResponseSchema).pipe( + openApiJsonSchema(AuthSnapshotResponseSchema, (client) => client.GET("/auth/menu")).pipe( Effect.map((response) => response.snapshot) ) export const runAuthMenuFlow = (request: AuthMenuRequestBody & { readonly flow: AuthMenuFlow }) => - requestJson("POST", "/auth/menu", AuthSnapshotResponseSchema, request).pipe( + openApiJsonSchema(AuthSnapshotResponseSchema, (client) => client.POST("/auth/menu", { body: request })).pipe( Effect.map((response) => response.snapshot) ) export const loadProjectAuthSnapshot = (projectId: string) => - requestJson( - "GET", - `/projects/${encodeURIComponent(projectId)}/auth/menu`, - ProjectAuthSnapshotResponseSchema - ).pipe( + openApiJsonSchema(ProjectAuthSnapshotResponseSchema, (client) => client.GET("/projects/{projectId}/auth/menu", { + params: { path: { projectId } } + })).pipe( Effect.map((response) => response.snapshot) ) @@ -338,12 +339,10 @@ export const runProjectAuthFlow = ( projectId: string, request: ProjectAuthMenuRequestBody & { readonly flow: ProjectAuthFlow } ) => - requestJson( - "POST", - `/projects/${encodeURIComponent(projectId)}/auth/menu`, - ProjectAuthSnapshotResponseSchema, - request - ).pipe( + openApiJsonSchema(ProjectAuthSnapshotResponseSchema, (client) => client.POST("/projects/{projectId}/auth/menu", { + body: request, + params: { path: { projectId } } + })).pipe( Effect.map((response) => response.snapshot) ) diff --git a/packages/app/src/web/generated/openapi-paths.ts b/packages/app/src/web/generated/openapi-paths.ts new file mode 100644 index 00000000..22181121 --- /dev/null +++ b/packages/app/src/web/generated/openapi-paths.ts @@ -0,0 +1,4293 @@ +/** + * This file was auto-generated by openapi-typescript. + * Do not make direct changes to the file. + */ + +export interface paths { + "/health": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["core.health"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/projects": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["projects.listProjects"]; + put?: never; + post: operations["projects.createProject"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/projects/apply-all": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post: operations["projects.applyAllProjects"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/projects/down-all": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post: operations["projects.downAllProjects"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/projects/{projectId}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["projects.getProject"]; + put?: never; + post?: never; + delete: operations["projects.deleteProject"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/projects/{projectId}/down": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post: operations["projects.downProject"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/projects/{projectId}/apply": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post: operations["projects.applyProject"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/projects/{projectId}/up": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post: operations["projects.upProject"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/projects/{projectId}/resume": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post: operations["projects.resumeProject"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/projects/{projectId}/suspend": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post: operations["projects.suspendProject"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/projects/{projectId}/ps": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["projects.projectPs"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/projects/{projectId}/logs": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["projects.projectLogs"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/projects/{projectId}/ports": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["projectPorts.listProjectPorts"]; + put?: never; + post: operations["projectPorts.createProjectPort"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/projects/{projectId}/ports/{targetPort}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post?: never; + delete: operations["projectPorts.deleteProjectPort"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/projects/{projectId}/browser": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["projectBrowser.readProjectBrowser"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/projects/{projectId}/browser/start": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post: operations["projectBrowser.startProjectBrowser"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/projects/{projectId}/databases/profiles": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["projectDatabases.listDatabaseProfiles"]; + put?: never; + post: operations["projectDatabases.saveDatabaseProfile"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/projects/{projectId}/databases/profiles/{profileId}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post?: never; + delete: operations["projectDatabases.deleteDatabaseProfile"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/projects/{projectId}/databases/profiles/{profileId}/expose": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post: operations["projectDatabases.exposeDatabaseProfile"]; + delete: operations["projectDatabases.deleteDatabaseForward"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/projects/{projectId}/databases/forwards": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["projectDatabases.listDatabaseForwards"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/projects/{projectId}/databases/session": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["projectDatabases.readDatabaseSession"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/projects/{projectId}/databases/open": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post: operations["projectDatabases.openDatabaseEditor"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/projects/{projectId}/databases/restart": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post: operations["projectDatabases.restartDatabaseEditor"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/auth/github/status": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["auth.githubStatus"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/auth/gitlab/status": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["auth.gitlabStatus"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/auth/git/status": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["auth.gitStatus"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/auth/grok/status": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["auth.grokStatus"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/auth/codex/status": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["auth.codexStatus"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/auth/github/login": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post: operations["auth.githubLogin"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/auth/github/logout": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post: operations["auth.githubLogout"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/auth/gitlab/login": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post: operations["auth.gitlabLogin"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/auth/gitlab/logout": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post: operations["auth.gitlabLogout"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/auth/git/login": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post: operations["auth.gitLogin"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/auth/git/logout": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post: operations["auth.gitLogout"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/auth/menu": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["auth.authMenu"]; + put?: never; + post: operations["auth.authMenuAction"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/auth/terminal-sessions": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post: operations["auth.authTerminalSession"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/auth/codex/import": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post: operations["auth.codexImport"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/auth/codex/logout": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post: operations["auth.codexLogout"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/auth/grok/logout": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post: operations["auth.grokLogout"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/projects/{projectId}/auth/menu": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["projectAuth.projectAuth"]; + put?: never; + post: operations["projectAuth.projectAuthAction"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/projects/by-key/{projectKey}/terminal-sessions": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["terminal.listTerminalsByKey"]; + put?: never; + post: operations["terminal.createTerminalByKey"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/projects/by-key/{projectKey}/terminal-sessions/start": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post: operations["terminal.startTerminalByKey"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/projects/by-key/{projectKey}/terminal-sessions/{sessionId}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["terminal.getTerminalByKey"]; + put?: never; + post?: never; + delete: operations["terminal.deleteTerminalByKey"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/projects/by-key/{projectKey}/terminal-sessions/active": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put: operations["terminal.setActiveTerminalByKey"]; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/terminal-sessions/{sessionId}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["terminal.lookupTerminal"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/auth/terminal-sessions/{sessionId}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post?: never; + delete: operations["terminal.deleteAuthTerminal"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/projects/{projectId}/prompts": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["prompts.listPrompts"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/projects/{projectId}/prompts/{kind}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put: operations["prompts.writePrompt"]; + post?: never; + delete: operations["prompts.deletePrompt"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/projects/{projectId}/skills": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["skills.listSkills"]; + put?: never; + post: operations["skills.writeSkill"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/projects/{projectId}/skills/{scopeId}/{name}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post?: never; + delete: operations["skills.deleteSkill"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/projects/{projectId}/tasks": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["tasks.listTasks"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/projects/{projectId}/tasks/{pid}/stop": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post: operations["tasks.stopTask"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/projects/{projectId}/tasks/{pid}/logs": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["tasks.taskLogs"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/cloudflare-tunnels/panel": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["sharing.readPanelCloudflareTunnel"]; + put?: never; + post: operations["sharing.startPanelCloudflareTunnel"]; + delete: operations["sharing.stopPanelCloudflareTunnel"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; +} +export type webhooks = Record; +export interface components { + schemas: { + /** @description The request did not match the expected schema */ + HttpApiDecodeError: { + issues: components["schemas"]["Issue"][]; + message: string; + /** @enum {string} */ + _tag: "HttpApiDecodeError"; + }; + /** @description Represents an error encountered while parsing a value to match the schema */ + Issue: { + /** + * @description The tag identifying the type of parse issue + * @enum {string} + */ + _tag: "Pointer" | "Unexpected" | "Missing" | "Composite" | "Refinement" | "Transformation" | "Type" | "Forbidden"; + /** @description The path to the property where the issue occurred */ + path: components["schemas"]["PropertyKey"][]; + /** @description A descriptive message explaining the issue */ + message: string; + }; + PropertyKey: string | number | { + /** @enum {string} */ + _tag: "symbol"; + key: string; + }; + /** @description a string to be decoded into a number */ + NumberFromString: string; + /** + * Format: uuid + * @description a Universally Unique Identifier + */ + UUID: string; + }; + responses: never; + parameters: never; + requestBodies: never; + headers: never; + pathItems: never; +} +export type $defs = Record; +export interface operations { + "core.health": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + cwd: string; + ok: boolean; + projectsRoot: string; + revision: string | null; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: string; + message: string; + }; + }; + }; + }; + }; + "projects.listProjects": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + projects: { + clonedOnHostname?: string; + containerName?: string; + displayName: string; + id: string; + projectKey: string; + repoRef: string; + repoUrl: string; + sshSessions: number; + startedAtEpochMs: number | null; + startedAtIso: string | null; + /** @enum {string} */ + status: "running" | "stopped" | "unknown"; + statusLabel: string; + }[]; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: string; + message: string; + }; + }; + }; + }; + }; + "projects.createProject": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + repoUrl?: string; + repoRef?: string; + targetDir?: string; + sshPort?: string; + sshUser?: string; + containerName?: string; + serviceName?: string; + volumeName?: string; + secretsRoot?: string; + authorizedKeysPath?: string; + authorizedKeysContents?: string; + useManagedAuthorizedKeys?: boolean; + envGlobalPath?: string; + envProjectPath?: string; + codexAuthPath?: string; + codexHome?: string; + cpuLimit?: string; + ramLimit?: string; + playwrightCpuLimit?: string; + playwrightRamLimit?: string; + /** @enum {string} */ + gpu?: "none" | "all"; + dockerNetworkMode?: string; + dockerSharedNetworkName?: string; + enableMcpPlaywright?: boolean; + outDir?: string; + gitTokenLabel?: string; + skipGithubAuth?: boolean; + codexTokenLabel?: string; + claudeTokenLabel?: string; + geminiTokenLabel?: string; + grokTokenLabel?: string; + agentAutoMode?: string; + up?: boolean; + openSsh?: boolean; + force?: boolean; + forceEnv?: boolean; + waitForClone?: boolean; + async?: boolean; + /** + * maxLength(253) + * @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])?)*$ + */ + clonedOnHostname?: string; + }; + }; + }; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ok?: boolean; + project: { + clonedOnHostname?: string; + containerName: string; + displayName: string; + id: string; + projectKey: string; + repoRef: string; + repoUrl: string; + sshSessions: number; + startedAtEpochMs: number | null; + startedAtIso: string | null; + /** @enum {string} */ + status: "running" | "stopped" | "unknown"; + statusLabel: string; + authorizedKeysExists: boolean; + authorizedKeysPath: string; + codexAuthPath: string; + codexHome: string; + envGlobalPath: string; + envProjectPath: string; + /** @enum {string} */ + gpu: "none" | "all"; + projectDir: string; + serviceName: string; + sshCommand: string; + sshPort: number; + sshUser: string; + targetDir: string; + }; + }; + }; + }; + /** @description Success */ + 202: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @enum {boolean} */ + accepted: true; + cursor: number; + projectId: string; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: string; + message: string; + }; + }; + }; + }; + }; + "projects.applyAllProjects": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + activeOnly?: boolean; + }; + }; + }; + responses: { + /** @description Success */ + 204: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: string; + message: string; + }; + }; + }; + }; + }; + "projects.downAllProjects": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 204: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: string; + message: string; + }; + }; + }; + }; + }; + "projects.getProject": { + parameters: { + query?: never; + header?: never; + path: { + projectId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ok?: boolean; + project: { + clonedOnHostname?: string; + containerName: string; + displayName: string; + id: string; + projectKey: string; + repoRef: string; + repoUrl: string; + sshSessions: number; + startedAtEpochMs: number | null; + startedAtIso: string | null; + /** @enum {string} */ + status: "running" | "stopped" | "unknown"; + statusLabel: string; + authorizedKeysExists: boolean; + authorizedKeysPath: string; + codexAuthPath: string; + codexHome: string; + envGlobalPath: string; + envProjectPath: string; + /** @enum {string} */ + gpu: "none" | "all"; + projectDir: string; + serviceName: string; + sshCommand: string; + sshPort: number; + sshUser: string; + targetDir: string; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: string; + message: string; + }; + }; + }; + }; + }; + "projects.deleteProject": { + parameters: { + query?: never; + header?: never; + path: { + projectId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 204: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: string; + message: string; + }; + }; + }; + }; + }; + "projects.downProject": { + parameters: { + query?: never; + header?: never; + path: { + projectId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 204: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: string; + message: string; + }; + }; + }; + }; + }; + "projects.applyProject": { + parameters: { + query?: never; + header?: never; + path: { + projectId: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + cpuLimit?: string; + ramLimit?: string; + playwrightCpuLimit?: string; + playwrightRamLimit?: string; + /** @enum {string} */ + gpu?: "none" | "all"; + }; + }; + }; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ok?: boolean; + project: { + clonedOnHostname?: string; + containerName: string; + displayName: string; + id: string; + projectKey: string; + repoRef: string; + repoUrl: string; + sshSessions: number; + startedAtEpochMs: number | null; + startedAtIso: string | null; + /** @enum {string} */ + status: "running" | "stopped" | "unknown"; + statusLabel: string; + authorizedKeysExists: boolean; + authorizedKeysPath: string; + codexAuthPath: string; + codexHome: string; + envGlobalPath: string; + envProjectPath: string; + /** @enum {string} */ + gpu: "none" | "all"; + projectDir: string; + serviceName: string; + sshCommand: string; + sshPort: number; + sshUser: string; + targetDir: string; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: string; + message: string; + }; + }; + }; + }; + }; + "projects.upProject": { + parameters: { + query?: never; + header?: never; + path: { + projectId: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + authorizedKeysContents?: string; + useManagedAuthorizedKeys?: boolean; + }; + }; + }; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ok?: boolean; + project: { + clonedOnHostname?: string; + containerName: string; + displayName: string; + id: string; + projectKey: string; + repoRef: string; + repoUrl: string; + sshSessions: number; + startedAtEpochMs: number | null; + startedAtIso: string | null; + /** @enum {string} */ + status: "running" | "stopped" | "unknown"; + statusLabel: string; + authorizedKeysExists: boolean; + authorizedKeysPath: string; + codexAuthPath: string; + codexHome: string; + envGlobalPath: string; + envProjectPath: string; + /** @enum {string} */ + gpu: "none" | "all"; + projectDir: string; + serviceName: string; + sshCommand: string; + sshPort: number; + sshUser: string; + targetDir: string; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: string; + message: string; + }; + }; + }; + }; + }; + "projects.resumeProject": { + parameters: { + query?: never; + header?: never; + path: { + projectId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ok?: boolean; + project: { + clonedOnHostname?: string; + containerName: string; + displayName: string; + id: string; + projectKey: string; + repoRef: string; + repoUrl: string; + sshSessions: number; + startedAtEpochMs: number | null; + startedAtIso: string | null; + /** @enum {string} */ + status: "running" | "stopped" | "unknown"; + statusLabel: string; + authorizedKeysExists: boolean; + authorizedKeysPath: string; + codexAuthPath: string; + codexHome: string; + envGlobalPath: string; + envProjectPath: string; + /** @enum {string} */ + gpu: "none" | "all"; + projectDir: string; + serviceName: string; + sshCommand: string; + sshPort: number; + sshUser: string; + targetDir: string; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: string; + message: string; + }; + }; + }; + }; + }; + "projects.suspendProject": { + parameters: { + query?: never; + header?: never; + path: { + projectId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ok?: boolean; + project: { + clonedOnHostname?: string; + containerName: string; + displayName: string; + id: string; + projectKey: string; + repoRef: string; + repoUrl: string; + sshSessions: number; + startedAtEpochMs: number | null; + startedAtIso: string | null; + /** @enum {string} */ + status: "running" | "stopped" | "unknown"; + statusLabel: string; + authorizedKeysExists: boolean; + authorizedKeysPath: string; + codexAuthPath: string; + codexHome: string; + envGlobalPath: string; + envProjectPath: string; + /** @enum {string} */ + gpu: "none" | "all"; + projectDir: string; + serviceName: string; + sshCommand: string; + sshPort: number; + sshUser: string; + targetDir: string; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: string; + message: string; + }; + }; + }; + }; + }; + "projects.projectPs": { + parameters: { + query?: never; + header?: never; + path: { + projectId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + output: string; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: string; + message: string; + }; + }; + }; + }; + }; + "projects.projectLogs": { + parameters: { + query?: never; + header?: never; + path: { + projectId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + output: string; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: string; + message: string; + }; + }; + }; + }; + }; + "projectPorts.listProjectPorts": { + parameters: { + query?: never; + header?: never; + path: { + projectId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + forwards: { + bindHost: string; + containerName: string; + createdAt: string | null; + hostPort: number; + id: string; + projectId: string; + projectKey: string; + proxyPath: string; + publicHost: string; + /** @enum {string} */ + status: "running" | "stopped" | "unknown"; + targetContainerName: string; + targetPort: number; + url: string; + }[]; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: string; + message: string; + }; + }; + }; + }; + }; + "projectPorts.createProjectPort": { + parameters: { + query?: never; + header?: never; + path: { + projectId: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + hostPort?: number; + targetPort: number; + }; + }; + }; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + forward: { + bindHost: string; + containerName: string; + createdAt: string | null; + hostPort: number; + id: string; + projectId: string; + projectKey: string; + proxyPath: string; + publicHost: string; + /** @enum {string} */ + status: "running" | "stopped" | "unknown"; + targetContainerName: string; + targetPort: number; + url: string; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: string; + message: string; + }; + }; + }; + }; + }; + "projectPorts.deleteProjectPort": { + parameters: { + query?: never; + header?: never; + path: { + projectId: string; + targetPort: components["schemas"]["NumberFromString"]; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 204: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: string; + message: string; + }; + }; + }; + }; + }; + "projectBrowser.readProjectBrowser": { + parameters: { + query?: never; + header?: never; + path: { + projectId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + browser: { + cdpPath: string; + cdpUrl: string; + containerName: string; + noVncPath: string; + noVncUrl: string; + projectId: string; + projectKey: string; + /** @enum {string} */ + status: "running" | "stopped" | "missing" | "unknown"; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: string; + message: string; + }; + }; + }; + }; + }; + "projectBrowser.startProjectBrowser": { + parameters: { + query?: never; + header?: never; + path: { + projectId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + browser: { + cdpPath: string; + cdpUrl: string; + containerName: string; + noVncPath: string; + noVncUrl: string; + projectId: string; + projectKey: string; + /** @enum {string} */ + status: "running" | "stopped" | "missing" | "unknown"; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: string; + message: string; + }; + }; + }; + }; + }; + "projectDatabases.listDatabaseProfiles": { + parameters: { + query?: never; + header?: never; + path: { + projectId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + profiles: { + createdAt: string; + database: string; + /** @enum {string} */ + engine: "postgres" | "mysql" | "mariadb"; + host: string; + id: string; + label: string; + maskedConnectionString: string; + port: number; + updatedAt: string; + user: string; + }[]; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: string; + message: string; + }; + }; + }; + }; + }; + "projectDatabases.saveDatabaseProfile": { + parameters: { + query?: never; + header?: never; + path: { + projectId: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + connectionString: string; + label?: string | null; + }; + }; + }; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + profile: { + createdAt: string; + database: string; + /** @enum {string} */ + engine: "postgres" | "mysql" | "mariadb"; + host: string; + id: string; + label: string; + maskedConnectionString: string; + port: number; + updatedAt: string; + user: string; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: string; + message: string; + }; + }; + }; + }; + }; + "projectDatabases.deleteDatabaseProfile": { + parameters: { + query?: never; + header?: never; + path: { + projectId: string; + profileId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 204: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: string; + message: string; + }; + }; + }; + }; + }; + "projectDatabases.exposeDatabaseProfile": { + parameters: { + query?: never; + header?: never; + path: { + projectId: string; + profileId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + forward: { + bindHost: string; + containerName: string; + createdAt: string | null; + database: string; + /** @enum {string} */ + engine: "postgres" | "mysql" | "mariadb"; + externalConnectionString: string; + hostPort: number; + id: string; + maskedExternalConnectionString: string; + profileId: string; + profileLabel: string; + projectId: string; + projectKey: string; + publicHost: string; + /** @enum {string} */ + status: "running" | "stopped" | "unknown"; + targetHost: string; + targetPort: number; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: string; + message: string; + }; + }; + }; + }; + }; + "projectDatabases.deleteDatabaseForward": { + parameters: { + query?: never; + header?: never; + path: { + projectId: string; + profileId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 204: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: string; + message: string; + }; + }; + }; + }; + }; + "projectDatabases.listDatabaseForwards": { + parameters: { + query?: never; + header?: never; + path: { + projectId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + forwards: { + bindHost: string; + containerName: string; + createdAt: string | null; + database: string; + /** @enum {string} */ + engine: "postgres" | "mysql" | "mariadb"; + externalConnectionString: string; + hostPort: number; + id: string; + maskedExternalConnectionString: string; + profileId: string; + profileLabel: string; + projectId: string; + projectKey: string; + publicHost: string; + /** @enum {string} */ + status: "running" | "stopped" | "unknown"; + targetHost: string; + targetPort: number; + }[]; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: string; + message: string; + }; + }; + }; + }; + }; + "projectDatabases.readDatabaseSession": { + parameters: { + query?: never; + header?: never; + path: { + projectId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + session: { + configHash: string; + containerName: string; + editorPath: string; + editorUrl: string; + projectId: string; + projectKey: string; + /** @enum {string} */ + status: "running" | "stopped" | "missing" | "unknown"; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: string; + message: string; + }; + }; + }; + }; + }; + "projectDatabases.openDatabaseEditor": { + parameters: { + query?: never; + header?: never; + path: { + projectId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + session: { + configHash: string; + containerName: string; + editorPath: string; + editorUrl: string; + projectId: string; + projectKey: string; + /** @enum {string} */ + status: "running" | "stopped" | "missing" | "unknown"; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: string; + message: string; + }; + }; + }; + }; + }; + "projectDatabases.restartDatabaseEditor": { + parameters: { + query?: never; + header?: never; + path: { + projectId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + session: { + configHash: string; + containerName: string; + editorPath: string; + editorUrl: string; + projectId: string; + projectKey: string; + /** @enum {string} */ + status: "running" | "stopped" | "missing" | "unknown"; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: string; + message: string; + }; + }; + }; + }; + }; + "auth.githubStatus": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ok?: boolean; + status: { + summary: string; + tokens: { + key: string; + label: string; + login: string | null; + /** @enum {string} */ + status: "valid" | "invalid" | "unknown"; + }[]; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: string; + message: string; + }; + }; + }; + }; + }; + "auth.gitlabStatus": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ok?: boolean; + status: { + summary: string; + tokens: { + key: string; + label: string; + login: string | null; + /** @enum {string} */ + status: "valid" | "invalid" | "unknown"; + }[]; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: string; + message: string; + }; + }; + }; + }; + }; + "auth.gitStatus": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ok?: boolean; + status: { + connections: { + host: string; + user: string; + }[]; + summary: string; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: string; + message: string; + }; + }; + }; + }; + }; + "auth.grokStatus": { + parameters: { + query?: { + label?: string; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ok?: boolean; + status: { + authPath: string; + connected: boolean; + label: string; + message: string; + /** @enum {string} */ + method: "none" | "api-key" | "oauth"; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: string; + message: string; + }; + }; + }; + }; + }; + "auth.codexStatus": { + parameters: { + query?: { + label?: string; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ok?: boolean; + status: { + account: string | null; + authPath: string; + label: string; + message: string; + present: boolean; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: string; + message: string; + }; + }; + }; + }; + }; + "auth.githubLogin": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + label?: string | null; + token?: string | null; + scopes?: string | null; + }; + }; + }; + responses: { + /** @description Success */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ok?: boolean; + status: { + summary: string; + tokens: { + key: string; + label: string; + login: string | null; + /** @enum {string} */ + status: "valid" | "invalid" | "unknown"; + }[]; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: string; + message: string; + }; + }; + }; + }; + }; + "auth.githubLogout": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + label?: string | null; + }; + }; + }; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ok?: boolean; + status: { + summary: string; + tokens: { + key: string; + label: string; + login: string | null; + /** @enum {string} */ + status: "valid" | "invalid" | "unknown"; + }[]; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: string; + message: string; + }; + }; + }; + }; + }; + "auth.gitlabLogin": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + label?: string | null; + token?: string | null; + }; + }; + }; + responses: { + /** @description Success */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ok?: boolean; + status: { + summary: string; + tokens: { + key: string; + label: string; + login: string | null; + /** @enum {string} */ + status: "valid" | "invalid" | "unknown"; + }[]; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: string; + message: string; + }; + }; + }; + }; + }; + "auth.gitlabLogout": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + label?: string | null; + }; + }; + }; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ok?: boolean; + status: { + summary: string; + tokens: { + key: string; + label: string; + login: string | null; + /** @enum {string} */ + status: "valid" | "invalid" | "unknown"; + }[]; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: string; + message: string; + }; + }; + }; + }; + }; + "auth.gitLogin": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + host: string; + token?: string | null; + user?: string | null; + }; + }; + }; + responses: { + /** @description Success */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ok?: boolean; + status: { + connections: { + host: string; + user: string; + }[]; + summary: string; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: string; + message: string; + }; + }; + }; + }; + }; + "auth.gitLogout": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + host: string; + }; + }; + }; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ok?: boolean; + status: { + connections: { + host: string; + user: string; + }[]; + summary: string; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: string; + message: string; + }; + }; + }; + }; + }; + "auth.authMenu": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ok?: boolean; + snapshot: { + claudeAuthEntries: number; + claudeAuthPath: string; + codexAuthEntries?: number; + codexAuthPath?: string; + geminiAuthEntries: number; + geminiAuthPath: string; + gitTokenEntries: number; + gitUserEntries: number; + githubTokenEntries: number; + globalEnvPath: string; + grokAuthEntries?: number; + grokAuthPath?: string; + totalEntries: number; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: string; + message: string; + }; + }; + }; + }; + }; + "auth.authMenuAction": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + /** @enum {string} */ + flow: "GithubRemove" | "GitSet" | "GitRemove" | "ClaudeLogout" | "GeminiApiKey" | "GeminiLogout" | "GrokApiKey" | "GrokLogout"; + label?: string | null; + token?: string | null; + user?: string | null; + apiKey?: string | null; + }; + }; + }; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ok?: boolean; + snapshot: { + claudeAuthEntries: number; + claudeAuthPath: string; + codexAuthEntries?: number; + codexAuthPath?: string; + geminiAuthEntries: number; + geminiAuthPath: string; + gitTokenEntries: number; + gitUserEntries: number; + githubTokenEntries: number; + globalEnvPath: string; + grokAuthEntries?: number; + grokAuthPath?: string; + totalEntries: number; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: string; + message: string; + }; + }; + }; + }; + }; + "auth.authTerminalSession": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + /** @enum {string} */ + flow: "ClaudeOauth" | "GeminiOauth" | "GrokOauth"; + label?: string | null; + }; + }; + }; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ok?: boolean; + session: { + attachedClients?: number; + closedAt?: string; + createdAt: string; + exitCode?: number; + id: string; + projectId: string; + signal?: number; + sshCommand: string; + startedAt?: string; + /** @enum {string} */ + status: "ready" | "attached" | "exited" | "failed"; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: string; + message: string; + }; + }; + }; + }; + }; + "auth.codexImport": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + label?: string | null; + authText: string; + }; + }; + }; + responses: { + /** @description Success */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ok?: boolean; + status: { + account: string | null; + authPath: string; + label: string; + message: string; + present: boolean; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: string; + message: string; + }; + }; + }; + }; + }; + "auth.codexLogout": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + label?: string | null; + }; + }; + }; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ok?: boolean; + status: { + account: string | null; + authPath: string; + label: string; + message: string; + present: boolean; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: string; + message: string; + }; + }; + }; + }; + }; + "auth.grokLogout": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + label?: string | null; + }; + }; + }; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ok?: boolean; + status: { + authPath: string; + connected: boolean; + label: string; + message: string; + /** @enum {string} */ + method: "none" | "api-key" | "oauth"; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: string; + message: string; + }; + }; + }; + }; + }; + "projectAuth.projectAuth": { + parameters: { + query?: never; + header?: never; + path: { + projectId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ok?: boolean; + snapshot: { + activeClaudeLabel: string | null; + activeGeminiLabel: string | null; + activeGitLabel: string | null; + activeGithubLabel: string | null; + activeGrokLabel: string | null; + claudeAuthEntries: number; + claudeAuthPath: string; + codexAuthEntries?: number; + codexAuthPath?: string; + envGlobalPath: string; + envProjectPath: string; + geminiAuthEntries: number; + geminiAuthPath: string; + gitTokenEntries: number; + githubTokenEntries: number; + grokAuthEntries?: number; + grokAuthPath?: string; + projectDir: string; + projectName: string; + totalEntries: number; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: string; + message: string; + }; + }; + }; + }; + }; + "projectAuth.projectAuthAction": { + parameters: { + query?: never; + header?: never; + path: { + projectId: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + /** @enum {string} */ + flow: "ProjectGithubConnect" | "ProjectGithubDisconnect" | "ProjectGitConnect" | "ProjectGitDisconnect" | "ProjectClaudeConnect" | "ProjectClaudeDisconnect" | "ProjectGeminiConnect" | "ProjectGeminiDisconnect" | "ProjectGrokConnect" | "ProjectGrokDisconnect"; + label?: string | null; + }; + }; + }; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ok?: boolean; + snapshot: { + activeClaudeLabel: string | null; + activeGeminiLabel: string | null; + activeGitLabel: string | null; + activeGithubLabel: string | null; + activeGrokLabel: string | null; + claudeAuthEntries: number; + claudeAuthPath: string; + codexAuthEntries?: number; + codexAuthPath?: string; + envGlobalPath: string; + envProjectPath: string; + geminiAuthEntries: number; + geminiAuthPath: string; + gitTokenEntries: number; + githubTokenEntries: number; + grokAuthEntries?: number; + grokAuthPath?: string; + projectDir: string; + projectName: string; + totalEntries: number; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: string; + message: string; + }; + }; + }; + }; + }; + "terminal.listTerminalsByKey": { + parameters: { + query?: never; + header?: never; + path: { + projectKey: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + activeSessionId: string | null; + sessions: { + attachedClients?: number; + closedAt?: string; + createdAt: string; + exitCode?: number; + id: string; + projectId: string; + signal?: number; + sshCommand: string; + startedAt?: string; + /** @enum {string} */ + status: "ready" | "attached" | "exited" | "failed"; + }[]; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: string; + message: string; + }; + }; + }; + }; + }; + "terminal.createTerminalByKey": { + parameters: { + query?: never; + header?: never; + path: { + projectKey: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ok?: boolean; + project: { + clonedOnHostname?: string; + containerName: string; + displayName: string; + id: string; + projectKey: string; + repoRef: string; + repoUrl: string; + sshSessions: number; + startedAtEpochMs: number | null; + startedAtIso: string | null; + /** @enum {string} */ + status: "running" | "stopped" | "unknown"; + statusLabel: string; + authorizedKeysExists: boolean; + authorizedKeysPath: string; + codexAuthPath: string; + codexHome: string; + envGlobalPath: string; + envProjectPath: string; + /** @enum {string} */ + gpu: "none" | "all"; + projectDir: string; + serviceName: string; + sshCommand: string; + sshPort: number; + sshUser: string; + targetDir: string; + }; + session: { + attachedClients?: number; + closedAt?: string; + createdAt: string; + exitCode?: number; + id: string; + projectId: string; + signal?: number; + sshCommand: string; + startedAt?: string; + /** @enum {string} */ + status: "ready" | "attached" | "exited" | "failed"; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: string; + message: string; + }; + }; + }; + }; + }; + "terminal.startTerminalByKey": { + parameters: { + query?: never; + header?: never; + path: { + projectKey: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + requestId: components["schemas"]["UUID"]; + }; + }; + }; + responses: { + /** @description Success */ + 202: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @enum {boolean} */ + accepted: true; + cursor: number; + projectId: string; + requestId: string; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: string; + message: string; + }; + }; + }; + }; + }; + "terminal.getTerminalByKey": { + parameters: { + query?: never; + header?: never; + path: { + projectKey: string; + sessionId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + session: { + attachedClients?: number; + closedAt?: string; + createdAt: string; + exitCode?: number; + id: string; + projectId: string; + signal?: number; + sshCommand: string; + startedAt?: string; + /** @enum {string} */ + status: "ready" | "attached" | "exited" | "failed"; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: string; + message: string; + }; + }; + }; + }; + }; + "terminal.deleteTerminalByKey": { + parameters: { + query?: never; + header?: never; + path: { + projectKey: string; + sessionId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 204: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: string; + message: string; + }; + }; + }; + }; + }; + "terminal.setActiveTerminalByKey": { + parameters: { + query?: never; + header?: never; + path: { + projectKey: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + sessionId: string; + }; + }; + }; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + session: { + attachedClients?: number; + closedAt?: string; + createdAt: string; + exitCode?: number; + id: string; + projectId: string; + signal?: number; + sshCommand: string; + startedAt?: string; + /** @enum {string} */ + status: "ready" | "attached" | "exited" | "failed"; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: string; + message: string; + }; + }; + }; + }; + }; + "terminal.lookupTerminal": { + parameters: { + query?: never; + header?: never; + path: { + sessionId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + projectDisplayName: string; + projectKey: string; + session: { + attachedClients?: number; + closedAt?: string; + createdAt: string; + exitCode?: number; + id: string; + projectId: string; + signal?: number; + sshCommand: string; + startedAt?: string; + /** @enum {string} */ + status: "ready" | "attached" | "exited" | "failed"; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: string; + message: string; + }; + }; + }; + }; + }; + "terminal.deleteAuthTerminal": { + parameters: { + query?: never; + header?: never; + path: { + sessionId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 204: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: string; + message: string; + }; + }; + }; + }; + }; + "prompts.listPrompts": { + parameters: { + query?: never; + header?: never; + path: { + projectId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ok?: boolean; + snapshot: { + projectDir: string; + projectId: string; + projectKey: string; + prompts: { + absolutePath: string; + bytes: number; + content: string; + exists: boolean; + fileName: string; + /** @enum {string} */ + kind: "claude" | "codex" | "gemini" | "grok"; + relativePath: string; + }[]; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: string; + message: string; + }; + }; + }; + }; + }; + "prompts.writePrompt": { + parameters: { + query?: never; + header?: never; + path: { + projectId: string; + kind: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + content: string; + }; + }; + }; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ok?: boolean; + prompt: { + absolutePath: string; + bytes: number; + content: string; + exists: boolean; + fileName: string; + /** @enum {string} */ + kind: "claude" | "codex" | "gemini" | "grok"; + relativePath: string; + }; + snapshot: { + projectDir: string; + projectId: string; + projectKey: string; + prompts: { + absolutePath: string; + bytes: number; + content: string; + exists: boolean; + fileName: string; + /** @enum {string} */ + kind: "claude" | "codex" | "gemini" | "grok"; + relativePath: string; + }[]; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: string; + message: string; + }; + }; + }; + }; + }; + "prompts.deletePrompt": { + parameters: { + query?: never; + header?: never; + path: { + projectId: string; + kind: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ok?: boolean; + snapshot: { + projectDir: string; + projectId: string; + projectKey: string; + prompts: { + absolutePath: string; + bytes: number; + content: string; + exists: boolean; + fileName: string; + /** @enum {string} */ + kind: "claude" | "codex" | "gemini" | "grok"; + relativePath: string; + }[]; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: string; + message: string; + }; + }; + }; + }; + }; + "skills.listSkills": { + parameters: { + query?: never; + header?: never; + path: { + projectId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ok?: boolean; + snapshot: { + projectDir: string; + projectId: string; + projectKey: string; + scopes: { + absoluteRoot: string; + relativeRoot: string; + /** @enum {string} */ + scope: "skills" | "agents/skills" | "agents/.skills" | "claude/skills" | "codex/skills" | "gemini/skills" | "grok/skills"; + }[]; + skills: { + absolutePath: string; + bytes: number; + content: string; + id: string; + name: string; + relativePath: string; + /** @enum {string} */ + scope: "skills" | "agents/skills" | "agents/.skills" | "claude/skills" | "codex/skills" | "gemini/skills" | "grok/skills"; + updatedAtIso: string | null; + }[]; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: string; + message: string; + }; + }; + }; + }; + }; + "skills.writeSkill": { + parameters: { + query?: never; + header?: never; + path: { + projectId: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + /** @enum {string} */ + scope: "skills" | "agents/skills" | "agents/.skills" | "claude/skills" | "codex/skills" | "gemini/skills" | "grok/skills"; + name: string; + content: string; + }; + }; + }; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ok?: boolean; + skill: { + absolutePath: string; + bytes: number; + content: string; + id: string; + name: string; + relativePath: string; + /** @enum {string} */ + scope: "skills" | "agents/skills" | "agents/.skills" | "claude/skills" | "codex/skills" | "gemini/skills" | "grok/skills"; + updatedAtIso: string | null; + }; + snapshot: { + projectDir: string; + projectId: string; + projectKey: string; + scopes: { + absoluteRoot: string; + relativeRoot: string; + /** @enum {string} */ + scope: "skills" | "agents/skills" | "agents/.skills" | "claude/skills" | "codex/skills" | "gemini/skills" | "grok/skills"; + }[]; + skills: { + absolutePath: string; + bytes: number; + content: string; + id: string; + name: string; + relativePath: string; + /** @enum {string} */ + scope: "skills" | "agents/skills" | "agents/.skills" | "claude/skills" | "codex/skills" | "gemini/skills" | "grok/skills"; + updatedAtIso: string | null; + }[]; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: string; + message: string; + }; + }; + }; + }; + }; + "skills.deleteSkill": { + parameters: { + query?: never; + header?: never; + path: { + projectId: string; + scopeId: string; + name: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ok?: boolean; + snapshot: { + projectDir: string; + projectId: string; + projectKey: string; + scopes: { + absoluteRoot: string; + relativeRoot: string; + /** @enum {string} */ + scope: "skills" | "agents/skills" | "agents/.skills" | "claude/skills" | "codex/skills" | "gemini/skills" | "grok/skills"; + }[]; + skills: { + absolutePath: string; + bytes: number; + content: string; + id: string; + name: string; + relativePath: string; + /** @enum {string} */ + scope: "skills" | "agents/skills" | "agents/.skills" | "claude/skills" | "codex/skills" | "gemini/skills" | "grok/skills"; + updatedAtIso: string | null; + }[]; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: string; + message: string; + }; + }; + }; + }; + }; + "tasks.listTasks": { + parameters: { + query?: { + includeDefault?: string; + }; + header?: never; + path: { + projectId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + snapshot: { + containerName: string; + generatedAt: string; + projectId: string; + sshConnections: number; + tasks: { + command: string; + elapsed?: string; + etime: string; + etimes: number; + /** @enum {string} */ + kind: "ssh" | "web-terminal" | "agent" | "background" | "system"; + logAvailable: boolean; + managedId?: string; + pid: number; + ppid: number; + tty: string; + user: string; + }[]; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: string; + message: string; + }; + }; + }; + }; + }; + "tasks.stopTask": { + parameters: { + query?: never; + header?: never; + path: { + projectId: string; + pid: components["schemas"]["NumberFromString"]; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 204: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: string; + message: string; + }; + }; + }; + }; + }; + "tasks.taskLogs": { + parameters: { + query?: { + lines?: string; + }; + header?: never; + path: { + projectId: string; + pid: components["schemas"]["NumberFromString"]; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + output: string; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: string; + message: string; + }; + }; + }; + }; + }; + "sharing.readPanelCloudflareTunnel": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + tunnel: { + error: string | null; + id: string; + logTail: string[]; + panelUrl: string; + publicUrl: string | null; + startedAt: string; + /** @enum {string} */ + status: "starting" | "running" | "stopped" | "failed"; + stoppedAt: string | null; + } | null; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: string; + message: string; + }; + }; + }; + }; + }; + "sharing.startPanelCloudflareTunnel": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + panelUrl: string; + }; + }; + }; + responses: { + /** @description Success */ + 202: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + tunnel: { + error: string | null; + id: string; + logTail: string[]; + panelUrl: string; + publicUrl: string | null; + startedAt: string; + /** @enum {string} */ + status: "starting" | "running" | "stopped" | "failed"; + stoppedAt: string | null; + } | null; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: string; + message: string; + }; + }; + }; + }; + }; + "sharing.stopPanelCloudflareTunnel": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + tunnel: { + error: string | null; + id: string; + logTail: string[]; + panelUrl: string; + publicUrl: string | null; + startedAt: string; + /** @enum {string} */ + status: "starting" | "running" | "stopped" | "failed"; + stoppedAt: string | null; + } | null; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: string; + message: string; + }; + }; + }; + }; + }; +} diff --git a/packages/app/src/web/openapi-client.ts b/packages/app/src/web/openapi-client.ts new file mode 100644 index 00000000..3e2da1d5 --- /dev/null +++ b/packages/app/src/web/openapi-client.ts @@ -0,0 +1,226 @@ +import * as ParseResult from "@effect/schema/ParseResult" +import * as Schema from "@effect/schema/Schema" +import * as TreeFormatter from "@effect/schema/TreeFormatter" +import createClient, { type Client, type Middleware } from "openapi-fetch" +import { Effect, Either } from "effect" + +import type { paths } from "./generated/openapi-paths.js" +import { resolveApiBaseUrl } from "./api-http.js" + +type DockerGitOpenApiClient = Client + +type OpenApiResponse = { + readonly data?: A + readonly error?: unknown + readonly response: Response +} + +type OpenApiClientMethod = + | DockerGitOpenApiClient["DELETE"] + | DockerGitOpenApiClient["GET"] + | DockerGitOpenApiClient["POST"] + | DockerGitOpenApiClient["PUT"] + +type OpenApiRequestResult = ReturnType + +type OpenApiRequest = (client: DockerGitOpenApiClient) => OpenApiRequestResult + +const noCacheHeaders: Readonly> = { + accept: "application/json", + "cache-control": "no-cache, no-store, max-age=0", + pragma: "no-cache" +} + +const safeJson = (value: unknown): string | null => + Either.match( + Effect.runSync( + Effect.either( + Effect.try({ + try: () => JSON.stringify(value, null, 2), + catch: () => null + }).pipe(Effect.map((json) => json ?? null)) + ) + ), + { + onLeft: (fallback) => fallback, + onRight: (json) => json + } + ) + +const renderUnknown = (value: unknown): string => { + if (typeof value === "string") { + return value + } + if (value instanceof Error) { + return value.message + } + if (typeof value === "object" && value !== null && "message" in value) { + const message = value["message"] + if (typeof message === "string") { + return message + } + } + return safeJson(value) ?? String(value) +} + +const renderOpenApiError = (response: Response, error: unknown): string => { + if (response.status === 429) { + return "HTTP 429: tunnel or proxy rate limited the request. Retry or request a fresh tunnel URL." + } + return error === undefined ? `HTTP ${response.status}` : renderUnknown(error) +} + +const noCacheGetMiddleware: Middleware = { + onRequest: ({ request }) => { + if (request.method !== "GET") { + return undefined + } + const url = new URL(request.url) + url.searchParams.set("_", String(Date.now())) + return new Request(url, request) + } +} + +const makeClient = (baseUrl: string): DockerGitOpenApiClient => { + const client = createClient({ + baseUrl, + headers: noCacheHeaders + }) + client.use(noCacheGetMiddleware) + return client +} + +const clientCache: { + baseUrl: string | null + client: DockerGitOpenApiClient | null +} = { + baseUrl: null, + client: null +} + +const getOpenApiClient = (): DockerGitOpenApiClient => { + const baseUrl = resolveApiBaseUrl() + if (clientCache.client === null || clientCache.baseUrl !== baseUrl) { + clientCache.baseUrl = baseUrl + clientCache.client = makeClient(baseUrl) + } + return clientCache.client +} + +const runOpenApi = ( + request: OpenApiRequest +): Effect.Effect, string> => + Effect.tryPromise({ + try: () => request(getOpenApiClient()), + catch: (cause) => renderUnknown(cause) + }) + +/** + * Executes a typed OpenAPI JSON request through openapi-fetch. + * + * @param request - Deferred typed openapi-fetch request. + * @returns Effect containing raw 2xx response data or a rendered API error. + * + * @pure false - performs browser HTTP IO when the Effect is run. + * @effect Network request via openapi-fetch wrapped by Effect.tryPromise. + * @invariant Promise interop is isolated inside this boundary. + * @precondition request uses a static path from generated OpenAPI paths. + * @postcondition successful Effect contains only the 2xx data branch as unknown. + * @complexity O(n) local response rendering where n is the error payload size. + * @throws Never; failures are returned in the Effect error channel. + */ +// CHANGE: route generated OpenAPI client calls through Effect. +// WHY: frontend REST calls must remain composable in the Effect error channel. +// QUOTE(ТЗ): "использовать openapi-fetch на фронте для удобства" +// REF: user-message-2026-06-18-openapi-fetch +// SOURCE: https://openapi-ts.dev/openapi-fetch/ +// FORMAT THEOREM: response.ok ∧ data defined -> success(unknown data); otherwise -> failure(message). +// PURITY: SHELL +// EFFECT: Effect +// INVARIANT: no Promise escapes this module. +// COMPLEXITY: O(n)/O(n) for error rendering, O(1)/O(1) on successful local processing. +export const openApiJson = ( + request: OpenApiRequest +): Effect.Effect => + runOpenApi(request).pipe( + Effect.flatMap(({ data, error, response }) => { + if (error !== undefined || !response.ok) { + return Effect.fail(renderOpenApiError(response, error)) + } + return data === undefined ? Effect.fail(`HTTP ${response.status}: empty response`) : Effect.succeed(data) + }) + ) + +const decodeSchema = (schema: Schema.Schema, value: unknown): Effect.Effect => + Either.match(ParseResult.decodeUnknownEither(schema)(value), { + onLeft: (error) => Effect.fail(TreeFormatter.formatIssueSync(error)), + onRight: (decoded) => Effect.succeed(decoded) + }) + +/** + * Executes an OpenAPI request and decodes the data with an Effect Schema. + * + * @param schema - Boundary decoder preserving the existing frontend DTO type. + * @param request - Deferred typed openapi-fetch request. + * @returns Effect containing schema-decoded response data. + * + * @pure false - performs browser HTTP IO and boundary decoding when the Effect is run. + * @effect openapi-fetch request plus synchronous Effect Schema decoding. + * @invariant transport typing comes from OpenAPI; exported data typing comes from Schema. + * @precondition schema matches the endpoint success response documented in DockerGitApi. + * @postcondition no generated optional/default representation leaks into existing UI APIs. + * @complexity O(n) where n is the decoded response size. + * @throws Never; failures are returned in the Effect error channel. + */ +// CHANGE: compose generated transport typing with existing Schema DTO boundaries. +// WHY: generated OpenAPI types encode optional/default fields differently than current UI contracts. +// QUOTE(ТЗ): "использовать openapi-fetch на фронте для удобства" +// REF: user-message-2026-06-18-openapi-fetch +// SOURCE: https://openapi-ts.dev/openapi-fetch/ +// FORMAT THEOREM: decode(schema, response.data) = success(a) -> exported(a). +// PURITY: SHELL +// EFFECT: Effect +// INVARIANT: only schema-decoded values leave the API boundary. +// COMPLEXITY: O(n)/O(n) +export const openApiJsonSchema = ( + schema: Schema.Schema, + request: OpenApiRequest +): Effect.Effect => + openApiJson(request).pipe( + Effect.flatMap((data) => decodeSchema(schema, data)) + ) + +/** + * Executes a typed OpenAPI request whose successful response has no body. + * + * @param request - Deferred typed openapi-fetch request. + * @returns Effect that succeeds with void for 2xx/3xx empty responses. + * + * @pure false - performs browser HTTP IO when the Effect is run. + * @effect Network request via openapi-fetch wrapped by Effect.tryPromise. + * @invariant only response status determines success for empty endpoints. + * @precondition request targets an endpoint whose OpenAPI success response has no content. + * @postcondition successful Effect returns void and never exposes transport details. + * @complexity O(n) local response rendering where n is the error payload size. + * @throws Never; failures are returned in the Effect error channel. + */ +// CHANGE: provide an Effect wrapper for generated empty-response endpoints. +// WHY: old requestText(...).asVoid call sites need a typed OpenAPI equivalent. +// QUOTE(ТЗ): "использовать openapi-fetch на фронте для удобства" +// REF: user-message-2026-06-18-openapi-fetch +// SOURCE: https://openapi-ts.dev/openapi-fetch/ +// FORMAT THEOREM: response.ok -> success(void); !response.ok -> failure(message). +// PURITY: SHELL +// EFFECT: Effect +// INVARIANT: no Promise escapes this module. +// COMPLEXITY: O(n)/O(n) for error rendering, O(1)/O(1) on successful local processing. +export const openApiVoid = ( + request: OpenApiRequest +): Effect.Effect => + runOpenApi(request).pipe( + Effect.flatMap(({ error, response }) => + error !== undefined || !response.ok + ? Effect.fail(renderOpenApiError(response, error)) + : Effect.void + ) + ) diff --git a/scripts/write-openapi.ts b/scripts/write-openapi.ts new file mode 100644 index 00000000..40db46dd --- /dev/null +++ b/scripts/write-openapi.ts @@ -0,0 +1,11 @@ +import { dirname, resolve } from "node:path" +import { fileURLToPath } from "node:url" + +import { buildDockerGitOpenApi } from "../packages/api/src/api/openapi.js" + +const __filename = fileURLToPath(import.meta.url) +const __dirname = dirname(__filename) +const repositoryRoot = resolve(__dirname, "..") +const outputPath = resolve(repositoryRoot, "packages/api/openapi.json") + +await Bun.write(outputPath, `${JSON.stringify(buildDockerGitOpenApi(), null, 2)}\n`) From 38cd8eab2391735ee53570ba628eb5621fb6498e Mon Sep 17 00:00:00 2001 From: skulidropek <66840575+skulidropek@users.noreply.github.com> Date: Thu, 18 Jun 2026 10:14:26 +0000 Subject: [PATCH 02/10] fix(web): keep generated OpenAPI client out of lint --- packages/app/biome.json | 3 +- packages/app/eslint.config.mts | 2 +- packages/app/src/web/api-create-project.ts | 7 +- packages/app/src/web/api-database.ts | 91 ++++++--- packages/app/src/web/api-project-core.ts | 104 +++++----- packages/app/src/web/api-prompts.ts | 40 ++-- packages/app/src/web/api-share.ts | 11 +- packages/app/src/web/api-skills.ts | 40 ++-- packages/app/src/web/api-tasks.ts | 47 ++--- packages/app/src/web/api-terminal.ts | 117 +++++++++++ packages/app/src/web/api.ts | 214 ++++++++------------- packages/app/src/web/openapi-client.ts | 80 ++++---- 12 files changed, 429 insertions(+), 327 deletions(-) create mode 100644 packages/app/src/web/api-terminal.ts diff --git a/packages/app/biome.json b/packages/app/biome.json index 27008332..fe8bb3b8 100644 --- a/packages/app/biome.json +++ b/packages/app/biome.json @@ -6,7 +6,8 @@ "useIgnoreFile": false }, "files": { - "ignoreUnknown": false + "ignoreUnknown": false, + "includes": ["**", "!src/web/generated/**"] }, "assist": { "enabled": false diff --git a/packages/app/eslint.config.mts b/packages/app/eslint.config.mts index 0a1fba61..2870aaae 100644 --- a/packages/app/eslint.config.mts +++ b/packages/app/eslint.config.mts @@ -304,5 +304,5 @@ export default defineConfig( }, // 4) Глобальные игноры - { ignores: ['dist/**', 'build/**', 'coverage/**', '**/dist/**'] }, + { ignores: ['dist/**', 'build/**', 'coverage/**', '**/dist/**', 'src/web/generated/**'] }, ); diff --git a/packages/app/src/web/api-create-project.ts b/packages/app/src/web/api-create-project.ts index 6eb44f11..461d9e6e 100644 --- a/packages/app/src/web/api-create-project.ts +++ b/packages/app/src/web/api-create-project.ts @@ -21,6 +21,7 @@ const createProjectAcceptedBody = (draft: CreateProjectDraft) => ({ }) export const startCreateProject = (draft: CreateProjectDraft): Effect.Effect => - openApiJsonSchema(CreateProjectAcceptedResponseSchema, (client) => client.POST("/projects", { - body: createProjectAcceptedBody(draft) - })) + openApiJsonSchema(CreateProjectAcceptedResponseSchema, (client) => + client.POST("/projects", { + body: createProjectAcceptedBody(draft) + })) diff --git a/packages/app/src/web/api-database.ts b/packages/app/src/web/api-database.ts index 38019b33..31b19dee 100644 --- a/packages/app/src/web/api-database.ts +++ b/packages/app/src/web/api-database.ts @@ -16,16 +16,24 @@ export const projectDatabaseExternalUrl = (forward: ProjectDatabaseForward): str `${forward.publicHost}:${forward.hostPort}` export const loadProjectDatabaseProfiles = (projectId: string) => - openApiJsonSchema(ProjectDatabaseProfilesResponseSchema, (client) => client.GET("/projects/{projectId}/databases/profiles", { - params: { path: { projectId } } - })).pipe( + openApiJsonSchema( + ProjectDatabaseProfilesResponseSchema, + (client) => + client.GET("/projects/{projectId}/databases/profiles", { + params: { path: { projectId } } + }) + ).pipe( Effect.map((response) => response.profiles) ) export const loadProjectDatabaseForwards = (projectId: string) => - openApiJsonSchema(ProjectDatabaseForwardsResponseSchema, (client) => client.GET("/projects/{projectId}/databases/forwards", { - params: { path: { projectId } } - })).pipe( + openApiJsonSchema( + ProjectDatabaseForwardsResponseSchema, + (client) => + client.GET("/projects/{projectId}/databases/forwards", { + params: { path: { projectId } } + }) + ).pipe( Effect.map((response) => response.forwards) ) @@ -34,55 +42,80 @@ export const saveProjectDatabaseProfile = ( connectionString: string, label: string | null ) => - openApiJsonSchema(ProjectDatabaseProfileResponseSchema, (client) => client.POST("/projects/{projectId}/databases/profiles", { - body: { connectionString, label }, - params: { path: { projectId } } - })).pipe( + openApiJsonSchema( + ProjectDatabaseProfileResponseSchema, + (client) => + client.POST("/projects/{projectId}/databases/profiles", { + body: { connectionString, label }, + params: { path: { projectId } } + }) + ).pipe( Effect.map((response) => response.profile) ) export const deleteProjectDatabaseProfile = ( projectId: string, profileId: string -) => openApiVoid((client) => client.DELETE("/projects/{projectId}/databases/profiles/{profileId}", { - params: { path: { profileId, projectId } } -})) +) => + openApiVoid((client) => + client.DELETE("/projects/{projectId}/databases/profiles/{profileId}", { + params: { path: { profileId, projectId } } + }) + ) export const exposeProjectDatabaseProfile = ( projectId: string, profileId: string ) => - openApiJsonSchema(ProjectDatabaseForwardResponseSchema, (client) => - client.POST("/projects/{projectId}/databases/profiles/{profileId}/expose", { - params: { path: { profileId, projectId } } - })).pipe( + openApiJsonSchema( + ProjectDatabaseForwardResponseSchema, + (client) => + client.POST("/projects/{projectId}/databases/profiles/{profileId}/expose", { + params: { path: { profileId, projectId } } + }) + ).pipe( Effect.map((response) => response.forward) ) export const deleteProjectDatabaseForward = ( projectId: string, profileId: string -) => openApiVoid((client) => client.DELETE("/projects/{projectId}/databases/profiles/{profileId}/expose", { - params: { path: { profileId, projectId } } -})) +) => + openApiVoid((client) => + client.DELETE("/projects/{projectId}/databases/profiles/{profileId}/expose", { + params: { path: { profileId, projectId } } + }) + ) export const loadProjectDatabaseSession = (projectId: string) => - openApiJsonSchema(ProjectDatabaseSessionResponseSchema, (client) => client.GET("/projects/{projectId}/databases/session", { - params: { path: { projectId } } - })).pipe( + openApiJsonSchema( + ProjectDatabaseSessionResponseSchema, + (client) => + client.GET("/projects/{projectId}/databases/session", { + params: { path: { projectId } } + }) + ).pipe( Effect.map((response) => response.session) ) export const openProjectDatabaseEditor = (projectId: string) => - openApiJsonSchema(ProjectDatabaseSessionResponseSchema, (client) => client.POST("/projects/{projectId}/databases/open", { - params: { path: { projectId } } - })).pipe( + openApiJsonSchema( + ProjectDatabaseSessionResponseSchema, + (client) => + client.POST("/projects/{projectId}/databases/open", { + params: { path: { projectId } } + }) + ).pipe( Effect.map((response) => response.session) ) export const restartProjectDatabaseEditor = (projectId: string) => - openApiJsonSchema(ProjectDatabaseSessionResponseSchema, (client) => client.POST("/projects/{projectId}/databases/restart", { - params: { path: { projectId } } - })).pipe( + openApiJsonSchema( + ProjectDatabaseSessionResponseSchema, + (client) => + client.POST("/projects/{projectId}/databases/restart", { + params: { path: { projectId } } + }) + ).pipe( Effect.map((response) => response.session) ) diff --git a/packages/app/src/web/api-project-core.ts b/packages/app/src/web/api-project-core.ts index 57706770..b39ee944 100644 --- a/packages/app/src/web/api-project-core.ts +++ b/packages/app/src/web/api-project-core.ts @@ -10,15 +10,15 @@ export type { ApplyProjectRequest, ProjectResourceLimitRequest } from "../shared type CreateProjectRequestDraft = CreateProjectDraft & ProjectResourceLimitRequest const optionalProjectResourceFields = (request: ProjectResourceLimitRequest) => ({ - ...(request.playwrightCpuLimit === undefined ? {} : { playwrightCpuLimit: request.playwrightCpuLimit }), - ...(request.playwrightRamLimit === undefined ? {} : { playwrightRamLimit: request.playwrightRamLimit }) + ...(request.playwrightCpuLimit !== undefined && { playwrightCpuLimit: request.playwrightCpuLimit }), + ...(request.playwrightRamLimit !== undefined && { playwrightRamLimit: request.playwrightRamLimit }) }) const applyProjectBody = (request: ApplyProjectRequest | undefined) => ({ - ...(request?.cpuLimit === undefined ? {} : { cpuLimit: request.cpuLimit }), - ...(request?.gpu === undefined ? {} : { gpu: request.gpu }), - ...(request?.ramLimit === undefined ? {} : { ramLimit: request.ramLimit }), - ...(request === undefined ? {} : optionalProjectResourceFields(request)) + ...(request?.cpuLimit !== undefined && { cpuLimit: request.cpuLimit }), + ...(request?.gpu !== undefined && { gpu: request.gpu }), + ...(request?.ramLimit !== undefined && { ramLimit: request.ramLimit }), + ...(request !== undefined && optionalProjectResourceFields(request)) }) const createProjectBody = (draft: CreateProjectRequestDraft) => ({ @@ -38,62 +38,70 @@ const createProjectBody = (draft: CreateProjectRequestDraft) => ({ }) export const loadProjectDetails = (projectId: string) => - openApiJsonSchema(ProjectResponseSchema, (client) => client.GET("/projects/{projectId}", { - params: { path: { projectId } } - })).pipe( - Effect.map((response) => response.project) - ) + openApiJsonSchema(ProjectResponseSchema, (client) => + client.GET("/projects/{projectId}", { + params: { path: { projectId } } + })).pipe( + Effect.map((response) => response.project) + ) export const loadProjectPs = (projectId: string) => - openApiJsonSchema(OutputResponseSchema, (client) => client.GET("/projects/{projectId}/ps", { - params: { path: { projectId } } - })).pipe( - Effect.map((response) => response.output) - ) + openApiJsonSchema(OutputResponseSchema, (client) => + client.GET("/projects/{projectId}/ps", { + params: { path: { projectId } } + })).pipe( + Effect.map((response) => response.output) + ) export const loadProjectLogs = (projectId: string) => - openApiJsonSchema(OutputResponseSchema, (client) => client.GET("/projects/{projectId}/logs", { - params: { path: { projectId } } - })).pipe( - Effect.map((response) => response.output) - ) + openApiJsonSchema(OutputResponseSchema, (client) => + client.GET("/projects/{projectId}/logs", { + params: { path: { projectId } } + })).pipe( + Effect.map((response) => response.output) + ) export const applyProject = ( projectId: string, request?: ApplyProjectRequest ) => - openApiJsonSchema(ProjectResponseSchema, (client) => client.POST("/projects/{projectId}/apply", { - body: applyProjectBody(request), - params: { path: { projectId } } - })).pipe( - Effect.map((response) => response.project) - ) + openApiJsonSchema(ProjectResponseSchema, (client) => + client.POST("/projects/{projectId}/apply", { + body: applyProjectBody(request), + params: { path: { projectId } } + })).pipe( + Effect.map((response) => response.project) + ) export const createProject = (draft: CreateProjectRequestDraft) => - openApiJsonSchema(ProjectResponseSchema, (client) => client.POST("/projects", { - body: createProjectBody(draft) - })).pipe( - Effect.map((response) => response.project) - ) + openApiJsonSchema(ProjectResponseSchema, (client) => + client.POST("/projects", { + body: createProjectBody(draft) + })).pipe( + Effect.map((response) => response.project) + ) export const upProject = (projectId: string) => - openApiJsonSchema(ProjectResponseSchema, (client) => client.POST("/projects/{projectId}/up", { - body: { useManagedAuthorizedKeys: true }, - params: { path: { projectId } } - })).pipe( - Effect.map((response) => response.project) - ) + openApiJsonSchema(ProjectResponseSchema, (client) => + client.POST("/projects/{projectId}/up", { + body: { useManagedAuthorizedKeys: true }, + params: { path: { projectId } } + })).pipe( + Effect.map((response) => response.project) + ) export const resumeProject = (projectId: string) => - openApiJsonSchema(ProjectResponseSchema, (client) => client.POST("/projects/{projectId}/resume", { - params: { path: { projectId } } - })).pipe( - Effect.map((response) => response.project) - ) + openApiJsonSchema(ProjectResponseSchema, (client) => + client.POST("/projects/{projectId}/resume", { + params: { path: { projectId } } + })).pipe( + Effect.map((response) => response.project) + ) export const suspendProject = (projectId: string) => - openApiJsonSchema(ProjectResponseSchema, (client) => client.POST("/projects/{projectId}/suspend", { - params: { path: { projectId } } - })).pipe( - Effect.map((response) => response.project) - ) + openApiJsonSchema(ProjectResponseSchema, (client) => + client.POST("/projects/{projectId}/suspend", { + params: { path: { projectId } } + })).pipe( + Effect.map((response) => response.project) + ) diff --git a/packages/app/src/web/api-prompts.ts b/packages/app/src/web/api-prompts.ts index c3e4a552..d040462f 100644 --- a/packages/app/src/web/api-prompts.ts +++ b/packages/app/src/web/api-prompts.ts @@ -1,37 +1,37 @@ import { Effect } from "effect" -import { - ProjectPromptsResponseSchema, - ProjectPromptUpdateResponseSchema -} from "./api-schema.js" +import { ProjectPromptsResponseSchema, ProjectPromptUpdateResponseSchema } from "./api-schema.js" import type { ProjectPromptKind } from "./api-schema.js" import { openApiJsonSchema } from "./openapi-client.js" export const loadProjectPrompts = (projectId: string) => - openApiJsonSchema(ProjectPromptsResponseSchema, (client) => client.GET("/projects/{projectId}/prompts", { - params: { path: { projectId } } - })).pipe( - Effect.map((response) => response.snapshot) - ) + openApiJsonSchema(ProjectPromptsResponseSchema, (client) => + client.GET("/projects/{projectId}/prompts", { + params: { path: { projectId } } + })).pipe( + Effect.map((response) => response.snapshot) + ) export const writeProjectPrompt = ( projectId: string, kind: ProjectPromptKind, content: string ) => - openApiJsonSchema(ProjectPromptUpdateResponseSchema, (client) => client.PUT("/projects/{projectId}/prompts/{kind}", { - body: { content }, - params: { path: { kind, projectId } } - })).pipe( - Effect.map((response) => response.snapshot) - ) + openApiJsonSchema(ProjectPromptUpdateResponseSchema, (client) => + client.PUT("/projects/{projectId}/prompts/{kind}", { + body: { content }, + params: { path: { kind, projectId } } + })).pipe( + Effect.map((response) => response.snapshot) + ) export const deleteProjectPrompt = ( projectId: string, kind: ProjectPromptKind ) => - openApiJsonSchema(ProjectPromptsResponseSchema, (client) => client.DELETE("/projects/{projectId}/prompts/{kind}", { - params: { path: { kind, projectId } } - })).pipe( - Effect.map((response) => response.snapshot) - ) + openApiJsonSchema(ProjectPromptsResponseSchema, (client) => + client.DELETE("/projects/{projectId}/prompts/{kind}", { + params: { path: { kind, projectId } } + })).pipe( + Effect.map((response) => response.snapshot) + ) diff --git a/packages/app/src/web/api-share.ts b/packages/app/src/web/api-share.ts index d780bfc9..fc1e4149 100644 --- a/packages/app/src/web/api-share.ts +++ b/packages/app/src/web/api-share.ts @@ -51,11 +51,12 @@ export const loadPanelCloudflareTunnel = () => // INVARIANT: Returned state is decoded by PanelCloudflareTunnelResponseSchema. // COMPLEXITY: O(1) local work plus network IO and controller-side startup. export const startPanelCloudflareTunnel = (panelUrl: string) => - openApiJsonSchema(PanelCloudflareTunnelResponseSchema, (client) => client.POST("/cloudflare-tunnels/panel", { - body: { panelUrl } - })).pipe( - Effect.map((response) => response.tunnel) - ) + openApiJsonSchema(PanelCloudflareTunnelResponseSchema, (client) => + client.POST("/cloudflare-tunnels/panel", { + body: { panelUrl } + })).pipe( + Effect.map((response) => response.tunnel) + ) /** * Stops the controller-owned panel Cloudflare tunnel. diff --git a/packages/app/src/web/api-skills.ts b/packages/app/src/web/api-skills.ts index e862ba8e..b0ffb147 100644 --- a/packages/app/src/web/api-skills.ts +++ b/packages/app/src/web/api-skills.ts @@ -1,9 +1,6 @@ import { Effect } from "effect" -import { - ProjectSkillsResponseSchema, - ProjectSkillUpdateResponseSchema -} from "./api-schema.js" +import { ProjectSkillsResponseSchema, ProjectSkillUpdateResponseSchema } from "./api-schema.js" import type { ProjectSkillScope } from "./api-schema.js" import { openApiJsonSchema } from "./openapi-client.js" @@ -20,11 +17,12 @@ const skillScopeIdByScope: Readonly> = { export const projectSkillScopeToId = (scope: ProjectSkillScope): string => skillScopeIdByScope[scope] export const loadProjectSkills = (projectId: string) => - openApiJsonSchema(ProjectSkillsResponseSchema, (client) => client.GET("/projects/{projectId}/skills", { - params: { path: { projectId } } - })).pipe( - Effect.map((response) => response.snapshot) - ) + openApiJsonSchema(ProjectSkillsResponseSchema, (client) => + client.GET("/projects/{projectId}/skills", { + params: { path: { projectId } } + })).pipe( + Effect.map((response) => response.snapshot) + ) export const writeProjectSkill = ( projectId: string, @@ -32,21 +30,25 @@ export const writeProjectSkill = ( name: string, content: string ) => - openApiJsonSchema(ProjectSkillUpdateResponseSchema, (client) => client.POST("/projects/{projectId}/skills", { - body: { content, name, scope }, - params: { path: { projectId } } - })).pipe( - Effect.map((response) => response.snapshot) - ) + openApiJsonSchema(ProjectSkillUpdateResponseSchema, (client) => + client.POST("/projects/{projectId}/skills", { + body: { content, name, scope }, + params: { path: { projectId } } + })).pipe( + Effect.map((response) => response.snapshot) + ) export const deleteProjectSkill = ( projectId: string, scope: ProjectSkillScope, name: string ) => - openApiJsonSchema(ProjectSkillsResponseSchema, (client) => - client.DELETE("/projects/{projectId}/skills/{scopeId}/{name}", { - params: { path: { name, projectId, scopeId: projectSkillScopeToId(scope) } } - })).pipe( + openApiJsonSchema( + ProjectSkillsResponseSchema, + (client) => + client.DELETE("/projects/{projectId}/skills/{scopeId}/{name}", { + params: { path: { name, projectId, scopeId: projectSkillScopeToId(scope) } } + }) + ).pipe( Effect.map((response) => response.snapshot) ) diff --git a/packages/app/src/web/api-tasks.ts b/packages/app/src/web/api-tasks.ts index 5228d0c5..e4938207 100644 --- a/packages/app/src/web/api-tasks.ts +++ b/packages/app/src/web/api-tasks.ts @@ -1,39 +1,40 @@ import { Effect } from "effect" -import { - ContainerTaskSnapshotResponseSchema, - OutputResponseSchema -} from "./api-schema.js" +import { ContainerTaskSnapshotResponseSchema, OutputResponseSchema } from "./api-schema.js" import { openApiJsonSchema, openApiVoid } from "./openapi-client.js" export const loadProjectTasks = (projectId: string, shouldIncludeDefault = false) => - openApiJsonSchema(ContainerTaskSnapshotResponseSchema, (client) => client.GET("/projects/{projectId}/tasks", { - params: { - path: { projectId }, - query: shouldIncludeDefault ? { includeDefault: "true" } : {} - } - })).pipe( - Effect.map((response) => response.snapshot) - ) + openApiJsonSchema(ContainerTaskSnapshotResponseSchema, (client) => + client.GET("/projects/{projectId}/tasks", { + params: { + path: { projectId }, + query: shouldIncludeDefault ? { includeDefault: "true" } : {} + } + })).pipe( + Effect.map((response) => response.snapshot) + ) export const stopProjectTask = ( projectId: string, pid: number ) => - openApiVoid((client) => client.POST("/projects/{projectId}/tasks/{pid}/stop", { - params: { path: { pid: String(pid), projectId } } - })) + openApiVoid((client) => + client.POST("/projects/{projectId}/tasks/{pid}/stop", { + params: { path: { pid: String(pid), projectId } } + }) + ) export const loadProjectTaskLogs = ( projectId: string, pid: number, lines = 200 ) => - openApiJsonSchema(OutputResponseSchema, (client) => client.GET("/projects/{projectId}/tasks/{pid}/logs", { - params: { - path: { pid: String(pid), projectId }, - query: { lines: String(lines) } - } - })).pipe( - Effect.map((response) => response.output) - ) + openApiJsonSchema(OutputResponseSchema, (client) => + client.GET("/projects/{projectId}/tasks/{pid}/logs", { + params: { + path: { pid: String(pid), projectId }, + query: { lines: String(lines) } + } + })).pipe( + Effect.map((response) => response.output) + ) diff --git a/packages/app/src/web/api-terminal.ts b/packages/app/src/web/api-terminal.ts new file mode 100644 index 00000000..0f18cecd --- /dev/null +++ b/packages/app/src/web/api-terminal.ts @@ -0,0 +1,117 @@ +import { Effect } from "effect" + +import { requestText } from "./api-http.js" +import { + AuthTerminalSessionResponseSchema, + ProjectTerminalSessionResponseSchema, + ProjectTerminalSessionsResponseSchema, + StartProjectTerminalSessionAcceptedResponseSchema, + TerminalSessionLookupResponseSchema, + TerminalSessionResponseSchema +} from "./api-schema.js" +import { openApiJsonSchema, openApiVoid } from "./openapi-client.js" + +export const createProjectTerminalSession = (projectKey: string) => + openApiJsonSchema( + TerminalSessionResponseSchema, + (client) => + client.POST("/projects/by-key/{projectKey}/terminal-sessions", { + params: { path: { projectKey } } + }) + ).pipe( + Effect.map((response) => ({ + project: response.project, + session: response.session + })) + ) + +export const startProjectTerminalSession = ( + projectKey: string, + requestId: string +) => + openApiJsonSchema( + StartProjectTerminalSessionAcceptedResponseSchema, + (client) => + client.POST("/projects/by-key/{projectKey}/terminal-sessions/start", { + body: { requestId }, + params: { path: { projectKey } } + }) + ) + +export const createAuthTerminalSession = ( + flow: "ClaudeOauth" | "GeminiOauth" | "GrokOauth", + label: string | null +) => + openApiJsonSchema(AuthTerminalSessionResponseSchema, (client) => + client.POST("/auth/terminal-sessions", { + body: { flow, label } + })).pipe( + Effect.map((response) => response.session) + ) + +export const deleteProjectTerminalSession = ( + projectKey: string, + sessionId: string +) => + openApiVoid((client) => + client.DELETE("/projects/by-key/{projectKey}/terminal-sessions/{sessionId}", { + params: { path: { projectKey, sessionId } } + }) + ) + +export const loadProjectTerminalSessions = (projectKey: string) => + openApiJsonSchema( + ProjectTerminalSessionsResponseSchema, + (client) => + client.GET("/projects/by-key/{projectKey}/terminal-sessions", { + params: { path: { projectKey } } + }) + ).pipe( + Effect.map((response) => response.sessions) + ) + +export const loadProjectTerminalWorkspace = (projectKey: string) => + openApiJsonSchema( + ProjectTerminalSessionsResponseSchema, + (client) => + client.GET("/projects/by-key/{projectKey}/terminal-sessions", { + params: { path: { projectKey } } + }) + ) + +export const setProjectActiveTerminalSession = ( + projectKey: string, + sessionId: string +) => + openApiJsonSchema( + ProjectTerminalSessionResponseSchema, + (client) => + client.PUT("/projects/by-key/{projectKey}/terminal-sessions/active", { + body: { sessionId }, + params: { path: { projectKey } } + }) + ).pipe( + Effect.map((response) => response.session) + ) + +export const loadProjectTerminalSession = ( + projectKey: string, + sessionId: string +) => + openApiJsonSchema( + ProjectTerminalSessionResponseSchema, + (client) => + client.GET("/projects/by-key/{projectKey}/terminal-sessions/{sessionId}", { + params: { path: { projectKey, sessionId } } + }) + ).pipe( + Effect.map((response) => response.session) + ) + +export const loadTerminalSessionById = (sessionId: string) => + openApiJsonSchema(TerminalSessionLookupResponseSchema, (client) => + client.GET("/terminal-sessions/{sessionId}", { + params: { path: { sessionId } } + })) + +export const deleteTerminalSessionByPath = (path: string) => requestText("DELETE", path).pipe(Effect.asVoid) diff --git a/packages/app/src/web/api.ts b/packages/app/src/web/api.ts index be61eb0a..329c5a0e 100644 --- a/packages/app/src/web/api.ts +++ b/packages/app/src/web/api.ts @@ -3,10 +3,9 @@ import { Effect } from "effect" import { sortSelectItemsByLaunchTime } from "../docker-git/menu-select-order.js" import type { SelectProjectRuntime } from "../docker-git/menu-types.js" import type { AuthMenuRequestBody, ProjectAuthMenuRequestBody } from "../shared/auth-menu-request.js" -import { requestJson, requestText, requestTextStream, resolveApiBaseUrl } from "./api-http.js" +import { requestJson, requestTextStream, resolveApiBaseUrl } from "./api-http.js" import { AuthSnapshotResponseSchema, - AuthTerminalSessionResponseSchema, CodexStatusResponseSchema, GithubStatusResponseSchema, HealthResponseSchema, @@ -16,12 +15,7 @@ import { ProjectPortForwardResponseSchema, ProjectPortForwardsResponseSchema, ProjectsResponseSchema, - ProjectTerminalSessionResponseSchema, - ProjectTerminalSessionsResponseSchema, - SkillerLaunchResponseSchema, - StartProjectTerminalSessionAcceptedResponseSchema, - TerminalSessionLookupResponseSchema, - TerminalSessionResponseSchema + SkillerLaunchResponseSchema } from "./api-schema.js" import type { AuthMenuFlow, @@ -62,6 +56,18 @@ export { deleteProjectPrompt, loadProjectPrompts, writeProjectPrompt } from "./a export { loadPanelCloudflareTunnel, startPanelCloudflareTunnel, stopPanelCloudflareTunnel } from "./api-share.js" export { deleteProjectSkill, loadProjectSkills, projectSkillScopeToId, writeProjectSkill } from "./api-skills.js" export { loadProjectTaskLogs, loadProjectTasks, stopProjectTask } from "./api-tasks.js" +export { + createAuthTerminalSession, + createProjectTerminalSession, + deleteProjectTerminalSession, + deleteTerminalSessionByPath, + loadProjectTerminalSession, + loadProjectTerminalSessions, + loadProjectTerminalWorkspace, + loadTerminalSessionById, + setProjectActiveTerminalSession, + startProjectTerminalSession +} from "./api-terminal.js" export type * from "./api-types.js" @@ -136,22 +142,25 @@ export const openSkiller = (projectKey?: string, sessionId?: string) => ) export const loadProjectPortForwards = (projectId: string) => - openApiJsonSchema(ProjectPortForwardsResponseSchema, (client) => client.GET("/projects/{projectId}/ports", { - params: { path: { projectId } } - })).pipe( - Effect.map((response) => response.forwards) - ) + openApiJsonSchema(ProjectPortForwardsResponseSchema, (client) => + client.GET("/projects/{projectId}/ports", { + params: { path: { projectId } } + })).pipe( + Effect.map((response) => response.forwards) + ) export const loadProjectBrowser = (projectId: string) => - openApiJsonSchema(ProjectBrowserResponseSchema, (client) => client.GET("/projects/{projectId}/browser", { - params: { path: { projectId } } - })) + openApiJsonSchema(ProjectBrowserResponseSchema, (client) => + client.GET("/projects/{projectId}/browser", { + params: { path: { projectId } } + })) .pipe(Effect.map((response) => response.browser)) export const startProjectBrowser = (projectId: string) => - openApiJsonSchema(ProjectBrowserResponseSchema, (client) => client.POST("/projects/{projectId}/browser/start", { - params: { path: { projectId } } - })) + openApiJsonSchema(ProjectBrowserResponseSchema, (client) => + client.POST("/projects/{projectId}/browser/start", { + params: { path: { projectId } } + })) .pipe(Effect.map((response) => response.browser)) export const createProjectPortForward = ( @@ -159,119 +168,46 @@ export const createProjectPortForward = ( targetPort: number, hostPort?: number ) => - openApiJsonSchema(ProjectPortForwardResponseSchema, (client) => client.POST("/projects/{projectId}/ports", { - body: hostPort === undefined ? { targetPort } : { hostPort, targetPort }, - params: { path: { projectId } } - })).pipe( - Effect.map((response) => response.forward) - ) + openApiJsonSchema(ProjectPortForwardResponseSchema, (client) => + client.POST("/projects/{projectId}/ports", { + body: hostPort === undefined ? { targetPort } : { hostPort, targetPort }, + params: { path: { projectId } } + })).pipe( + Effect.map((response) => response.forward) + ) export const deleteProjectPortForward = ( projectId: string, targetPort: number -) => openApiVoid((client) => client.DELETE("/projects/{projectId}/ports/{targetPort}", { - params: { path: { projectId, targetPort: String(targetPort) } } -})) - -export const createProjectTerminalSession = (projectKey: string) => - openApiJsonSchema(TerminalSessionResponseSchema, (client) => - client.POST("/projects/by-key/{projectKey}/terminal-sessions", { - params: { path: { projectKey } } - })).pipe( - Effect.map((response) => ({ - project: response.project, - session: response.session - })) - ) - -export const startProjectTerminalSession = ( - projectKey: string, - requestId: string -) => - openApiJsonSchema(StartProjectTerminalSessionAcceptedResponseSchema, (client) => - client.POST("/projects/by-key/{projectKey}/terminal-sessions/start", { - body: { requestId }, - params: { path: { projectKey } } - })) - -export const createAuthTerminalSession = ( - flow: "ClaudeOauth" | "GeminiOauth" | "GrokOauth", - label: string | null -) => - openApiJsonSchema(AuthTerminalSessionResponseSchema, (client) => client.POST("/auth/terminal-sessions", { - body: { flow, label } - })).pipe( - Effect.map((response) => response.session) - ) - -export const deleteProjectTerminalSession = ( - projectKey: string, - sessionId: string -) => - openApiVoid((client) => client.DELETE("/projects/by-key/{projectKey}/terminal-sessions/{sessionId}", { - params: { path: { projectKey, sessionId } } - })) - -export const loadProjectTerminalSessions = (projectKey: string) => - openApiJsonSchema(ProjectTerminalSessionsResponseSchema, (client) => - client.GET("/projects/by-key/{projectKey}/terminal-sessions", { - params: { path: { projectKey } } - })).pipe( - Effect.map((response) => response.sessions) - ) - -export const loadProjectTerminalWorkspace = (projectKey: string) => - openApiJsonSchema(ProjectTerminalSessionsResponseSchema, (client) => - client.GET("/projects/by-key/{projectKey}/terminal-sessions", { - params: { path: { projectKey } } - })) - -export const setProjectActiveTerminalSession = ( - projectKey: string, - sessionId: string ) => - openApiJsonSchema(ProjectTerminalSessionResponseSchema, (client) => - client.PUT("/projects/by-key/{projectKey}/terminal-sessions/active", { - body: { sessionId }, - params: { path: { projectKey } } - })).pipe( - Effect.map((response) => response.session) + openApiVoid((client) => + client.DELETE("/projects/{projectId}/ports/{targetPort}", { + params: { path: { projectId, targetPort: String(targetPort) } } + }) ) -export const loadProjectTerminalSession = ( - projectKey: string, - sessionId: string -) => - openApiJsonSchema(ProjectTerminalSessionResponseSchema, (client) => - client.GET("/projects/by-key/{projectKey}/terminal-sessions/{sessionId}", { - params: { path: { projectKey, sessionId } } - })).pipe( - Effect.map((response) => response.session) - ) - -export const loadTerminalSessionById = (sessionId: string) => - openApiJsonSchema(TerminalSessionLookupResponseSchema, (client) => client.GET("/terminal-sessions/{sessionId}", { - params: { path: { sessionId } } - })) - -export const deleteTerminalSessionByPath = (path: string) => requestText("DELETE", path).pipe(Effect.asVoid) - export const downProject = (projectId: string) => - openApiVoid((client) => client.POST("/projects/{projectId}/down", { - params: { path: { projectId } } - })) + openApiVoid((client) => + client.POST("/projects/{projectId}/down", { + params: { path: { projectId } } + }) + ) export const deleteProject = (projectId: string) => - openApiVoid((client) => client.DELETE("/projects/{projectId}", { - params: { path: { projectId } } - })) + openApiVoid((client) => + client.DELETE("/projects/{projectId}", { + params: { path: { projectId } } + }) + ) export const downAllProjects = () => openApiVoid((client) => client.POST("/projects/down-all")) export const applyAllProjects = (shouldApplyActiveOnly: boolean) => - openApiVoid((client) => client.POST("/projects/apply-all", { - body: { activeOnly: shouldApplyActiveOnly } - })) + openApiVoid((client) => + client.POST("/projects/apply-all", { + body: { activeOnly: shouldApplyActiveOnly } + }) + ) export const loadGithubStatus = () => openApiJsonSchema(GithubStatusResponseSchema, (client) => client.GET("/auth/github/status")).pipe( @@ -279,11 +215,12 @@ export const loadGithubStatus = () => ) export const loginGithub = (label: string | null) => - openApiJsonSchema(GithubStatusResponseSchema, (client) => client.POST("/auth/github/login", { - body: { label } - })).pipe( - Effect.map((response) => response.status) - ) + openApiJsonSchema(GithubStatusResponseSchema, (client) => + client.POST("/auth/github/login", { + body: { label } + })).pipe( + Effect.map((response) => response.status) + ) export const loginGithubStream = (label: string | null, onChunk: (chunk: string) => void) => requestTextStream({ @@ -302,9 +239,10 @@ export const loginCodexStream = (label: string | null, onChunk: (chunk: string) }) export const logoutCodex = (label: string | null) => - openApiJsonSchema(CodexStatusResponseSchema, (client) => client.POST("/auth/codex/logout", { - body: { label } - })).pipe(Effect.asVoid) + openApiJsonSchema(CodexStatusResponseSchema, (client) => + client.POST("/auth/codex/logout", { + body: { label } + })).pipe(Effect.asVoid) export const loadProjectEvents = ( projectId: string, @@ -329,22 +267,24 @@ export const runAuthMenuFlow = (request: AuthMenuRequestBody & { readonly flow: ) export const loadProjectAuthSnapshot = (projectId: string) => - openApiJsonSchema(ProjectAuthSnapshotResponseSchema, (client) => client.GET("/projects/{projectId}/auth/menu", { - params: { path: { projectId } } - })).pipe( - Effect.map((response) => response.snapshot) - ) + openApiJsonSchema(ProjectAuthSnapshotResponseSchema, (client) => + client.GET("/projects/{projectId}/auth/menu", { + params: { path: { projectId } } + })).pipe( + Effect.map((response) => response.snapshot) + ) export const runProjectAuthFlow = ( projectId: string, request: ProjectAuthMenuRequestBody & { readonly flow: ProjectAuthFlow } ) => - openApiJsonSchema(ProjectAuthSnapshotResponseSchema, (client) => client.POST("/projects/{projectId}/auth/menu", { - body: request, - params: { path: { projectId } } - })).pipe( - Effect.map((response) => response.snapshot) - ) + openApiJsonSchema(ProjectAuthSnapshotResponseSchema, (client) => + client.POST("/projects/{projectId}/auth/menu", { + body: request, + params: { path: { projectId } } + })).pipe( + Effect.map((response) => response.snapshot) + ) export { resolveApiBaseUrl } from "./api-http.js" diff --git a/packages/app/src/web/openapi-client.ts b/packages/app/src/web/openapi-client.ts index 3e2da1d5..bf28c38d 100644 --- a/packages/app/src/web/openapi-client.ts +++ b/packages/app/src/web/openapi-client.ts @@ -1,27 +1,30 @@ import * as ParseResult from "@effect/schema/ParseResult" -import * as Schema from "@effect/schema/Schema" +import type * as Schema from "@effect/schema/Schema" import * as TreeFormatter from "@effect/schema/TreeFormatter" -import createClient, { type Client, type Middleware } from "openapi-fetch" import { Effect, Either } from "effect" +import createClient, { type Client, type Middleware } from "openapi-fetch" -import type { paths } from "./generated/openapi-paths.js" import { resolveApiBaseUrl } from "./api-http.js" +import type { paths } from "./generated/openapi-paths.js" type DockerGitOpenApiClient = Client +type ApiTransportValue = + | undefined + | null + | boolean + | number + | string + | ReadonlyArray + | { readonly [key: string]: ApiTransportValue } + type OpenApiResponse = { readonly data?: A - readonly error?: unknown + readonly error?: ApiTransportValue readonly response: Response } -type OpenApiClientMethod = - | DockerGitOpenApiClient["DELETE"] - | DockerGitOpenApiClient["GET"] - | DockerGitOpenApiClient["POST"] - | DockerGitOpenApiClient["PUT"] - -type OpenApiRequestResult = ReturnType +type OpenApiRequestResult = PromiseLike> type OpenApiRequest = (client: DockerGitOpenApiClient) => OpenApiRequestResult @@ -31,49 +34,44 @@ const noCacheHeaders: Readonly> = { pragma: "no-cache" } -const safeJson = (value: unknown): string | null => - Either.match( - Effect.runSync( - Effect.either( - Effect.try({ - try: () => JSON.stringify(value, null, 2), - catch: () => null - }).pipe(Effect.map((json) => json ?? null)) - ) - ), - { - onLeft: (fallback) => fallback, - onRight: (json) => json - } - ) +const stringifyJson = (value: ApiTransportValue): Effect.Effect => + Effect.try({ + try: () => JSON.stringify(value, null, 2), + catch: () => null + }) + +const safeJson = (value: ApiTransportValue): string | null => { + const result = Effect.runSync(Effect.either(stringifyJson(value))) + return Either.match(result, { + onLeft: (fallback) => fallback, + onRight: (json) => json + }) +} -const renderUnknown = (value: unknown): string => { +const renderTransportValue = (value: ApiTransportValue): string => { if (typeof value === "string") { return value } - if (value instanceof Error) { - return value.message - } if (typeof value === "object" && value !== null && "message" in value) { const message = value["message"] if (typeof message === "string") { return message } } - return safeJson(value) ?? String(value) + return safeJson(value) ?? "unrenderable response payload" } -const renderOpenApiError = (response: Response, error: unknown): string => { +const renderOpenApiError = (response: Response, error: ApiTransportValue): string => { if (response.status === 429) { return "HTTP 429: tunnel or proxy rate limited the request. Retry or request a fresh tunnel URL." } - return error === undefined ? `HTTP ${response.status}` : renderUnknown(error) + return error === undefined ? `HTTP ${response.status}` : renderTransportValue(error) } const noCacheGetMiddleware: Middleware = { onRequest: ({ request }) => { if (request.method !== "GET") { - return undefined + return } const url = new URL(request.url) url.searchParams.set("_", String(Date.now())) @@ -109,10 +107,10 @@ const getOpenApiClient = (): DockerGitOpenApiClient => { const runOpenApi = ( request: OpenApiRequest -): Effect.Effect, string> => +): Effect.Effect, string> => Effect.tryPromise({ try: () => request(getOpenApiClient()), - catch: (cause) => renderUnknown(cause) + catch: String }) /** @@ -125,7 +123,7 @@ const runOpenApi = ( * @effect Network request via openapi-fetch wrapped by Effect.tryPromise. * @invariant Promise interop is isolated inside this boundary. * @precondition request uses a static path from generated OpenAPI paths. - * @postcondition successful Effect contains only the 2xx data branch as unknown. + * @postcondition successful Effect contains only the 2xx data branch as a transport value. * @complexity O(n) local response rendering where n is the error payload size. * @throws Never; failures are returned in the Effect error channel. */ @@ -134,14 +132,14 @@ const runOpenApi = ( // QUOTE(ТЗ): "использовать openapi-fetch на фронте для удобства" // REF: user-message-2026-06-18-openapi-fetch // SOURCE: https://openapi-ts.dev/openapi-fetch/ -// FORMAT THEOREM: response.ok ∧ data defined -> success(unknown data); otherwise -> failure(message). +// FORMAT THEOREM: response.ok ∧ data defined -> success(transport data); otherwise -> failure(message). // PURITY: SHELL -// EFFECT: Effect +// EFFECT: Effect // INVARIANT: no Promise escapes this module. // COMPLEXITY: O(n)/O(n) for error rendering, O(1)/O(1) on successful local processing. export const openApiJson = ( request: OpenApiRequest -): Effect.Effect => +): Effect.Effect => runOpenApi(request).pipe( Effect.flatMap(({ data, error, response }) => { if (error !== undefined || !response.ok) { @@ -151,7 +149,7 @@ export const openApiJson = ( }) ) -const decodeSchema = (schema: Schema.Schema, value: unknown): Effect.Effect => +const decodeSchema = (schema: Schema.Schema, value: ApiTransportValue): Effect.Effect => Either.match(ParseResult.decodeUnknownEither(schema)(value), { onLeft: (error) => Effect.fail(TreeFormatter.formatIssueSync(error)), onRight: (decoded) => Effect.succeed(decoded) From 68a1d56eb762cefdf1e67c429c89216cbd7c61cd Mon Sep 17 00:00:00 2001 From: skulidropek <66840575+skulidropek@users.noreply.github.com> Date: Thu, 18 Jun 2026 11:08:14 +0000 Subject: [PATCH 03/10] fix(api): align OpenAPI contract with handlers --- packages/api/openapi.json | 2095 +++++++++++++---- packages/api/src/api/openapi.ts | 23 +- packages/api/tests/openapi.test.ts | 41 + packages/app/src/web/api-auth-schema.ts | 22 + packages/app/src/web/api-create-project.ts | 41 +- packages/app/src/web/api-project-core.ts | 29 +- .../app/src/web/api-project-create-body.ts | 63 + packages/app/src/web/api-terminal.ts | 70 +- packages/app/src/web/api.ts | 1 + .../app/src/web/generated/openapi-paths.ts | 682 ++++-- packages/app/src/web/openapi-client.ts | 76 +- .../docker-git/api-create-project.test.ts | 43 + .../app/tests/docker-git/api-terminal.test.ts | 84 + scripts/write-openapi.ts | 14 +- 14 files changed, 2660 insertions(+), 624 deletions(-) create mode 100644 packages/app/src/web/api-project-create-body.ts create mode 100644 packages/app/tests/docker-git/api-create-project.test.ts create mode 100644 packages/app/tests/docker-git/api-terminal.test.ts diff --git a/packages/api/openapi.json b/packages/api/openapi.json index f48847b1..02f3c51a 100644 --- a/packages/api/openapi.json +++ b/packages/api/openapi.json @@ -65,15 +65,34 @@ { "type": "object", "required": [ - "error", - "message" + "error" ], "properties": { "error": { - "type": "string" - }, - "message": { - "type": "string" + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -199,15 +218,34 @@ { "type": "object", "required": [ - "error", - "message" + "error" ], "properties": { "error": { - "type": "string" - }, - "message": { - "type": "string" + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -227,7 +265,7 @@ "parameters": [], "security": [], "responses": { - "200": { + "201": { "description": "Success", "content": { "application/json": { @@ -418,15 +456,34 @@ { "type": "object", "required": [ - "error", - "message" + "error" ], "properties": { "error": { - "type": "string" - }, - "message": { - "type": "string" + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -603,15 +660,34 @@ { "type": "object", "required": [ - "error", - "message" + "error" ], "properties": { "error": { - "type": "string" - }, - "message": { - "type": "string" + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -665,15 +741,34 @@ { "type": "object", "required": [ - "error", - "message" + "error" ], "properties": { "error": { - "type": "string" - }, - "message": { - "type": "string" + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -865,15 +960,34 @@ { "type": "object", "required": [ - "error", - "message" + "error" ], "properties": { "error": { - "type": "string" - }, - "message": { - "type": "string" + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -917,15 +1031,34 @@ { "type": "object", "required": [ - "error", - "message" + "error" ], "properties": { "error": { - "type": "string" - }, - "message": { - "type": "string" + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -971,15 +1104,34 @@ { "type": "object", "required": [ - "error", - "message" + "error" ], "properties": { "error": { - "type": "string" - }, - "message": { - "type": "string" + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -1171,15 +1323,34 @@ { "type": "object", "required": [ - "error", - "message" + "error" ], "properties": { "error": { - "type": "string" - }, - "message": { - "type": "string" + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -1404,15 +1575,34 @@ { "type": "object", "required": [ - "error", - "message" + "error" ], "properties": { "error": { - "type": "string" - }, - "message": { - "type": "string" + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -1624,15 +1814,34 @@ { "type": "object", "required": [ - "error", - "message" + "error" ], "properties": { "error": { - "type": "string" - }, - "message": { - "type": "string" + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -1824,15 +2033,34 @@ { "type": "object", "required": [ - "error", - "message" + "error" ], "properties": { "error": { - "type": "string" - }, - "message": { - "type": "string" + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -1894,15 +2122,34 @@ { "type": "object", "required": [ - "error", - "message" + "error" ], "properties": { "error": { - "type": "string" - }, - "message": { - "type": "string" + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -1964,18 +2211,37 @@ { "type": "object", "required": [ - "error", - "message" + "error" ], "properties": { "error": { - "type": "string" - }, - "message": { - "type": "string" - } - }, - "additionalProperties": false + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false } ] } @@ -2106,15 +2372,34 @@ { "type": "object", "required": [ - "error", - "message" + "error" ], "properties": { "error": { - "type": "string" - }, - "message": { - "type": "string" + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -2143,7 +2428,7 @@ ], "security": [], "responses": { - "200": { + "201": { "description": "Success", "content": { "application/json": { @@ -2243,15 +2528,34 @@ { "type": "object", "required": [ - "error", - "message" + "error" ], "properties": { "error": { - "type": "string" - }, - "message": { - "type": "string" + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -2327,15 +2631,34 @@ { "type": "object", "required": [ - "error", - "message" + "error" ], "properties": { "error": { - "type": "string" - }, - "message": { - "type": "string" + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -2440,15 +2763,34 @@ { "type": "object", "required": [ - "error", - "message" + "error" ], "properties": { "error": { - "type": "string" - }, - "message": { - "type": "string" + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -2553,15 +2895,34 @@ { "type": "object", "required": [ - "error", - "message" + "error" ], "properties": { "error": { - "type": "string" - }, - "message": { - "type": "string" + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -2676,15 +3037,34 @@ { "type": "object", "required": [ - "error", - "message" + "error" ], "properties": { "error": { - "type": "string" - }, - "message": { - "type": "string" + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -2713,7 +3093,7 @@ ], "security": [], "responses": { - "200": { + "201": { "description": "Success", "content": { "application/json": { @@ -2794,15 +3174,34 @@ { "type": "object", "required": [ - "error", - "message" + "error" ], "properties": { "error": { - "type": "string" - }, - "message": { - "type": "string" + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -2885,15 +3284,34 @@ { "type": "object", "required": [ - "error", - "message" + "error" ], "properties": { "error": { - "type": "string" - }, - "message": { - "type": "string" + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -2932,7 +3350,7 @@ ], "security": [], "responses": { - "200": { + "201": { "description": "Success", "content": { "application/json": { @@ -3053,15 +3471,34 @@ { "type": "object", "required": [ - "error", - "message" + "error" ], "properties": { "error": { - "type": "string" - }, - "message": { - "type": "string" + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -3113,15 +3550,34 @@ { "type": "object", "required": [ - "error", - "message" + "error" ], "properties": { "error": { - "type": "string" - }, - "message": { - "type": "string" + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -3276,15 +3732,34 @@ { "type": "object", "required": [ - "error", - "message" + "error" ], "properties": { "error": { - "type": "string" - }, - "message": { - "type": "string" + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -3385,15 +3860,34 @@ { "type": "object", "required": [ - "error", - "message" + "error" ], "properties": { "error": { - "type": "string" - }, - "message": { - "type": "string" + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -3494,15 +3988,34 @@ { "type": "object", "required": [ - "error", - "message" + "error" ], "properties": { "error": { - "type": "string" - }, - "message": { - "type": "string" + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -3603,15 +4116,34 @@ { "type": "object", "required": [ - "error", - "message" + "error" ], "properties": { "error": { - "type": "string" - }, - "message": { - "type": "string" + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -3716,15 +4248,34 @@ { "type": "object", "required": [ - "error", - "message" + "error" ], "properties": { "error": { - "type": "string" - }, - "message": { - "type": "string" + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -3829,15 +4380,34 @@ { "type": "object", "required": [ - "error", - "message" + "error" ], "properties": { "error": { - "type": "string" - }, - "message": { - "type": "string" + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -3922,15 +4492,34 @@ { "type": "object", "required": [ - "error", - "message" + "error" ], "properties": { "error": { - "type": "string" - }, - "message": { - "type": "string" + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -4025,15 +4614,34 @@ { "type": "object", "required": [ - "error", - "message" + "error" ], "properties": { "error": { - "type": "string" - }, - "message": { - "type": "string" + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -4130,15 +4738,34 @@ { "type": "object", "required": [ - "error", - "message" + "error" ], "properties": { "error": { - "type": "string" - }, - "message": { - "type": "string" + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -4243,15 +4870,34 @@ { "type": "object", "required": [ - "error", - "message" + "error" ], "properties": { "error": { - "type": "string" - }, - "message": { - "type": "string" + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -4400,15 +5046,34 @@ { "type": "object", "required": [ - "error", - "message" + "error" ], "properties": { "error": { - "type": "string" - }, - "message": { - "type": "string" + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -4537,15 +5202,34 @@ { "type": "object", "required": [ - "error", - "message" + "error" ], "properties": { "error": { - "type": "string" - }, - "message": { - "type": "string" + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -4684,15 +5368,34 @@ { "type": "object", "required": [ - "error", - "message" + "error" ], "properties": { "error": { - "type": "string" - }, - "message": { - "type": "string" + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -4801,15 +5504,34 @@ { "type": "object", "required": [ - "error", - "message" + "error" ], "properties": { "error": { - "type": "string" - }, - "message": { - "type": "string" + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -4933,15 +5655,34 @@ { "type": "object", "required": [ - "error", - "message" + "error" ], "properties": { "error": { - "type": "string" - }, - "message": { - "type": "string" + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -5069,15 +5810,34 @@ { "type": "object", "required": [ - "error", - "message" + "error" ], "properties": { "error": { - "type": "string" - }, - "message": { - "type": "string" + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -5184,15 +5944,34 @@ { "type": "object", "required": [ - "error", - "message" + "error" ], "properties": { "error": { - "type": "string" - }, - "message": { - "type": "string" + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -5283,7 +6062,7 @@ "parameters": [], "security": [], "responses": { - "200": { + "201": { "description": "Success", "content": { "application/json": { @@ -5363,15 +6142,34 @@ { "type": "object", "required": [ - "error", - "message" + "error" ], "properties": { "error": { - "type": "string" - }, - "message": { - "type": "string" + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -5493,15 +6291,34 @@ { "type": "object", "required": [ - "error", - "message" + "error" ], "properties": { "error": { - "type": "string" - }, - "message": { - "type": "string" + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -5618,15 +6435,34 @@ { "type": "object", "required": [ - "error", - "message" + "error" ], "properties": { "error": { - "type": "string" - }, - "message": { - "type": "string" + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -5736,15 +6572,34 @@ { "type": "object", "required": [ - "error", - "message" + "error" ], "properties": { "error": { - "type": "string" - }, - "message": { - "type": "string" + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -5949,15 +6804,34 @@ { "type": "object", "required": [ - "error", - "message" + "error" ], "properties": { "error": { - "type": "string" - }, - "message": { - "type": "string" + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -6136,15 +7010,34 @@ { "type": "object", "required": [ - "error", - "message" + "error" ], "properties": { "error": { - "type": "string" - }, - "message": { - "type": "string" + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -6216,7 +7109,7 @@ ], "security": [], "responses": { - "200": { + "201": { "description": "Success", "content": { "application/json": { @@ -6427,15 +7320,34 @@ { "type": "object", "required": [ - "error", - "message" + "error" ], "properties": { "error": { - "type": "string" - }, - "message": { - "type": "string" + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -6555,15 +7467,34 @@ { "type": "object", "required": [ - "error", - "message" + "error" ], "properties": { "error": { - "type": "string" - }, - "message": { - "type": "string" + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -6640,15 +7571,34 @@ { "type": "object", "required": [ - "error", - "message" + "error" ], "properties": { "error": { - "type": "string" - }, - "message": { - "type": "string" + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -6783,15 +7733,34 @@ { "type": "object", "required": [ - "error", - "message" + "error" ], "properties": { "error": { - "type": "string" - }, - "message": { - "type": "string" + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -6843,15 +7812,34 @@ { "type": "object", "required": [ - "error", - "message" + "error" ], "properties": { "error": { - "type": "string" - }, - "message": { - "type": "string" + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -6959,15 +7947,34 @@ { "type": "object", "required": [ - "error", - "message" + "error" ], "properties": { "error": { - "type": "string" - }, - "message": { - "type": "string" + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -7102,15 +8109,34 @@ { "type": "object", "required": [ - "error", - "message" + "error" ], "properties": { "error": { - "type": "string" - }, - "message": { - "type": "string" + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -7156,15 +8182,34 @@ { "type": "object", "required": [ - "error", - "message" + "error" ], "properties": { "error": { - "type": "string" - }, - "message": { - "type": "string" + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -7292,15 +8337,34 @@ { "type": "object", "required": [ - "error", - "message" + "error" ], "properties": { "error": { - "type": "string" - }, - "message": { - "type": "string" + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -7479,15 +8543,34 @@ { "type": "object", "required": [ - "error", - "message" + "error" ], "properties": { "error": { - "type": "string" - }, - "message": { - "type": "string" + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -7640,15 +8723,34 @@ { "type": "object", "required": [ - "error", - "message" + "error" ], "properties": { "error": { - "type": "string" - }, - "message": { - "type": "string" + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -7823,15 +8925,34 @@ { "type": "object", "required": [ - "error", - "message" + "error" ], "properties": { "error": { - "type": "string" - }, - "message": { - "type": "string" + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -8061,15 +9182,34 @@ { "type": "object", "required": [ - "error", - "message" + "error" ], "properties": { "error": { - "type": "string" - }, - "message": { - "type": "string" + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -8296,15 +9436,34 @@ { "type": "object", "required": [ - "error", - "message" + "error" ], "properties": { "error": { - "type": "string" - }, - "message": { - "type": "string" + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -8456,15 +9615,34 @@ { "type": "object", "required": [ - "error", - "message" + "error" ], "properties": { "error": { - "type": "string" - }, - "message": { - "type": "string" + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -8518,15 +9696,34 @@ { "type": "object", "required": [ - "error", - "message" + "error" ], "properties": { "error": { - "type": "string" - }, - "message": { - "type": "string" + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -8604,15 +9801,34 @@ { "type": "object", "required": [ - "error", - "message" + "error" ], "properties": { "error": { - "type": "string" - }, - "message": { - "type": "string" + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -8739,15 +9955,34 @@ { "type": "object", "required": [ - "error", - "message" + "error" ], "properties": { "error": { - "type": "string" - }, - "message": { - "type": "string" + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -8872,15 +10107,34 @@ { "type": "object", "required": [ - "error", - "message" + "error" ], "properties": { "error": { - "type": "string" - }, - "message": { - "type": "string" + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -9024,15 +10278,34 @@ { "type": "object", "required": [ - "error", - "message" + "error" ], "properties": { "error": { - "type": "string" - }, - "message": { - "type": "string" + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false diff --git a/packages/api/src/api/openapi.ts b/packages/api/src/api/openapi.ts index 17d58598..d078665a 100644 --- a/packages/api/src/api/openapi.ts +++ b/packages/api/src/api/openapi.ts @@ -465,9 +465,16 @@ const QueryLinesSchema = Schema.Struct({ lines: Schema.optional(Schema.String) }) +const ApiErrorEnvelopeSchema = Schema.Struct({ + command: Schema.optional(Schema.String), + details: Schema.optional(Schema.Unknown), + message: Schema.String, + provider: Schema.optional(Schema.String), + type: Schema.String +}) + const ApiErrorResponseSchema = Schema.Struct({ - error: Schema.String, - message: Schema.String + error: ApiErrorEnvelopeSchema }) const endpoint = { @@ -486,7 +493,7 @@ const ProjectsGroup = HttpApiGroup.make("projects") .add( endpoint.post("createProject", "/projects") .setPayload(CreateProjectRequestSchema) - .addSuccess(ProjectResponseSchema) + .addSuccess(ProjectResponseSchema, { status: 201 }) .addSuccess(CreateProjectAcceptedResponseSchema, { status: 202 }) ) .add( @@ -518,7 +525,7 @@ const ProjectPortsGroup = HttpApiGroup.make("projectPorts") .add( endpoint.post("createProjectPort")`/projects/${ProjectIdParam}/ports` .setPayload(ProjectPortForwardRequestSchema) - .addSuccess(ProjectPortForwardResponseSchema) + .addSuccess(ProjectPortForwardResponseSchema, { status: 201 }) ) .add( endpoint.del("deleteProjectPort")`/projects/${ProjectIdParam}/ports/${TargetPortParam}` @@ -540,7 +547,7 @@ const ProjectDatabasesGroup = HttpApiGroup.make("projectDatabases") .add( endpoint.post("saveDatabaseProfile")`/projects/${ProjectIdParam}/databases/profiles` .setPayload(ProjectDatabaseProfileRequestSchema) - .addSuccess(ProjectDatabaseProfileResponseSchema) + .addSuccess(ProjectDatabaseProfileResponseSchema, { status: 201 }) ) .add( endpoint.del("deleteDatabaseProfile")`/projects/${ProjectIdParam}/databases/profiles/${ProfileIdParam}` @@ -548,7 +555,7 @@ const ProjectDatabasesGroup = HttpApiGroup.make("projectDatabases") ) .add( endpoint.post("exposeDatabaseProfile")`/projects/${ProjectIdParam}/databases/profiles/${ProfileIdParam}/expose` - .addSuccess(ProjectDatabaseForwardResponseSchema) + .addSuccess(ProjectDatabaseForwardResponseSchema, { status: 201 }) ) .add( endpoint.del("deleteDatabaseForward")`/projects/${ProjectIdParam}/databases/profiles/${ProfileIdParam}/expose` @@ -624,7 +631,7 @@ const AuthGroup = HttpApiGroup.make("auth") .add( endpoint.post("authTerminalSession", "/auth/terminal-sessions") .setPayload(AuthTerminalSessionRequestSchema) - .addSuccess(AuthTerminalSessionResponseSchema) + .addSuccess(AuthTerminalSessionResponseSchema, { status: 201 }) ) .add( endpoint.post("codexImport", "/auth/codex/import") @@ -653,7 +660,7 @@ const ProjectAuthGroup = HttpApiGroup.make("projectAuth") const TerminalGroup = HttpApiGroup.make("terminal") .add( endpoint.post("createTerminalByKey")`/projects/by-key/${ProjectKeyParam}/terminal-sessions` - .addSuccess(TerminalSessionResponseSchema) + .addSuccess(TerminalSessionResponseSchema, { status: 201 }) ) .add( endpoint.post("startTerminalByKey")`/projects/by-key/${ProjectKeyParam}/terminal-sessions/start` diff --git a/packages/api/tests/openapi.test.ts b/packages/api/tests/openapi.test.ts index 2c49bf6e..e7e73f71 100644 --- a/packages/api/tests/openapi.test.ts +++ b/packages/api/tests/openapi.test.ts @@ -22,4 +22,45 @@ describe("openapi contract", () => { expect(paths["/projects/{projectId}/auth"]).toBeUndefined() expect(Object.keys(paths)).toHaveLength(54) })) + + it.effect("documents real HTTP success status codes for create and async endpoints", () => + Effect.sync(() => { + const spec = buildDockerGitOpenApi() + const paths = spec.paths ?? {} + + const postResponseStatuses = (path: string): ReadonlyArray => + Object.keys(paths[path]?.post?.responses ?? {}) + + expect(postResponseStatuses("/projects")).toEqual(expect.arrayContaining(["201", "202", "400"])) + expect(postResponseStatuses("/projects/{projectId}/ports")).toEqual(expect.arrayContaining(["201", "400"])) + expect(postResponseStatuses("/projects/{projectId}/databases/profiles")).toEqual( + expect.arrayContaining(["201", "400"]) + ) + expect(postResponseStatuses("/projects/{projectId}/databases/profiles/{profileId}/expose")).toEqual( + expect.arrayContaining(["201", "400"]) + ) + expect(postResponseStatuses("/auth/terminal-sessions")).toEqual(expect.arrayContaining(["201", "400"])) + expect(postResponseStatuses("/projects/by-key/{projectKey}/terminal-sessions")).toEqual( + expect.arrayContaining(["201", "400"]) + ) + expect(postResponseStatuses("/projects/by-key/{projectKey}/terminal-sessions/start")).toEqual( + expect.arrayContaining(["202", "400"]) + ) + })) + + it.effect("documents the nested API error envelope used by HTTP handlers", () => + Effect.sync(() => { + const spec = buildDockerGitOpenApi() + const serializedBadRequestSchema = JSON.stringify( + spec.paths?.["/projects"]?.post?.responses?.["400"] ?? {} + ) + + expect(serializedBadRequestSchema).toContain("\"required\":[\"error\"]") + expect(serializedBadRequestSchema).toContain("\"error\":{\"type\":\"object\"") + expect(serializedBadRequestSchema).toContain("\"type\":{\"type\":\"string\"") + expect(serializedBadRequestSchema).toContain("\"message\":{\"type\":\"string\"") + expect(serializedBadRequestSchema).toContain("\"provider\":{\"type\":\"string\"") + expect(serializedBadRequestSchema).toContain("\"command\":{\"type\":\"string\"") + expect(serializedBadRequestSchema).not.toContain("\"required\":[\"error\",\"message\"]") + })) }) diff --git a/packages/app/src/web/api-auth-schema.ts b/packages/app/src/web/api-auth-schema.ts index a51b8a32..9d02807c 100644 --- a/packages/app/src/web/api-auth-schema.ts +++ b/packages/app/src/web/api-auth-schema.ts @@ -23,6 +23,17 @@ export const GithubStatusResponseSchema = Schema.Struct({ status: GithubAuthStatusSchema }) +/** + * Boundary schema for the controller Codex authentication status. + * + * @pure true - describes immutable response data only. + * @effect none + * @invariant account may be null, while authPath, label, message, and present are always typed. + * @precondition input is an unknown JSON value received from the API boundary. + * @postcondition successful decoding yields the UI-safe Codex auth status shape. + * @complexity O(1) schema construction; O(n) decode where n is the response size. + * @throws Never. + */ export const CodexAuthStatusSchema = Schema.Struct({ account: NullableString, authPath: Schema.String, @@ -31,6 +42,17 @@ export const CodexAuthStatusSchema = Schema.Struct({ present: Schema.Boolean }) +/** + * Boundary schema for the Codex status API response envelope. + * + * @pure true - describes immutable response data only. + * @effect none + * @invariant status is always present; ok is an optional compatibility flag. + * @precondition input is an unknown JSON value received from the API boundary. + * @postcondition successful decoding yields a response with a validated CodexAuthStatusSchema status. + * @complexity O(1) schema construction; O(n) decode where n is the response size. + * @throws Never. + */ export const CodexStatusResponseSchema = Schema.Struct({ ok: Schema.optional(Schema.Boolean), status: CodexAuthStatusSchema diff --git a/packages/app/src/web/api-create-project.ts b/packages/app/src/web/api-create-project.ts index 461d9e6e..061c3a1c 100644 --- a/packages/app/src/web/api-create-project.ts +++ b/packages/app/src/web/api-create-project.ts @@ -1,26 +1,37 @@ import type { Effect } from "effect" +import { + baseCreateProjectBody, + type CreateProjectRequestDraft, + optionalProjectResourceFields +} from "./api-project-create-body.js" import { CreateProjectAcceptedResponseSchema } from "./api-schema.js" -import type { CreateProjectAcceptedResponse, CreateProjectDraft } from "./api-schema.js" +import type { CreateProjectAcceptedResponse } from "./api-schema.js" import { openApiJsonSchema } from "./openapi-client.js" -const createProjectAcceptedBody = (draft: CreateProjectDraft) => ({ +/** + * Builds the async POST /projects request body. + * + * @param draft - Validated project creation draft plus optional resource limits. + * @returns Request body for an accepted asynchronous create request. + * + * @pure true - deterministic serialization of immutable input. + * @effect none + * @invariant async create uses the same common fields and optional resource fields as sync create. + * @precondition draft fields were validated by the UI create flow. + * @postcondition output includes async = true and preserves defined Playwright resource limits. + * @complexity O(1). + * @throws Never. + */ +export const createProjectAcceptedBody = (draft: CreateProjectRequestDraft) => ({ + ...baseCreateProjectBody(draft), async: true, - cpuLimit: draft.cpuLimit, - enableMcpPlaywright: draft.enableMcpPlaywright, - force: draft.force, - forceEnv: draft.forceEnv, - gpu: draft.gpu, - openSsh: false, - outDir: draft.outDir, - ramLimit: draft.ramLimit, - repoRef: draft.repoRef, - repoUrl: draft.repoUrl, - up: draft.up, - useManagedAuthorizedKeys: true + ...optionalProjectResourceFields(draft) }) -export const startCreateProject = (draft: CreateProjectDraft): Effect.Effect => +export const startCreateProject = ( + draft: CreateProjectRequestDraft +): Effect.Effect => openApiJsonSchema(CreateProjectAcceptedResponseSchema, (client) => client.POST("/projects", { body: createProjectAcceptedBody(draft) diff --git a/packages/app/src/web/api-project-core.ts b/packages/app/src/web/api-project-core.ts index b39ee944..565b6143 100644 --- a/packages/app/src/web/api-project-core.ts +++ b/packages/app/src/web/api-project-core.ts @@ -1,18 +1,16 @@ import { Effect } from "effect" -import type { ApplyProjectRequest, ProjectResourceLimitRequest } from "../shared/project-resource-request.js" -import type { CreateProjectDraft } from "./api-schema.js" +import type { ApplyProjectRequest } from "../shared/project-resource-request.js" +import { + baseCreateProjectBody, + type CreateProjectRequestDraft, + optionalProjectResourceFields +} from "./api-project-create-body.js" import { OutputResponseSchema, ProjectResponseSchema } from "./api-schema.js" import { openApiJsonSchema } from "./openapi-client.js" export type { ApplyProjectRequest, ProjectResourceLimitRequest } from "../shared/project-resource-request.js" - -type CreateProjectRequestDraft = CreateProjectDraft & ProjectResourceLimitRequest - -const optionalProjectResourceFields = (request: ProjectResourceLimitRequest) => ({ - ...(request.playwrightCpuLimit !== undefined && { playwrightCpuLimit: request.playwrightCpuLimit }), - ...(request.playwrightRamLimit !== undefined && { playwrightRamLimit: request.playwrightRamLimit }) -}) +export type { CreateProjectRequestDraft } from "./api-project-create-body.js" const applyProjectBody = (request: ApplyProjectRequest | undefined) => ({ ...(request?.cpuLimit !== undefined && { cpuLimit: request.cpuLimit }), @@ -22,18 +20,7 @@ const applyProjectBody = (request: ApplyProjectRequest | undefined) => ({ }) const createProjectBody = (draft: CreateProjectRequestDraft) => ({ - cpuLimit: draft.cpuLimit, - enableMcpPlaywright: draft.enableMcpPlaywright, - force: draft.force, - forceEnv: draft.forceEnv, - gpu: draft.gpu, - openSsh: false, - outDir: draft.outDir, - ramLimit: draft.ramLimit, - repoRef: draft.repoRef, - repoUrl: draft.repoUrl, - up: draft.up, - useManagedAuthorizedKeys: true, + ...baseCreateProjectBody(draft), ...optionalProjectResourceFields(draft) }) diff --git a/packages/app/src/web/api-project-create-body.ts b/packages/app/src/web/api-project-create-body.ts new file mode 100644 index 00000000..109f31ea --- /dev/null +++ b/packages/app/src/web/api-project-create-body.ts @@ -0,0 +1,63 @@ +import type { ProjectResourceLimitRequest } from "../shared/project-resource-request.js" +import type { CreateProjectDraft } from "./api-schema.js" + +/** + * Draft accepted by POST /projects helpers. + * + * @pure true - type-only boundary contract. + * @effect none + * @invariant includes the base project draft plus optional resource limit fields shared by sync and async create flows. + * @precondition callers already validated UI input into CreateProjectDraft fields. + * @postcondition request builders can serialize the same resource fields for both create variants. + * @complexity O(1). + * @throws Never. + */ +export type CreateProjectRequestDraft = CreateProjectDraft & ProjectResourceLimitRequest + +/** + * Serializes optional Playwright resource limits for project creation requests. + * + * @param request - Shared resource limit fields from the validated create draft. + * @returns Object containing only defined Playwright limit fields. + * + * @pure true - deterministic projection from immutable input. + * @effect none + * @invariant undefined optional fields are omitted from the request body. + * @precondition request is a validated web create/apply resource limit request. + * @postcondition output is safe to spread into a JSON request body. + * @complexity O(1). + * @throws Never. + */ +export const optionalProjectResourceFields = (request: ProjectResourceLimitRequest) => ({ + ...(request.playwrightCpuLimit !== undefined && { playwrightCpuLimit: request.playwrightCpuLimit }), + ...(request.playwrightRamLimit !== undefined && { playwrightRamLimit: request.playwrightRamLimit }) +}) + +/** + * Builds the common POST /projects request body used by sync and async flows. + * + * @param draft - Validated project creation draft. + * @returns Shared request body fields without the flow-specific async flag. + * + * @pure true - deterministic serialization of create draft fields. + * @effect none + * @invariant sync and async create requests share one definition of common fields. + * @precondition draft fields were validated by the UI create flow. + * @postcondition output preserves all non-optional project creation fields. + * @complexity O(1). + * @throws Never. + */ +export const baseCreateProjectBody = (draft: CreateProjectDraft) => ({ + cpuLimit: draft.cpuLimit, + enableMcpPlaywright: draft.enableMcpPlaywright, + force: draft.force, + forceEnv: draft.forceEnv, + gpu: draft.gpu, + openSsh: false, + outDir: draft.outDir, + ramLimit: draft.ramLimit, + repoRef: draft.repoRef, + repoUrl: draft.repoUrl, + up: draft.up, + useManagedAuthorizedKeys: true +}) diff --git a/packages/app/src/web/api-terminal.ts b/packages/app/src/web/api-terminal.ts index 0f18cecd..47ca25e1 100644 --- a/packages/app/src/web/api-terminal.ts +++ b/packages/app/src/web/api-terminal.ts @@ -1,6 +1,5 @@ import { Effect } from "effect" -import { requestText } from "./api-http.js" import { AuthTerminalSessionResponseSchema, ProjectTerminalSessionResponseSchema, @@ -59,6 +58,15 @@ export const deleteProjectTerminalSession = ( }) ) +export const deleteAuthTerminalSession = (sessionId: string) => + openApiVoid((client) => + client.DELETE("/auth/terminal-sessions/{sessionId}", { + params: { path: { sessionId } } + }) + ) + +// WHY: panel UI needs only the sessions array for list rendering. +// INVARIANT: this helper intentionally projects the full terminal workspace response to sessions. export const loadProjectTerminalSessions = (projectKey: string) => openApiJsonSchema( ProjectTerminalSessionsResponseSchema, @@ -70,6 +78,8 @@ export const loadProjectTerminalSessions = (projectKey: string) => Effect.map((response) => response.sessions) ) +// WHY: SSH-link initialization needs the full terminal workspace, including activeSessionId. +// INVARIANT: this helper intentionally preserves the complete response shape. export const loadProjectTerminalWorkspace = (projectKey: string) => openApiJsonSchema( ProjectTerminalSessionsResponseSchema, @@ -114,4 +124,60 @@ export const loadTerminalSessionById = (sessionId: string) => params: { path: { sessionId } } })) -export const deleteTerminalSessionByPath = (path: string) => requestText("DELETE", path).pipe(Effect.asVoid) +const invalidTerminalClosePath = (path: string): string => `Invalid terminal close path: ${path}` + +const authTerminalClosePathPattern = /^\/auth\/terminal-sessions\/([^/]+)$/u +const projectTerminalClosePathPattern = /^\/projects\/by-key\/([^/]+)\/terminal-sessions\/([^/]+)$/u + +const decodeTerminalClosePathSegment = ( + segment: string, + path: string +): Effect.Effect => + Effect.try({ + try: () => decodeURIComponent(segment), + catch: () => invalidTerminalClosePath(path) + }) + +const readTerminalClosePathMatchSegment = ( + match: RegExpExecArray, + index: number, + path: string +): Effect.Effect => { + const segment = match[index] + return segment === undefined + ? Effect.fail(invalidTerminalClosePath(path)) + : decodeTerminalClosePathSegment(segment, path) +} + +const deleteMatchedAuthTerminalSession = ( + match: RegExpExecArray, + path: string +): Effect.Effect => + readTerminalClosePathMatchSegment(match, 1, path).pipe( + Effect.flatMap((sessionId) => deleteAuthTerminalSession(sessionId)) + ) + +const deleteMatchedProjectTerminalSession = ( + match: RegExpExecArray, + path: string +): Effect.Effect => + Effect.all({ + projectKey: readTerminalClosePathMatchSegment(match, 1, path), + sessionId: readTerminalClosePathMatchSegment(match, 2, path) + }).pipe( + Effect.flatMap(({ projectKey, sessionId }) => deleteProjectTerminalSession(projectKey, sessionId)) + ) + +export const deleteTerminalSessionByPath = (path: string): Effect.Effect => { + const authMatch = authTerminalClosePathPattern.exec(path) + if (authMatch !== null) { + return deleteMatchedAuthTerminalSession(authMatch, path) + } + + const projectMatch = projectTerminalClosePathPattern.exec(path) + if (projectMatch !== null) { + return deleteMatchedProjectTerminalSession(projectMatch, path) + } + + return Effect.fail(invalidTerminalClosePath(path)) +} diff --git a/packages/app/src/web/api.ts b/packages/app/src/web/api.ts index 329c5a0e..3b2a71ef 100644 --- a/packages/app/src/web/api.ts +++ b/packages/app/src/web/api.ts @@ -59,6 +59,7 @@ export { loadProjectTaskLogs, loadProjectTasks, stopProjectTask } from "./api-ta export { createAuthTerminalSession, createProjectTerminalSession, + deleteAuthTerminalSession, deleteProjectTerminalSession, deleteTerminalSessionByPath, loadProjectTerminalSession, diff --git a/packages/app/src/web/generated/openapi-paths.ts b/packages/app/src/web/generated/openapi-paths.ts index 22181121..19bc4aab 100644 --- a/packages/app/src/web/generated/openapi-paths.ts +++ b/packages/app/src/web/generated/openapi-paths.ts @@ -942,8 +942,14 @@ export interface operations { }; content: { "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: string; - message: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; @@ -990,8 +996,14 @@ export interface operations { }; content: { "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: string; - message: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; @@ -1056,7 +1068,7 @@ export interface operations { }; responses: { /** @description Success */ - 200: { + 201: { headers: { [name: string]: unknown; }; @@ -1116,8 +1128,14 @@ export interface operations { }; content: { "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: string; - message: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; @@ -1152,8 +1170,14 @@ export interface operations { }; content: { "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: string; - message: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; @@ -1182,8 +1206,14 @@ export interface operations { }; content: { "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: string; - message: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; @@ -1247,8 +1277,14 @@ export interface operations { }; content: { "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: string; - message: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; @@ -1279,8 +1315,14 @@ export interface operations { }; content: { "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: string; - message: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; @@ -1311,8 +1353,14 @@ export interface operations { }; content: { "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: string; - message: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; @@ -1387,8 +1435,14 @@ export interface operations { }; content: { "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: string; - message: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; @@ -1459,8 +1513,14 @@ export interface operations { }; content: { "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: string; - message: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; @@ -1524,8 +1584,14 @@ export interface operations { }; content: { "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: string; - message: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; @@ -1589,8 +1655,14 @@ export interface operations { }; content: { "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: string; - message: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; @@ -1625,8 +1697,14 @@ export interface operations { }; content: { "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: string; - message: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; @@ -1661,8 +1739,14 @@ export interface operations { }; content: { "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: string; - message: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; @@ -1712,8 +1796,14 @@ export interface operations { }; content: { "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: string; - message: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; @@ -1738,7 +1828,7 @@ export interface operations { }; responses: { /** @description Success */ - 200: { + 201: { headers: { [name: string]: unknown; }; @@ -1770,8 +1860,14 @@ export interface operations { }; content: { "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: string; - message: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; @@ -1803,8 +1899,14 @@ export interface operations { }; content: { "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: string; - message: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; @@ -1849,8 +1951,14 @@ export interface operations { }; content: { "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: string; - message: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; @@ -1895,8 +2003,14 @@ export interface operations { }; content: { "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: string; - message: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; @@ -1943,8 +2057,14 @@ export interface operations { }; content: { "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: string; - message: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; @@ -1969,7 +2089,7 @@ export interface operations { }; responses: { /** @description Success */ - 200: { + 201: { headers: { [name: string]: unknown; }; @@ -1998,8 +2118,14 @@ export interface operations { }; content: { "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: string; - message: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; @@ -2031,8 +2157,14 @@ export interface operations { }; content: { "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: string; - message: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; @@ -2051,7 +2183,7 @@ export interface operations { requestBody?: never; responses: { /** @description Success */ - 200: { + 201: { headers: { [name: string]: unknown; }; @@ -2088,8 +2220,14 @@ export interface operations { }; content: { "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: string; - message: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; @@ -2121,8 +2259,14 @@ export interface operations { }; content: { "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: string; - message: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; @@ -2177,8 +2321,14 @@ export interface operations { }; content: { "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: string; - message: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; @@ -2222,8 +2372,14 @@ export interface operations { }; content: { "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: string; - message: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; @@ -2267,8 +2423,14 @@ export interface operations { }; content: { "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: string; - message: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; @@ -2312,8 +2474,14 @@ export interface operations { }; content: { "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: string; - message: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; @@ -2356,8 +2524,14 @@ export interface operations { }; content: { "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: string; - message: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; @@ -2400,8 +2574,14 @@ export interface operations { }; content: { "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: string; - message: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; @@ -2441,8 +2621,14 @@ export interface operations { }; content: { "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: string; - message: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; @@ -2485,8 +2671,14 @@ export interface operations { }; content: { "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: string; - message: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; @@ -2528,8 +2720,14 @@ export interface operations { }; content: { "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: string; - message: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; @@ -2580,8 +2778,14 @@ export interface operations { }; content: { "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: string; - message: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; @@ -2630,8 +2834,14 @@ export interface operations { }; content: { "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: string; - message: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; @@ -2681,8 +2891,14 @@ export interface operations { }; content: { "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: string; - message: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; @@ -2731,8 +2947,14 @@ export interface operations { }; content: { "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: string; - message: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; @@ -2780,8 +3002,14 @@ export interface operations { }; content: { "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: string; - message: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; @@ -2827,8 +3055,14 @@ export interface operations { }; content: { "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: string; - message: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; @@ -2876,8 +3110,14 @@ export interface operations { }; content: { "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: string; - message: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; @@ -2936,8 +3176,14 @@ export interface operations { }; content: { "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: string; - message: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; @@ -2961,7 +3207,7 @@ export interface operations { }; responses: { /** @description Success */ - 200: { + 201: { headers: { [name: string]: unknown; }; @@ -2991,8 +3237,14 @@ export interface operations { }; content: { "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: string; - message: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; @@ -3039,8 +3291,14 @@ export interface operations { }; content: { "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: string; - message: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; @@ -3086,8 +3344,14 @@ export interface operations { }; content: { "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: string; - message: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; @@ -3134,8 +3398,14 @@ export interface operations { }; content: { "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: string; - message: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; @@ -3192,8 +3462,14 @@ export interface operations { }; content: { "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: string; - message: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; @@ -3258,8 +3534,14 @@ export interface operations { }; content: { "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: string; - message: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; @@ -3307,8 +3589,14 @@ export interface operations { }; content: { "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: string; - message: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; @@ -3326,7 +3614,7 @@ export interface operations { requestBody?: never; responses: { /** @description Success */ - 200: { + 201: { headers: { [name: string]: unknown; }; @@ -3385,8 +3673,14 @@ export interface operations { }; content: { "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: string; - message: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; @@ -3431,8 +3725,14 @@ export interface operations { }; content: { "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: string; - message: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; @@ -3480,8 +3780,14 @@ export interface operations { }; content: { "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: string; - message: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; @@ -3513,8 +3819,14 @@ export interface operations { }; content: { "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: string; - message: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; @@ -3567,8 +3879,14 @@ export interface operations { }; content: { "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: string; - message: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; @@ -3617,8 +3935,14 @@ export interface operations { }; content: { "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: string; - message: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; @@ -3649,8 +3973,14 @@ export interface operations { }; content: { "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: string; - message: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; @@ -3700,8 +4030,14 @@ export interface operations { }; content: { "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: string; - message: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; @@ -3768,8 +4104,14 @@ export interface operations { }; content: { "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: string; - message: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; @@ -3820,8 +4162,14 @@ export interface operations { }; content: { "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: string; - message: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; @@ -3878,8 +4226,14 @@ export interface operations { }; content: { "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: string; - message: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; @@ -3956,8 +4310,14 @@ export interface operations { }; content: { "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: string; - message: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; @@ -4016,8 +4376,14 @@ export interface operations { }; content: { "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: string; - message: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; @@ -4073,8 +4439,14 @@ export interface operations { }; content: { "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: string; - message: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; @@ -4106,8 +4478,14 @@ export interface operations { }; content: { "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: string; - message: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; @@ -4145,8 +4523,14 @@ export interface operations { }; content: { "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: string; - message: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; @@ -4189,8 +4573,14 @@ export interface operations { }; content: { "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: string; - message: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; @@ -4239,8 +4629,14 @@ export interface operations { }; content: { "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: string; - message: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; @@ -4283,8 +4679,14 @@ export interface operations { }; content: { "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: string; - message: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; diff --git a/packages/app/src/web/openapi-client.ts b/packages/app/src/web/openapi-client.ts index bf28c38d..c9da6064 100644 --- a/packages/app/src/web/openapi-client.ts +++ b/packages/app/src/web/openapi-client.ts @@ -1,7 +1,7 @@ import * as ParseResult from "@effect/schema/ParseResult" import type * as Schema from "@effect/schema/Schema" import * as TreeFormatter from "@effect/schema/TreeFormatter" -import { Effect, Either } from "effect" +import { Effect, Either, Option } from "effect" import createClient, { type Client, type Middleware } from "openapi-fetch" import { resolveApiBaseUrl } from "./api-http.js" @@ -18,9 +18,11 @@ type ApiTransportValue = | ReadonlyArray | { readonly [key: string]: ApiTransportValue } +type ApiTransportError = ApiTransportValue | object + type OpenApiResponse = { readonly data?: A - readonly error?: ApiTransportValue + readonly error?: ApiTransportError readonly response: Response } @@ -34,38 +36,38 @@ const noCacheHeaders: Readonly> = { pragma: "no-cache" } -const stringifyJson = (value: ApiTransportValue): Effect.Effect => +const stringifyJson = (value: ApiTransportError): Effect.Effect => Effect.try({ try: () => JSON.stringify(value, null, 2), catch: () => null }) -const safeJson = (value: ApiTransportValue): string | null => { - const result = Effect.runSync(Effect.either(stringifyJson(value))) - return Either.match(result, { - onLeft: (fallback) => fallback, - onRight: (json) => json - }) -} +const safeJson = (value: ApiTransportError): Effect.Effect => + stringifyJson(value).pipe( + Effect.orElseSucceed(() => "unrenderable response payload") + ) -const renderTransportValue = (value: ApiTransportValue): string => { +const renderTransportValue = (value: ApiTransportError): Effect.Effect => { if (typeof value === "string") { - return value + return Effect.succeed(value) } if (typeof value === "object" && value !== null && "message" in value) { const message = value["message"] if (typeof message === "string") { - return message + return Effect.succeed(message) } } - return safeJson(value) ?? "unrenderable response payload" + return safeJson(value) } -const renderOpenApiError = (response: Response, error: ApiTransportValue): string => { +const renderOpenApiError = ( + response: Response, + error: ApiTransportError | undefined +): Effect.Effect => { if (response.status === 429) { - return "HTTP 429: tunnel or proxy rate limited the request. Retry or request a fresh tunnel URL." + return Effect.succeed("HTTP 429: tunnel or proxy rate limited the request. Retry or request a fresh tunnel URL.") } - return error === undefined ? `HTTP ${response.status}` : renderTransportValue(error) + return error === undefined ? Effect.succeed(`HTTP ${response.status}`) : renderTransportValue(error) } const noCacheGetMiddleware: Middleware = { @@ -88,6 +90,11 @@ const makeClient = (baseUrl: string): DockerGitOpenApiClient => { return client } +// PURITY: SHELL +// LIMITATION: mutable cache for client reuse until frontend API helpers are migrated to an Effect Layer. +// MIGRATION: replace this cache with Context.Tag + Layer once UI call sites provide shared dependencies. +// INVARIANT: cache is keyed only by resolved baseUrl and is invalidated on baseUrl change. +// TESTABILITY: tests that exercise client creation must isolate module state or reset the module between cases. const clientCache: { baseUrl: string | null client: DockerGitOpenApiClient | null @@ -113,6 +120,14 @@ const runOpenApi = ( catch: String }) +const failRenderedOpenApiError = ( + response: Response, + error: ApiTransportError | undefined +): Effect.Effect => + renderOpenApiError(response, error).pipe( + Effect.flatMap((message) => Effect.fail(message)) + ) + /** * Executes a typed OpenAPI JSON request through openapi-fetch. * @@ -141,12 +156,18 @@ export const openApiJson = ( request: OpenApiRequest ): Effect.Effect => runOpenApi(request).pipe( - Effect.flatMap(({ data, error, response }) => { - if (error !== undefined || !response.ok) { - return Effect.fail(renderOpenApiError(response, error)) - } - return data === undefined ? Effect.fail(`HTTP ${response.status}: empty response`) : Effect.succeed(data) - }) + Effect.flatMap(({ data, error, response }) => + Option.match(Option.fromNullable(error), { + onNone: () => + response.ok + ? Option.match(Option.fromNullable(data), { + onNone: () => Effect.fail(`HTTP ${response.status}: empty response`), + onSome: (value) => Effect.succeed(value) + }) + : failRenderedOpenApiError(response, error), + onSome: (apiError) => failRenderedOpenApiError(response, apiError) + }) + ) ) const decodeSchema = (schema: Schema.Schema, value: ApiTransportValue): Effect.Effect => @@ -217,8 +238,11 @@ export const openApiVoid = ( ): Effect.Effect => runOpenApi(request).pipe( Effect.flatMap(({ error, response }) => - error !== undefined || !response.ok - ? Effect.fail(renderOpenApiError(response, error)) - : Effect.void + response.ok + ? Option.match(Option.fromNullable(error), { + onNone: () => Effect.void, + onSome: (apiError) => failRenderedOpenApiError(response, apiError) + }) + : failRenderedOpenApiError(response, error) ) ) diff --git a/packages/app/tests/docker-git/api-create-project.test.ts b/packages/app/tests/docker-git/api-create-project.test.ts new file mode 100644 index 00000000..887c9b7c --- /dev/null +++ b/packages/app/tests/docker-git/api-create-project.test.ts @@ -0,0 +1,43 @@ +import { describe, expect, it } from "@effect/vitest" +import { Effect } from "effect" + +import { createProjectAcceptedBody } from "../../src/web/api-create-project.js" +import type { CreateProjectRequestDraft } from "../../src/web/api-project-create-body.js" + +const projectDraft = { + cpuLimit: "80%", + enableMcpPlaywright: true, + force: false, + forceEnv: false, + gpu: "none", + outDir: "/home/dev/.docker-git/octocat/hello-world", + playwrightCpuLimit: "40%", + playwrightRamLimit: "512m", + ramLimit: "2g", + repoRef: "main", + repoUrl: "https://github.com/octocat/hello-world.git", + up: true +} satisfies CreateProjectRequestDraft + +describe("api create project request body", () => { + it.effect("serializes async create requests with Playwright resource limits", () => + Effect.sync(() => { + expect(createProjectAcceptedBody(projectDraft)).toEqual({ + async: true, + cpuLimit: "80%", + enableMcpPlaywright: true, + force: false, + forceEnv: false, + gpu: "none", + openSsh: false, + outDir: "/home/dev/.docker-git/octocat/hello-world", + playwrightCpuLimit: "40%", + playwrightRamLimit: "512m", + ramLimit: "2g", + repoRef: "main", + repoUrl: "https://github.com/octocat/hello-world.git", + up: true, + useManagedAuthorizedKeys: true + }) + })) +}) diff --git a/packages/app/tests/docker-git/api-terminal.test.ts b/packages/app/tests/docker-git/api-terminal.test.ts new file mode 100644 index 00000000..d7807493 --- /dev/null +++ b/packages/app/tests/docker-git/api-terminal.test.ts @@ -0,0 +1,84 @@ +import { describe, expect, it } from "@effect/vitest" +import { Effect } from "effect" +import { beforeEach, vi } from "vitest" + +import { deleteTerminalSessionByPath } from "../../src/web/api-terminal.js" + +type CapturedDeleteRequest = { + readonly params: Readonly> + readonly route: string +} + +type MinimalDeleteClient = { + readonly DELETE: ( + route: string, + options: { readonly params: { readonly path: Readonly> } } + ) => void +} + +const capturedDeleteRequests = vi.hoisted((): Array => []) +const openApiVoidMock = vi.hoisted(() => + vi.fn((request: (client: MinimalDeleteClient) => void) => { + const client: MinimalDeleteClient = { + DELETE: (route, options) => { + capturedDeleteRequests.push({ + params: options.params.path, + route + }) + } + } + request(client) + return Effect.void + }) +) + +vi.mock("../../src/web/openapi-client.js", () => ({ + openApiJsonSchema: vi.fn(), + openApiVoid: openApiVoidMock +})) + +describe("api terminal helpers", () => { + beforeEach(() => { + capturedDeleteRequests.length = 0 + openApiVoidMock.mockClear() + }) + + it.effect("routes auth terminal close paths through the typed OpenAPI endpoint", () => + deleteTerminalSessionByPath("/auth/terminal-sessions/auth-session-1").pipe( + Effect.tap(() => + Effect.sync(() => { + expect(capturedDeleteRequests).toEqual([ + { + params: { sessionId: "auth-session-1" }, + route: "/auth/terminal-sessions/{sessionId}" + } + ]) + }) + ) + )) + + it.effect("routes project terminal close paths through the typed OpenAPI endpoint", () => + deleteTerminalSessionByPath("/projects/by-key/octocat%2Fhello-world/terminal-sessions/session-1").pipe( + Effect.tap(() => + Effect.sync(() => { + expect(capturedDeleteRequests).toEqual([ + { + params: { + projectKey: "octocat/hello-world", + sessionId: "session-1" + }, + route: "/projects/by-key/{projectKey}/terminal-sessions/{sessionId}" + } + ]) + }) + ) + )) + + it.effect("rejects unsupported terminal close paths before issuing a request", () => + Effect.gen(function*(_) { + const result = yield* _(Effect.either(deleteTerminalSessionByPath("/terminal-sessions/session-1"))) + + expect(result._tag).toBe("Left") + expect(capturedDeleteRequests).toEqual([]) + })) +}) diff --git a/scripts/write-openapi.ts b/scripts/write-openapi.ts index 40db46dd..866fd3e5 100644 --- a/scripts/write-openapi.ts +++ b/scripts/write-openapi.ts @@ -8,4 +8,16 @@ const __dirname = dirname(__filename) const repositoryRoot = resolve(__dirname, "..") const outputPath = resolve(repositoryRoot, "packages/api/openapi.json") -await Bun.write(outputPath, `${JSON.stringify(buildDockerGitOpenApi(), null, 2)}\n`) +const describeWriteError = (error: unknown): string => + error instanceof Error ? error.message : String(error) + +try { + const spec = buildDockerGitOpenApi() + const content = `${JSON.stringify(spec, null, 2)}\n` + + await Bun.write(outputPath, content) + console.log(`OpenAPI spec written to ${outputPath}`) +} catch (error) { + console.error(`Failed to write OpenAPI spec: ${describeWriteError(error)}`) + process.exit(1) +} From 5ca3d90ba61de61c50a13bad8ecbc8660a637a4c Mon Sep 17 00:00:00 2001 From: skulidropek <66840575+skulidropek@users.noreply.github.com> Date: Thu, 18 Jun 2026 13:15:30 +0000 Subject: [PATCH 04/10] refactor(openapi): move generated artifacts into workspace package --- bun.lock | 12 ++++++- package.json | 7 ++-- packages/app/biome.json | 2 +- packages/app/eslint.config.mts | 2 +- packages/app/package.json | 4 +-- packages/app/src/web/openapi-client.ts | 2 +- packages/{api => openapi}/openapi.json | 0 packages/openapi/package.json | 32 +++++++++++++++++++ packages/openapi/src/index.ts | 14 ++++++++ .../src}/openapi-paths.ts | 0 packages/openapi/tsconfig.json | 9 ++++++ scripts/write-openapi.ts | 2 +- 12 files changed, 76 insertions(+), 10 deletions(-) rename packages/{api => openapi}/openapi.json (100%) create mode 100644 packages/openapi/package.json create mode 100644 packages/openapi/src/index.ts rename packages/{app/src/web/generated => openapi/src}/openapi-paths.ts (100%) create mode 100644 packages/openapi/tsconfig.json diff --git a/bun.lock b/bun.lock index 59c81af7..af90171c 100644 --- a/bun.lock +++ b/bun.lock @@ -79,6 +79,7 @@ "@eslint/compat": "2.1.0", "@eslint/eslintrc": "3.3.5", "@eslint/js": "10.0.1", + "@prover-coder-ai/docker-git-openapi": "workspace:*", "@prover-coder-ai/docker-git-terminal": "workspace:*", "@prover-coder-ai/eslint-plugin-suggest-members": "^0.0.26", "@ton-ai-core/vibecode-linter": "^1.0.11", @@ -103,7 +104,6 @@ "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", @@ -233,6 +233,14 @@ "vitest": "^4.1.9", }, }, + "packages/openapi": { + "name": "@prover-coder-ai/docker-git-openapi", + "version": "0.1.0", + "devDependencies": { + "openapi-typescript": "^7.13.0", + "typescript": "^6.0.3", + }, + }, "packages/terminal": { "name": "@prover-coder-ai/docker-git-terminal", "version": "0.1.1", @@ -656,6 +664,8 @@ "@prover-coder-ai/docker-git-container": ["@prover-coder-ai/docker-git-container@workspace:packages/container"], + "@prover-coder-ai/docker-git-openapi": ["@prover-coder-ai/docker-git-openapi@workspace:packages/openapi"], + "@prover-coder-ai/docker-git-session-sync": ["@prover-coder-ai/docker-git-session-sync@workspace:packages/docker-git-session-sync"], "@prover-coder-ai/docker-git-terminal": ["@prover-coder-ai/docker-git-terminal@workspace:packages/terminal"], diff --git a/package.json b/package.json index a83bfa4f..8ddeda9b 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "packages/container", "packages/docker-git-session-sync", "packages/lib", + "packages/openapi", "packages/terminal" ], "scripts": { @@ -20,7 +21,7 @@ "api:dev": "bun run --filter @effect-template/api dev", "api:test": "bun run --filter @effect-template/api test", "api:typecheck": "bun run --filter @effect-template/api typecheck", - "check": "bun run --filter @prover-coder-ai/docker-git-session-sync check && bun run --filter @prover-coder-ai/docker-git-terminal check && bun run --filter @prover-coder-ai/docker-git check && bun run --filter @effect-template/lib typecheck", + "check": "bun run --filter @prover-coder-ai/docker-git-session-sync check && bun run --filter @prover-coder-ai/docker-git-terminal check && bun run --filter @prover-coder-ai/docker-git-openapi check && bun run --filter @prover-coder-ai/docker-git check && bun run --filter @effect-template/lib typecheck", "check:dist-deps-prune": "bun node_modules/@prover-coder-ai/dist-deps-prune/dist/main.js scan --package ./packages/app/package.json --prune-dev true --silent", "changeset": "changeset", "changeset-publish": "bun -e \"if (!process.env.NPM_TOKEN) { console.log('Skipping publish: NPM_TOKEN is not set'); process.exit(0); }\" && changeset publish", @@ -42,14 +43,14 @@ "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:generate-api": "bun run --cwd packages/openapi generate", "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", "lint:tests": "bun run --filter @prover-coder-ai/docker-git lint:tests", "lint:effect": "bun run --filter @prover-coder-ai/docker-git-session-sync lint:effect && bun run --filter @prover-coder-ai/docker-git-terminal lint:effect && bun run --filter @prover-coder-ai/docker-git lint:effect && bun run --filter @prover-coder-ai/docker-git-container lint:effect && bun run --filter @effect-template/lib lint:effect && bun run --filter @effect-template/api lint:effect", "test": "bun run --filter @prover-coder-ai/docker-git-session-sync test && bun run --filter @prover-coder-ai/docker-git-terminal test && bun run --filter @prover-coder-ai/docker-git test && bun run --filter @effect-template/lib test", - "typecheck": "bun run --filter @prover-coder-ai/docker-git-session-sync typecheck && bun run --filter @prover-coder-ai/docker-git-terminal typecheck && bun run --filter @prover-coder-ai/docker-git typecheck && bun run --filter @effect-template/lib typecheck", + "typecheck": "bun run --filter @prover-coder-ai/docker-git-session-sync typecheck && bun run --filter @prover-coder-ai/docker-git-terminal typecheck && bun run --filter @prover-coder-ai/docker-git-openapi typecheck && bun run --filter @prover-coder-ai/docker-git typecheck && bun run --filter @effect-template/lib typecheck", "start": "bun run --cwd packages/app build:docker-git && bun ./packages/app/dist/src/docker-git/main.js" }, "devDependencies": { diff --git a/packages/app/biome.json b/packages/app/biome.json index fe8bb3b8..6d652efc 100644 --- a/packages/app/biome.json +++ b/packages/app/biome.json @@ -7,7 +7,7 @@ }, "files": { "ignoreUnknown": false, - "includes": ["**", "!src/web/generated/**"] + "includes": ["**"] }, "assist": { "enabled": false diff --git a/packages/app/eslint.config.mts b/packages/app/eslint.config.mts index 2870aaae..0a1fba61 100644 --- a/packages/app/eslint.config.mts +++ b/packages/app/eslint.config.mts @@ -304,5 +304,5 @@ export default defineConfig( }, // 4) Глобальные игноры - { ignores: ['dist/**', 'build/**', 'coverage/**', '**/dist/**', 'src/web/generated/**'] }, + { ignores: ['dist/**', 'build/**', 'coverage/**', '**/dist/**'] }, ); diff --git a/packages/app/package.json b/packages/app/package.json index 57df0c32..90df5380 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -18,7 +18,7 @@ "build:app": "vite build --ssr src/app/main.ts", "build:web": "vite build --config vite.web.config.ts", "build:web:strict": "bun ../../scripts/ci/check-web-build-output.mjs", - "generate:api": "bun ../../scripts/write-openapi.ts && openapi-typescript ../../packages/api/openapi.json -o src/web/generated/openapi-paths.ts", + "generate:api": "bun run --cwd ../openapi generate", "prepack": "bun run build:docker-git", "dev": "vite build --watch --ssr src/app/main.ts", "dev:web": "vite --config vite.web.config.ts", @@ -96,6 +96,7 @@ "@eslint/compat": "2.1.0", "@eslint/eslintrc": "3.3.5", "@eslint/js": "10.0.1", + "@prover-coder-ai/docker-git-openapi": "workspace:*", "@prover-coder-ai/docker-git-terminal": "workspace:*", "@prover-coder-ai/eslint-plugin-suggest-members": "^0.0.26", "@ton-ai-core/vibecode-linter": "^1.0.11", @@ -120,7 +121,6 @@ "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", diff --git a/packages/app/src/web/openapi-client.ts b/packages/app/src/web/openapi-client.ts index c9da6064..b5db333d 100644 --- a/packages/app/src/web/openapi-client.ts +++ b/packages/app/src/web/openapi-client.ts @@ -1,11 +1,11 @@ import * as ParseResult from "@effect/schema/ParseResult" import type * as Schema from "@effect/schema/Schema" import * as TreeFormatter from "@effect/schema/TreeFormatter" +import type { paths } from "@prover-coder-ai/docker-git-openapi" import { Effect, Either, Option } from "effect" import createClient, { type Client, type Middleware } from "openapi-fetch" import { resolveApiBaseUrl } from "./api-http.js" -import type { paths } from "./generated/openapi-paths.js" type DockerGitOpenApiClient = Client diff --git a/packages/api/openapi.json b/packages/openapi/openapi.json similarity index 100% rename from packages/api/openapi.json rename to packages/openapi/openapi.json diff --git a/packages/openapi/package.json b/packages/openapi/package.json new file mode 100644 index 00000000..0f980109 --- /dev/null +++ b/packages/openapi/package.json @@ -0,0 +1,32 @@ +{ + "name": "@prover-coder-ai/docker-git-openapi", + "version": "0.1.0", + "private": true, + "description": "Generated docker-git OpenAPI artifacts", + "type": "module", + "packageManager": "bun@1.3.11", + "types": "./src/index.ts", + "exports": { + ".": { + "types": "./src/index.ts" + }, + "./openapi-paths": { + "types": "./src/openapi-paths.ts" + }, + "./openapi.json": "./openapi.json" + }, + "scripts": { + "check": "bun run typecheck", + "generate": "bun ../../scripts/write-openapi.ts && openapi-typescript openapi.json -o src/openapi-paths.ts", + "typecheck": "tsc --noEmit -p tsconfig.json" + }, + "devDependencies": { + "openapi-typescript": "^7.13.0", + "typescript": "^6.0.3" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/ProverCoderAI/docker-git.git" + }, + "license": "MIT" +} diff --git a/packages/openapi/src/index.ts b/packages/openapi/src/index.ts new file mode 100644 index 00000000..8f6f26d1 --- /dev/null +++ b/packages/openapi/src/index.ts @@ -0,0 +1,14 @@ +/** + * Generated OpenAPI path map for the docker-git JSON REST API. + * + * @returns Type-only exports consumed by typed OpenAPI clients. + * + * @pure true + * @effect none + * @invariant all exported types are derived from packages/openapi/openapi.json. + * @precondition run `bun run --cwd packages/openapi generate` after changing the source HttpApi contract. + * @postcondition consumers depend on this package instead of importing generated files from app internals. + * @complexity O(1)/O(1) + * @throws Never - type-only module. + */ +export type { components, operations, paths, webhooks } from "./openapi-paths.js" diff --git a/packages/app/src/web/generated/openapi-paths.ts b/packages/openapi/src/openapi-paths.ts similarity index 100% rename from packages/app/src/web/generated/openapi-paths.ts rename to packages/openapi/src/openapi-paths.ts diff --git a/packages/openapi/tsconfig.json b/packages/openapi/tsconfig.json new file mode 100644 index 00000000..3f16f31c --- /dev/null +++ b/packages/openapi/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "rootDir": ".", + "types": [] + }, + "include": ["src/**/*"], + "exclude": ["node_modules"] +} diff --git a/scripts/write-openapi.ts b/scripts/write-openapi.ts index 866fd3e5..f1e30a12 100644 --- a/scripts/write-openapi.ts +++ b/scripts/write-openapi.ts @@ -6,7 +6,7 @@ import { buildDockerGitOpenApi } from "../packages/api/src/api/openapi.js" const __filename = fileURLToPath(import.meta.url) const __dirname = dirname(__filename) const repositoryRoot = resolve(__dirname, "..") -const outputPath = resolve(repositoryRoot, "packages/api/openapi.json") +const outputPath = resolve(repositoryRoot, "packages/openapi/openapi.json") const describeWriteError = (error: unknown): string => error instanceof Error ? error.message : String(error) From c4f39e5d32487f88850c72f3a4ffca6fbd511e5e Mon Sep 17 00:00:00 2001 From: skulidropek <66840575+skulidropek@users.noreply.github.com> Date: Thu, 18 Jun 2026 13:22:13 +0000 Subject: [PATCH 05/10] fix(api): include OpenAPI package in controller Docker install --- packages/api/Dockerfile | 3 ++- .../tests/docker-git/controller-resource-limits.test.ts | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/api/Dockerfile b/packages/api/Dockerfile index 7a90dc03..593094f0 100644 --- a/packages/api/Dockerfile +++ b/packages/api/Dockerfile @@ -76,12 +76,13 @@ RUN set -eu; \ FROM controller-base AS workspace-deps COPY package.json bun.lock bunfig.toml tsconfig.base.json tsconfig.json ./ -RUN mkdir -p packages/api packages/app packages/container packages/docker-git-session-sync packages/lib packages/terminal +RUN mkdir -p packages/api packages/app packages/container packages/docker-git-session-sync packages/lib packages/openapi packages/terminal COPY packages/api/package.json ./packages/api/package.json COPY packages/app/package.json ./packages/app/package.json COPY packages/container/package.json ./packages/container/package.json COPY packages/docker-git-session-sync/package.json ./packages/docker-git-session-sync/package.json COPY packages/lib/package.json ./packages/lib/package.json +COPY packages/openapi/package.json ./packages/openapi/package.json COPY packages/terminal/package.json ./packages/terminal/package.json RUN set -eu; \ diff --git a/packages/app/tests/docker-git/controller-resource-limits.test.ts b/packages/app/tests/docker-git/controller-resource-limits.test.ts index 2a78d0f6..5b33b933 100644 --- a/packages/app/tests/docker-git/controller-resource-limits.test.ts +++ b/packages/app/tests/docker-git/controller-resource-limits.test.ts @@ -120,6 +120,13 @@ describe("API Dockerfile controller tooling install", () => { expect(contents).toContain("test \"$(bun --version)\" = \"1.3.11\"") expect(contents).toContain("node-gyp --version") })) + + it.effect("copies generated OpenAPI package metadata into workspace dependency install stage", () => + Effect.gen(function*(_) { + const contents = yield* _(readComposeFile("packages/api/Dockerfile")) + expect(contents).toContain("packages/openapi") + expect(contents).toContain("COPY packages/openapi/package.json ./packages/openapi/package.json") + })) }) describe("OpenCode E2E auth bootstrap", () => { From f1b607c43de7477c598249663a8240fc5950f632 Mon Sep 17 00:00:00 2001 From: skulidropek <66840575+skulidropek@users.noreply.github.com> Date: Thu, 18 Jun 2026 13:36:37 +0000 Subject: [PATCH 06/10] refactor(openapi): expose shared Effect client runtime --- bun.lock | 6 +- packages/app/package.json | 1 - packages/app/src/web/openapi-client.ts | 258 +++----------------- packages/openapi/package.json | 15 +- packages/openapi/src/client.ts | 312 +++++++++++++++++++++++++ packages/openapi/src/index.ts | 41 ++-- packages/openapi/tsconfig.json | 1 + 7 files changed, 386 insertions(+), 248 deletions(-) create mode 100644 packages/openapi/src/client.ts diff --git a/bun.lock b/bun.lock index af90171c..05634f3c 100644 --- a/bun.lock +++ b/bun.lock @@ -64,7 +64,6 @@ "@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", @@ -236,6 +235,11 @@ "packages/openapi": { "name": "@prover-coder-ai/docker-git-openapi", "version": "0.1.0", + "dependencies": { + "@effect/schema": "^0.75.5", + "effect": "^3.21.3", + "openapi-fetch": "^0.17.0", + }, "devDependencies": { "openapi-typescript": "^7.13.0", "typescript": "^6.0.3", diff --git a/packages/app/package.json b/packages/app/package.json index 90df5380..8b0b2dd4 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -81,7 +81,6 @@ "@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", diff --git a/packages/app/src/web/openapi-client.ts b/packages/app/src/web/openapi-client.ts index b5db333d..89ee5eb8 100644 --- a/packages/app/src/web/openapi-client.ts +++ b/packages/app/src/web/openapi-client.ts @@ -1,248 +1,46 @@ -import * as ParseResult from "@effect/schema/ParseResult" -import type * as Schema from "@effect/schema/Schema" -import * as TreeFormatter from "@effect/schema/TreeFormatter" -import type { paths } from "@prover-coder-ai/docker-git-openapi" -import { Effect, Either, Option } from "effect" -import createClient, { type Client, type Middleware } from "openapi-fetch" +import { makeDockerGitOpenApiRuntime } from "@prover-coder-ai/docker-git-openapi" import { resolveApiBaseUrl } from "./api-http.js" -type DockerGitOpenApiClient = Client - -type ApiTransportValue = - | undefined - | null - | boolean - | number - | string - | ReadonlyArray - | { readonly [key: string]: ApiTransportValue } - -type ApiTransportError = ApiTransportValue | object - -type OpenApiResponse = { - readonly data?: A - readonly error?: ApiTransportError - readonly response: Response -} - -type OpenApiRequestResult = PromiseLike> - -type OpenApiRequest = (client: DockerGitOpenApiClient) => OpenApiRequestResult - -const noCacheHeaders: Readonly> = { - accept: "application/json", - "cache-control": "no-cache, no-store, max-age=0", - pragma: "no-cache" -} - -const stringifyJson = (value: ApiTransportError): Effect.Effect => - Effect.try({ - try: () => JSON.stringify(value, null, 2), - catch: () => null - }) - -const safeJson = (value: ApiTransportError): Effect.Effect => - stringifyJson(value).pipe( - Effect.orElseSucceed(() => "unrenderable response payload") - ) - -const renderTransportValue = (value: ApiTransportError): Effect.Effect => { - if (typeof value === "string") { - return Effect.succeed(value) - } - if (typeof value === "object" && value !== null && "message" in value) { - const message = value["message"] - if (typeof message === "string") { - return Effect.succeed(message) - } - } - return safeJson(value) -} - -const renderOpenApiError = ( - response: Response, - error: ApiTransportError | undefined -): Effect.Effect => { - if (response.status === 429) { - return Effect.succeed("HTTP 429: tunnel or proxy rate limited the request. Retry or request a fresh tunnel URL.") - } - return error === undefined ? Effect.succeed(`HTTP ${response.status}`) : renderTransportValue(error) -} - -const noCacheGetMiddleware: Middleware = { - onRequest: ({ request }) => { - if (request.method !== "GET") { - return - } - const url = new URL(request.url) - url.searchParams.set("_", String(Date.now())) - return new Request(url, request) - } -} - -const makeClient = (baseUrl: string): DockerGitOpenApiClient => { - const client = createClient({ - baseUrl, - headers: noCacheHeaders - }) - client.use(noCacheGetMiddleware) - return client -} - -// PURITY: SHELL -// LIMITATION: mutable cache for client reuse until frontend API helpers are migrated to an Effect Layer. -// MIGRATION: replace this cache with Context.Tag + Layer once UI call sites provide shared dependencies. -// INVARIANT: cache is keyed only by resolved baseUrl and is invalidated on baseUrl change. -// TESTABILITY: tests that exercise client creation must isolate module state or reset the module between cases. -const clientCache: { - baseUrl: string | null - client: DockerGitOpenApiClient | null -} = { - baseUrl: null, - client: null -} - -const getOpenApiClient = (): DockerGitOpenApiClient => { - const baseUrl = resolveApiBaseUrl() - if (clientCache.client === null || clientCache.baseUrl !== baseUrl) { - clientCache.baseUrl = baseUrl - clientCache.client = makeClient(baseUrl) - } - return clientCache.client -} - -const runOpenApi = ( - request: OpenApiRequest -): Effect.Effect, string> => - Effect.tryPromise({ - try: () => request(getOpenApiClient()), - catch: String - }) - -const failRenderedOpenApiError = ( - response: Response, - error: ApiTransportError | undefined -): Effect.Effect => - renderOpenApiError(response, error).pipe( - Effect.flatMap((message) => Effect.fail(message)) - ) +const openApiRuntime = makeDockerGitOpenApiRuntime({ + resolveBaseUrl: resolveApiBaseUrl +}) /** - * Executes a typed OpenAPI JSON request through openapi-fetch. - * - * @param request - Deferred typed openapi-fetch request. - * @returns Effect containing raw 2xx response data or a rendered API error. + * Executes a docker-git OpenAPI JSON request against the current browser API base URL. * - * @pure false - performs browser HTTP IO when the Effect is run. - * @effect Network request via openapi-fetch wrapped by Effect.tryPromise. - * @invariant Promise interop is isolated inside this boundary. - * @precondition request uses a static path from generated OpenAPI paths. - * @postcondition successful Effect contains only the 2xx data branch as a transport value. - * @complexity O(n) local response rendering where n is the error payload size. + * @pure false - performs HTTP IO when the returned Effect is run. + * @effect openapi-fetch request wrapped in Effect. + * @invariant the shared OpenAPI runtime owns transport decoding and error rendering. + * @precondition request uses generated docker-git OpenAPI paths. + * @postcondition success contains the endpoint data branch as a JSON transport value. + * @complexity O(n)/O(n) for error rendering, O(1)/O(1) on local success handling. * @throws Never; failures are returned in the Effect error channel. */ -// CHANGE: route generated OpenAPI client calls through Effect. -// WHY: frontend REST calls must remain composable in the Effect error channel. -// QUOTE(ТЗ): "использовать openapi-fetch на фронте для удобства" -// REF: user-message-2026-06-18-openapi-fetch -// SOURCE: https://openapi-ts.dev/openapi-fetch/ -// FORMAT THEOREM: response.ok ∧ data defined -> success(transport data); otherwise -> failure(message). -// PURITY: SHELL -// EFFECT: Effect -// INVARIANT: no Promise escapes this module. -// COMPLEXITY: O(n)/O(n) for error rendering, O(1)/O(1) on successful local processing. -export const openApiJson = ( - request: OpenApiRequest -): Effect.Effect => - runOpenApi(request).pipe( - Effect.flatMap(({ data, error, response }) => - Option.match(Option.fromNullable(error), { - onNone: () => - response.ok - ? Option.match(Option.fromNullable(data), { - onNone: () => Effect.fail(`HTTP ${response.status}: empty response`), - onSome: (value) => Effect.succeed(value) - }) - : failRenderedOpenApiError(response, error), - onSome: (apiError) => failRenderedOpenApiError(response, apiError) - }) - ) - ) - -const decodeSchema = (schema: Schema.Schema, value: ApiTransportValue): Effect.Effect => - Either.match(ParseResult.decodeUnknownEither(schema)(value), { - onLeft: (error) => Effect.fail(TreeFormatter.formatIssueSync(error)), - onRight: (decoded) => Effect.succeed(decoded) - }) +export const openApiJson = openApiRuntime.openApiJson /** - * Executes an OpenAPI request and decodes the data with an Effect Schema. + * Executes a docker-git OpenAPI request and decodes the response with an Effect Schema. * - * @param schema - Boundary decoder preserving the existing frontend DTO type. - * @param request - Deferred typed openapi-fetch request. - * @returns Effect containing schema-decoded response data. - * - * @pure false - performs browser HTTP IO and boundary decoding when the Effect is run. - * @effect openapi-fetch request plus synchronous Effect Schema decoding. - * @invariant transport typing comes from OpenAPI; exported data typing comes from Schema. - * @precondition schema matches the endpoint success response documented in DockerGitApi. - * @postcondition no generated optional/default representation leaks into existing UI APIs. - * @complexity O(n) where n is the decoded response size. + * @pure false - performs HTTP IO and boundary decoding when the returned Effect is run. + * @effect openapi-fetch request plus synchronous Schema decoding. + * @invariant generated transport shapes are decoded before leaving the web API boundary. + * @precondition schema matches the endpoint success response contract. + * @postcondition success contains the schema-decoded DTO expected by UI code. + * @complexity O(n)/O(n) where n is the decoded response size. * @throws Never; failures are returned in the Effect error channel. */ -// CHANGE: compose generated transport typing with existing Schema DTO boundaries. -// WHY: generated OpenAPI types encode optional/default fields differently than current UI contracts. -// QUOTE(ТЗ): "использовать openapi-fetch на фронте для удобства" -// REF: user-message-2026-06-18-openapi-fetch -// SOURCE: https://openapi-ts.dev/openapi-fetch/ -// FORMAT THEOREM: decode(schema, response.data) = success(a) -> exported(a). -// PURITY: SHELL -// EFFECT: Effect -// INVARIANT: only schema-decoded values leave the API boundary. -// COMPLEXITY: O(n)/O(n) -export const openApiJsonSchema = ( - schema: Schema.Schema, - request: OpenApiRequest -): Effect.Effect => - openApiJson(request).pipe( - Effect.flatMap((data) => decodeSchema(schema, data)) - ) +export const openApiJsonSchema = openApiRuntime.openApiJsonSchema /** - * Executes a typed OpenAPI request whose successful response has no body. - * - * @param request - Deferred typed openapi-fetch request. - * @returns Effect that succeeds with void for 2xx/3xx empty responses. + * Executes a docker-git OpenAPI request whose success response has no body. * - * @pure false - performs browser HTTP IO when the Effect is run. - * @effect Network request via openapi-fetch wrapped by Effect.tryPromise. - * @invariant only response status determines success for empty endpoints. - * @precondition request targets an endpoint whose OpenAPI success response has no content. - * @postcondition successful Effect returns void and never exposes transport details. - * @complexity O(n) local response rendering where n is the error payload size. + * @pure false - performs HTTP IO when the returned Effect is run. + * @effect openapi-fetch request wrapped in Effect. + * @invariant only the HTTP success status determines the void success branch. + * @precondition request targets an endpoint whose successful response has no content. + * @postcondition success returns void without exposing transport details. + * @complexity O(n)/O(n) for error rendering, O(1)/O(1) on local success handling. * @throws Never; failures are returned in the Effect error channel. */ -// CHANGE: provide an Effect wrapper for generated empty-response endpoints. -// WHY: old requestText(...).asVoid call sites need a typed OpenAPI equivalent. -// QUOTE(ТЗ): "использовать openapi-fetch на фронте для удобства" -// REF: user-message-2026-06-18-openapi-fetch -// SOURCE: https://openapi-ts.dev/openapi-fetch/ -// FORMAT THEOREM: response.ok -> success(void); !response.ok -> failure(message). -// PURITY: SHELL -// EFFECT: Effect -// INVARIANT: no Promise escapes this module. -// COMPLEXITY: O(n)/O(n) for error rendering, O(1)/O(1) on successful local processing. -export const openApiVoid = ( - request: OpenApiRequest -): Effect.Effect => - runOpenApi(request).pipe( - Effect.flatMap(({ error, response }) => - response.ok - ? Option.match(Option.fromNullable(error), { - onNone: () => Effect.void, - onSome: (apiError) => failRenderedOpenApiError(response, apiError) - }) - : failRenderedOpenApiError(response, error) - ) - ) +export const openApiVoid = openApiRuntime.openApiVoid diff --git a/packages/openapi/package.json b/packages/openapi/package.json index 0f980109..0e748a23 100644 --- a/packages/openapi/package.json +++ b/packages/openapi/package.json @@ -8,10 +8,16 @@ "types": "./src/index.ts", "exports": { ".": { - "types": "./src/index.ts" + "types": "./src/index.ts", + "import": "./src/index.ts" + }, + "./client": { + "types": "./src/client.ts", + "import": "./src/client.ts" }, "./openapi-paths": { - "types": "./src/openapi-paths.ts" + "types": "./src/openapi-paths.ts", + "import": "./src/openapi-paths.ts" }, "./openapi.json": "./openapi.json" }, @@ -20,6 +26,11 @@ "generate": "bun ../../scripts/write-openapi.ts && openapi-typescript openapi.json -o src/openapi-paths.ts", "typecheck": "tsc --noEmit -p tsconfig.json" }, + "dependencies": { + "@effect/schema": "^0.75.5", + "effect": "^3.21.3", + "openapi-fetch": "^0.17.0" + }, "devDependencies": { "openapi-typescript": "^7.13.0", "typescript": "^6.0.3" diff --git a/packages/openapi/src/client.ts b/packages/openapi/src/client.ts new file mode 100644 index 00000000..208b9418 --- /dev/null +++ b/packages/openapi/src/client.ts @@ -0,0 +1,312 @@ +import * as ParseResult from "@effect/schema/ParseResult" +import type * as Schema from "@effect/schema/Schema" +import * as TreeFormatter from "@effect/schema/TreeFormatter" +import { Effect, Either, Option } from "effect" +import createClient, { type Client, type Middleware } from "openapi-fetch" + +import type { paths } from "./openapi-paths.js" + +export type DockerGitOpenApiClient = Client + +export type ApiTransportValue = + | undefined + | null + | boolean + | number + | string + | ReadonlyArray + | { readonly [key: string]: ApiTransportValue } + +export type ApiTransportError = ApiTransportValue | object + +export type OpenApiResponse = { + readonly data?: A + readonly error?: ApiTransportError + readonly response: Response +} + +export type OpenApiRequestResult = PromiseLike> + +export type OpenApiRequest = (client: DockerGitOpenApiClient) => OpenApiRequestResult + +export type DockerGitOpenApiRuntimeOptions = { + readonly resolveBaseUrl: () => string +} + +export type DockerGitOpenApiRuntime = { + readonly openApiJson: (request: OpenApiRequest) => Effect.Effect + readonly openApiJsonSchema: ( + schema: Schema.Schema, + request: OpenApiRequest + ) => Effect.Effect + readonly openApiVoid: (request: OpenApiRequest) => Effect.Effect +} + +type RunOpenApi = (request: OpenApiRequest) => Effect.Effect, string> + +const noCacheHeaders: Readonly> = { + accept: "application/json", + "cache-control": "no-cache, no-store, max-age=0", + pragma: "no-cache" +} + +const stringifyJson = (value: ApiTransportError): Effect.Effect => + Effect.try({ + try: () => JSON.stringify(value, null, 2), + catch: () => null + }) + +const safeJson = (value: ApiTransportError): Effect.Effect => + stringifyJson(value).pipe( + Effect.orElseSucceed(() => "unrenderable response payload") + ) + +const renderTransportValue = (value: ApiTransportError): Effect.Effect => { + if (typeof value === "string") { + return Effect.succeed(value) + } + if (typeof value === "object" && value !== null && "message" in value) { + const message = value["message"] + if (typeof message === "string") { + return Effect.succeed(message) + } + } + return safeJson(value) +} + +const renderOpenApiError = ( + response: Response, + error: ApiTransportError | undefined +): Effect.Effect => { + if (response.status === 429) { + return Effect.succeed("HTTP 429: tunnel or proxy rate limited the request. Retry or request a fresh tunnel URL.") + } + return error === undefined ? Effect.succeed(`HTTP ${response.status}`) : renderTransportValue(error) +} + +const noCacheGetMiddleware: Middleware = { + onRequest: ({ request }) => { + if (request.method !== "GET") { + return + } + const url = new URL(request.url) + url.searchParams.set("_", String(Date.now())) + return new Request(url, request) + } +} + +/** + * Creates a typed openapi-fetch client for the docker-git JSON REST API. + * + * @param baseUrl - Absolute API base URL. + * @returns Typed OpenAPI client with no-cache headers and GET cache-busting middleware. + * + * @pure false - constructs a browser/Fetch API HTTP client adapter. + * @effect none - client construction only; network IO happens when request methods are executed. + * @invariant client paths are constrained by generated DockerGit OpenAPI paths. + * @precondition baseUrl points at a docker-git API server or compatible proxy. + * @postcondition returned client sends no-cache headers on JSON requests. + * @complexity O(1)/O(1) + * @throws Never. + */ +export const createDockerGitOpenApiClient = (baseUrl: string): DockerGitOpenApiClient => { + const client = createClient({ + baseUrl, + headers: noCacheHeaders + }) + client.use(noCacheGetMiddleware) + return client +} + +/** + * Runs a typed OpenAPI request with a provided client through Effect. + * + * @param client - Typed docker-git OpenAPI client. + * @param request - Deferred openapi-fetch request. + * @returns Effect containing raw transport response data or a string failure. + * + * @pure false - executes Promise-producing openapi-fetch request when the Effect is run. + * @effect Promise interop isolated through Effect.tryPromise. + * @invariant no Promise escapes the function boundary. + * @precondition request was built against the same generated OpenAPI path map as client. + * @postcondition transport failures are represented in the Effect error channel. + * @complexity O(1)/O(1) excluding network and response body costs. + * @throws Never. + */ +export const runOpenApi = ( + client: DockerGitOpenApiClient, + request: OpenApiRequest +): Effect.Effect, string> => + Effect.tryPromise({ + try: () => request(client), + catch: String + }) + +const failRenderedOpenApiError = ( + response: Response, + error: ApiTransportError | undefined +): Effect.Effect => + renderOpenApiError(response, error).pipe( + Effect.flatMap((message) => Effect.fail(message)) + ) + +const openApiJsonWithRunner = ( + runner: RunOpenApi, + request: OpenApiRequest +): Effect.Effect => + runner(request).pipe( + Effect.flatMap(({ data, error, response }) => + Option.match(Option.fromNullable(error), { + onNone: () => + response.ok + ? Option.match(Option.fromNullable(data), { + onNone: () => Effect.fail(`HTTP ${response.status}: empty response`), + onSome: (value) => Effect.succeed(value) + }) + : failRenderedOpenApiError(response, error), + onSome: (apiError) => failRenderedOpenApiError(response, apiError) + }) + ) + ) + +const decodeSchema = (schema: Schema.Schema, value: ApiTransportValue): Effect.Effect => + Either.match(ParseResult.decodeUnknownEither(schema)(value), { + onLeft: (error) => Effect.fail(TreeFormatter.formatIssueSync(error)), + onRight: (decoded) => Effect.succeed(decoded) + }) + +const openApiJsonSchemaWithRunner = ( + runner: RunOpenApi, + schema: Schema.Schema, + request: OpenApiRequest +): Effect.Effect => + openApiJsonWithRunner(runner, request).pipe( + Effect.flatMap((data) => decodeSchema(schema, data)) + ) + +const openApiVoidWithRunner = ( + runner: RunOpenApi, + request: OpenApiRequest +): Effect.Effect => + runner(request).pipe( + Effect.flatMap(({ error, response }) => + response.ok + ? Option.match(Option.fromNullable(error), { + onNone: () => Effect.void, + onSome: (apiError) => failRenderedOpenApiError(response, apiError) + }) + : failRenderedOpenApiError(response, error) + ) + ) + +/** + * Executes a typed OpenAPI JSON request through a provided client. + * + * @param client - Typed docker-git OpenAPI client. + * @param request - Deferred typed openapi-fetch request. + * @returns Effect containing raw 2xx response data or a rendered API error. + * + * @pure false - performs browser HTTP IO when the Effect is run. + * @effect Network request via openapi-fetch wrapped by Effect.tryPromise. + * @invariant Promise interop is isolated inside this boundary. + * @precondition request uses a static path from generated OpenAPI paths. + * @postcondition successful Effect contains only the 2xx data branch as a transport value. + * @complexity O(n) local response rendering where n is the error payload size. + * @throws Never; failures are returned in the Effect error channel. + */ +export const openApiJson = ( + client: DockerGitOpenApiClient, + request: OpenApiRequest +): Effect.Effect => + openApiJsonWithRunner((nextRequest) => runOpenApi(client, nextRequest), request) + +/** + * Executes a typed OpenAPI request and decodes the data with an Effect Schema. + * + * @param client - Typed docker-git OpenAPI client. + * @param schema - Boundary decoder preserving the consumer DTO type. + * @param request - Deferred typed openapi-fetch request. + * @returns Effect containing schema-decoded response data. + * + * @pure false - performs browser HTTP IO and boundary decoding when the Effect is run. + * @effect openapi-fetch request plus synchronous Effect Schema decoding. + * @invariant transport typing comes from OpenAPI; exported data typing comes from Schema. + * @precondition schema matches the endpoint success response documented in DockerGitApi. + * @postcondition no generated optional/default representation leaks into existing consumers. + * @complexity O(n) where n is the decoded response size. + * @throws Never; failures are returned in the Effect error channel. + */ +export const openApiJsonSchema = ( + client: DockerGitOpenApiClient, + schema: Schema.Schema, + request: OpenApiRequest +): Effect.Effect => + openApiJsonSchemaWithRunner((nextRequest) => runOpenApi(client, nextRequest), schema, request) + +/** + * Executes a typed OpenAPI request whose successful response has no body. + * + * @param client - Typed docker-git OpenAPI client. + * @param request - Deferred typed openapi-fetch request. + * @returns Effect that succeeds with void for successful empty responses. + * + * @pure false - performs browser HTTP IO when the Effect is run. + * @effect Network request via openapi-fetch wrapped by Effect.tryPromise. + * @invariant only response status determines success for empty endpoints. + * @precondition request targets an endpoint whose OpenAPI success response has no content. + * @postcondition successful Effect returns void and never exposes transport details. + * @complexity O(n) local response rendering where n is the error payload size. + * @throws Never; failures are returned in the Effect error channel. + */ +export const openApiVoid = ( + client: DockerGitOpenApiClient, + request: OpenApiRequest +): Effect.Effect => + openApiVoidWithRunner((nextRequest) => runOpenApi(client, nextRequest), request) + +/** + * Creates reusable Effect helpers backed by a base URL resolver. + * + * @param options - Runtime configuration containing a base URL resolver. + * @returns OpenAPI helper set with a baseUrl-keyed client cache. + * + * @pure false - closes over mutable client cache for client reuse in a shell boundary. + * @effect none during construction; returned helpers perform HTTP IO when their Effects run. + * @invariant cache is keyed only by resolved baseUrl and invalidated on baseUrl change. + * @precondition resolveBaseUrl is deterministic for the duration of a single request Effect. + * @postcondition consumers can share OpenAPI helpers without importing app-specific base URL logic. + * @complexity O(1)/O(1) for client lookup, excluding request execution. + * @throws Never. + */ +export const makeDockerGitOpenApiRuntime = ( + options: DockerGitOpenApiRuntimeOptions +): DockerGitOpenApiRuntime => { + const clientCache: { + baseUrl: string | null + client: DockerGitOpenApiClient | null + } = { + baseUrl: null, + client: null + } + + const getOpenApiClient = (): DockerGitOpenApiClient => { + const baseUrl = options.resolveBaseUrl() + if (clientCache.client === null || clientCache.baseUrl !== baseUrl) { + clientCache.baseUrl = baseUrl + clientCache.client = createDockerGitOpenApiClient(baseUrl) + } + return clientCache.client + } + + const runRuntimeOpenApi = (request: OpenApiRequest): Effect.Effect, string> => + Effect.tryPromise({ + try: () => request(getOpenApiClient()), + catch: String + }) + + return { + openApiJson: (request) => openApiJsonWithRunner(runRuntimeOpenApi, request), + openApiJsonSchema: (schema, request) => openApiJsonSchemaWithRunner(runRuntimeOpenApi, schema, request), + openApiVoid: (request) => openApiVoidWithRunner(runRuntimeOpenApi, request) + } +} diff --git a/packages/openapi/src/index.ts b/packages/openapi/src/index.ts index 8f6f26d1..0bec554c 100644 --- a/packages/openapi/src/index.ts +++ b/packages/openapi/src/index.ts @@ -1,14 +1,27 @@ -/** - * Generated OpenAPI path map for the docker-git JSON REST API. - * - * @returns Type-only exports consumed by typed OpenAPI clients. - * - * @pure true - * @effect none - * @invariant all exported types are derived from packages/openapi/openapi.json. - * @precondition run `bun run --cwd packages/openapi generate` after changing the source HttpApi contract. - * @postcondition consumers depend on this package instead of importing generated files from app internals. - * @complexity O(1)/O(1) - * @throws Never - type-only module. - */ -export type { components, operations, paths, webhooks } from "./openapi-paths.js" +import type { components, operations, paths, webhooks } from "./openapi-paths.js" + +export { + createDockerGitOpenApiClient, + makeDockerGitOpenApiRuntime, + openApiJson, + openApiJsonSchema, + openApiVoid, + runOpenApi +} from "./client.js" +export type { + ApiTransportError, + ApiTransportValue, + DockerGitOpenApiClient, + DockerGitOpenApiRuntime, + DockerGitOpenApiRuntimeOptions, + OpenApiRequest, + OpenApiRequestResult, + OpenApiResponse +} from "./client.js" + +export type { components, operations, paths, webhooks } + +export type DockerGitOpenApiComponents = components +export type DockerGitOpenApiOperations = operations +export type DockerGitOpenApiPaths = paths +export type DockerGitOpenApiWebhooks = webhooks diff --git a/packages/openapi/tsconfig.json b/packages/openapi/tsconfig.json index 3f16f31c..b244d935 100644 --- a/packages/openapi/tsconfig.json +++ b/packages/openapi/tsconfig.json @@ -2,6 +2,7 @@ "extends": "../../tsconfig.base.json", "compilerOptions": { "rootDir": ".", + "lib": ["ES2023", "DOM"], "types": [] }, "include": ["src/**/*"], From a09a3ab3d2cee8e2fb794c809f39ea92c3cb245f Mon Sep 17 00:00:00 2001 From: skulidropek <66840575+skulidropek@users.noreply.github.com> Date: Thu, 18 Jun 2026 14:02:34 +0000 Subject: [PATCH 07/10] refactor(openapi): export public surface via barrels --- packages/openapi/src/index.ts | 29 ++--------------------------- 1 file changed, 2 insertions(+), 27 deletions(-) diff --git a/packages/openapi/src/index.ts b/packages/openapi/src/index.ts index 0bec554c..5f171cd1 100644 --- a/packages/openapi/src/index.ts +++ b/packages/openapi/src/index.ts @@ -1,27 +1,2 @@ -import type { components, operations, paths, webhooks } from "./openapi-paths.js" - -export { - createDockerGitOpenApiClient, - makeDockerGitOpenApiRuntime, - openApiJson, - openApiJsonSchema, - openApiVoid, - runOpenApi -} from "./client.js" -export type { - ApiTransportError, - ApiTransportValue, - DockerGitOpenApiClient, - DockerGitOpenApiRuntime, - DockerGitOpenApiRuntimeOptions, - OpenApiRequest, - OpenApiRequestResult, - OpenApiResponse -} from "./client.js" - -export type { components, operations, paths, webhooks } - -export type DockerGitOpenApiComponents = components -export type DockerGitOpenApiOperations = operations -export type DockerGitOpenApiPaths = paths -export type DockerGitOpenApiWebhooks = webhooks +export * from "./client.js" +export * from "./openapi-paths.js" From a0f08e50d27b10f51fcce4df448d0b7d681bbe30 Mon Sep 17 00:00:00 2001 From: skulidropek <66840575+skulidropek@users.noreply.github.com> Date: Thu, 18 Jun 2026 15:05:07 +0000 Subject: [PATCH 08/10] fix(api): align OpenAPI response contract --- package.json | 1 + packages/api/package.json | 1 + packages/api/src/api/openapi.ts | 43 +- packages/api/tests/openapi.test.ts | 86 + packages/openapi/openapi.json | 20469 +++++++++++++++++++----- packages/openapi/src/openapi-paths.ts | 8153 ++++++++-- 6 files changed, 22672 insertions(+), 6081 deletions(-) diff --git a/package.json b/package.json index 8ddeda9b..27e3c7cd 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "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/openapi generate", + "openapi:lint-contract": "bun run --cwd packages/api lint:openapi-contract", "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/package.json b/packages/api/package.json index b61ad57f..d2b9f357 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -16,6 +16,7 @@ "typecheck": "tsc --noEmit -p tsconfig.json", "lint": "eslint .", "lint:effect": "eslint --config eslint.effect-ts-check.config.mjs .", + "lint:openapi-contract": "vitest run tests/openapi.test.ts", "pretest": "bun run --cwd ../terminal build && bun run --cwd ../container build && bun run --cwd ../lib build", "test": "vitest run" }, diff --git a/packages/api/src/api/openapi.ts b/packages/api/src/api/openapi.ts index d078665a..4c4da351 100644 --- a/packages/api/src/api/openapi.ts +++ b/packages/api/src/api/openapi.ts @@ -7,6 +7,7 @@ import * as Schema from "effect/Schema" import { ActiveProjectTerminalSessionRequestSchema, + AgentSessionSchema, ApplyAllRequestSchema, ApplyProjectRequestSchema, AuthMenuRequestSchema, @@ -47,7 +48,9 @@ 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 OkResponseSchema = Schema.Struct({ + ok: Schema.Literal(true) +}) export const HealthResponseSchema = Schema.Struct({ cwd: Schema.String, @@ -301,8 +304,7 @@ export const ProjectAuthSnapshotSchema = Schema.Struct({ grokAuthEntries: Schema.optionalWith(Schema.Number, { default: () => 0 }), grokAuthPath: Schema.optionalWith(Schema.String, { default: () => "" }), projectDir: Schema.String, - projectName: Schema.String, - totalEntries: Schema.Number + projectName: Schema.String }) export const ProjectAuthSnapshotResponseSchema = Schema.Struct({ @@ -338,6 +340,11 @@ export const ProjectTerminalSessionResponseSchema = Schema.Struct({ session: TerminalSessionSchema }) +export const ActiveProjectTerminalSessionResponseSchema = Schema.Struct({ + ok: OptionalOkSchema, + session: TerminalSessionSchema +}) + export const TerminalSessionLookupResponseSchema = Schema.Struct({ projectDisplayName: Schema.String, projectKey: Schema.String, @@ -442,11 +449,13 @@ export const ContainerTaskSchema = Schema.Struct({ }) export const ContainerTaskSnapshotSchema = Schema.Struct({ + agents: Schema.Array(AgentSessionSchema), containerName: Schema.String, generatedAt: Schema.String, projectId: Schema.String, sshConnections: Schema.Number, - tasks: Schema.Array(ContainerTaskSchema) + tasks: Schema.Array(ContainerTaskSchema), + terminalSessions: Schema.Array(TerminalSessionSchema) }) export const ContainerTaskSnapshotResponseSchema = Schema.Struct({ @@ -499,12 +508,12 @@ const ProjectsGroup = HttpApiGroup.make("projects") .add( endpoint.post("applyAllProjects", "/projects/apply-all") .setPayload(ApplyAllRequestSchema) - .addSuccess(EmptyResponseSchema) + .addSuccess(OkResponseSchema) ) - .add(endpoint.post("downAllProjects", "/projects/down-all").addSuccess(EmptyResponseSchema)) + .add(endpoint.post("downAllProjects", "/projects/down-all").addSuccess(OkResponseSchema)) .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.del("deleteProject")`/projects/${ProjectIdParam}`.addSuccess(OkResponseSchema)) + .add(endpoint.post("downProject")`/projects/${ProjectIdParam}/down`.addSuccess(OkResponseSchema)) .add( endpoint.post("applyProject")`/projects/${ProjectIdParam}/apply` .setPayload(ApplyProjectRequestSchema) @@ -529,7 +538,7 @@ const ProjectPortsGroup = HttpApiGroup.make("projectPorts") ) .add( endpoint.del("deleteProjectPort")`/projects/${ProjectIdParam}/ports/${TargetPortParam}` - .addSuccess(EmptyResponseSchema) + .addSuccess(OkResponseSchema) ) const ProjectBrowserGroup = HttpApiGroup.make("projectBrowser") @@ -551,7 +560,7 @@ const ProjectDatabasesGroup = HttpApiGroup.make("projectDatabases") ) .add( endpoint.del("deleteDatabaseProfile")`/projects/${ProjectIdParam}/databases/profiles/${ProfileIdParam}` - .addSuccess(EmptyResponseSchema) + .addSuccess(OkResponseSchema) ) .add( endpoint.post("exposeDatabaseProfile")`/projects/${ProjectIdParam}/databases/profiles/${ProfileIdParam}/expose` @@ -559,7 +568,7 @@ const ProjectDatabasesGroup = HttpApiGroup.make("projectDatabases") ) .add( endpoint.del("deleteDatabaseForward")`/projects/${ProjectIdParam}/databases/profiles/${ProfileIdParam}/expose` - .addSuccess(EmptyResponseSchema) + .addSuccess(OkResponseSchema) ) .add( endpoint.get("listDatabaseForwards")`/projects/${ProjectIdParam}/databases/forwards` @@ -677,16 +686,16 @@ const TerminalGroup = HttpApiGroup.make("terminal") ) .add( endpoint.del("deleteTerminalByKey")`/projects/by-key/${ProjectKeyParam}/terminal-sessions/${SessionIdParam}` - .addSuccess(EmptyResponseSchema) + .addSuccess(OkResponseSchema) ) .add( endpoint.put("setActiveTerminalByKey")`/projects/by-key/${ProjectKeyParam}/terminal-sessions/active` .setPayload(ActiveProjectTerminalSessionRequestSchema) - .addSuccess(ProjectTerminalSessionResponseSchema) + .addSuccess(ActiveProjectTerminalSessionResponseSchema) ) .add(endpoint.get("lookupTerminal")`/terminal-sessions/${SessionIdParam}`.addSuccess(TerminalSessionLookupResponseSchema)) .add( - endpoint.del("deleteAuthTerminal")`/auth/terminal-sessions/${SessionIdParam}`.addSuccess(EmptyResponseSchema) + endpoint.del("deleteAuthTerminal")`/auth/terminal-sessions/${SessionIdParam}`.addSuccess(OkResponseSchema) ) const PromptsGroup = HttpApiGroup.make("prompts") @@ -719,7 +728,7 @@ const TasksGroup = HttpApiGroup.make("tasks") .setUrlParams(QueryIncludeDefaultSchema) .addSuccess(ContainerTaskSnapshotResponseSchema) ) - .add(endpoint.post("stopTask")`/projects/${ProjectIdParam}/tasks/${PidParam}/stop`.addSuccess(EmptyResponseSchema)) + .add(endpoint.post("stopTask")`/projects/${ProjectIdParam}/tasks/${PidParam}/stop`.addSuccess(OkResponseSchema)) .add( endpoint.get("taskLogs")`/projects/${ProjectIdParam}/tasks/${PidParam}/logs` .setUrlParams(QueryLinesSchema) @@ -740,6 +749,10 @@ export const DockerGitApi = HttpApi.make("docker-git") .annotate(OpenApi.Version, "1.0.0") .annotate(OpenApi.Description, "Effect contract for docker-git JSON REST endpoints.") .addError(ApiErrorResponseSchema, { status: 400 }) + .addError(ApiErrorResponseSchema, { status: 401 }) + .addError(ApiErrorResponseSchema, { status: 404 }) + .addError(ApiErrorResponseSchema, { status: 409 }) + .addError(ApiErrorResponseSchema, { status: 500 }) .add(CoreGroup) .add(ProjectsGroup) .add(ProjectPortsGroup) diff --git a/packages/api/tests/openapi.test.ts b/packages/api/tests/openapi.test.ts index e7e73f71..44b3632b 100644 --- a/packages/api/tests/openapi.test.ts +++ b/packages/api/tests/openapi.test.ts @@ -3,6 +3,9 @@ import { Effect } from "effect" import { buildDockerGitOpenApi } from "../src/api/openapi.js" +const documentedMethods = ["delete", "get", "post", "put"] as const +const commonErrorStatuses = ["400", "401", "404", "409", "500"] + describe("openapi contract", () => { it.effect("documents generated REST paths from the Effect HttpApi contract", () => Effect.sync(() => { @@ -63,4 +66,87 @@ describe("openapi contract", () => { expect(serializedBadRequestSchema).toContain("\"command\":{\"type\":\"string\"") expect(serializedBadRequestSchema).not.toContain("\"required\":[\"error\",\"message\"]") })) + + it.effect("documents common API error statuses for every JSON REST operation", () => + Effect.sync(() => { + const spec = buildDockerGitOpenApi() + const paths = spec.paths ?? {} + + for (const [path, item] of Object.entries(paths)) { + for (const method of documentedMethods) { + const responses = item[method]?.responses + if (responses === undefined) { + continue + } + + expect(Object.keys(responses), `${method.toUpperCase()} ${path}`).toEqual( + expect.arrayContaining(commonErrorStatuses) + ) + } + } + })) + + it.effect("documents ok-only HTTP handlers as 200 JSON responses", () => + Effect.sync(() => { + const spec = buildDockerGitOpenApi() + const paths = spec.paths ?? {} + const okOnlyOperations = [ + { method: "post", path: "/projects/apply-all" }, + { method: "post", path: "/projects/down-all" }, + { method: "delete", path: "/projects/{projectId}" }, + { method: "post", path: "/projects/{projectId}/down" }, + { method: "delete", path: "/projects/{projectId}/ports/{targetPort}" }, + { method: "delete", path: "/projects/{projectId}/databases/profiles/{profileId}" }, + { method: "delete", path: "/projects/{projectId}/databases/profiles/{profileId}/expose" }, + { method: "delete", path: "/projects/by-key/{projectKey}/terminal-sessions/{sessionId}" }, + { method: "delete", path: "/auth/terminal-sessions/{sessionId}" }, + { method: "post", path: "/projects/{projectId}/tasks/{pid}/stop" } + ] as const + + for (const operation of okOnlyOperations) { + const responses = paths[operation.path]?.[operation.method]?.responses ?? {} + const serializedSuccessSchema = JSON.stringify(responses["200"] ?? {}) + + expect(responses["200"], `${operation.method.toUpperCase()} ${operation.path}`).toBeDefined() + expect(responses["204"], `${operation.method.toUpperCase()} ${operation.path}`).toBeUndefined() + expect(serializedSuccessSchema).toContain("\"required\":[\"ok\"]") + expect(serializedSuccessSchema).toContain("\"ok\":{\"type\":\"boolean\",\"enum\":[true]}") + } + })) + + it.effect("documents project auth snapshots without nonexistent totalEntries", () => + Effect.sync(() => { + const spec = buildDockerGitOpenApi() + const serializedProjectAuthSchema = JSON.stringify( + spec.paths?.["/projects/{projectId}/auth/menu"]?.get?.responses?.["200"] ?? {} + ) + + expect(serializedProjectAuthSchema).toContain("\"projectName\":{\"type\":\"string\"") + expect(serializedProjectAuthSchema).not.toContain("\"totalEntries\"") + })) + + it.effect("documents active terminal response ok envelope", () => + Effect.sync(() => { + const spec = buildDockerGitOpenApi() + const serializedActiveTerminalSchema = JSON.stringify( + spec.paths?.["/projects/by-key/{projectKey}/terminal-sessions/active"]?.put?.responses?.["200"] ?? {} + ) + + expect(serializedActiveTerminalSchema).toContain("\"required\":[\"session\"]") + expect(serializedActiveTerminalSchema).toContain("\"ok\":{\"type\":\"boolean\"") + expect(serializedActiveTerminalSchema).toContain("\"session\":{\"type\":\"object\"") + })) + + it.effect("documents task snapshot terminal sessions and agents", () => + Effect.sync(() => { + const spec = buildDockerGitOpenApi() + const serializedTaskSchema = JSON.stringify( + spec.paths?.["/projects/{projectId}/tasks"]?.get?.responses?.["200"] ?? {} + ) + + expect(serializedTaskSchema).toContain("\"required\":[\"agents\"") + expect(serializedTaskSchema).toContain("\"terminalSessions\"") + expect(serializedTaskSchema).toContain("\"provider\":{\"type\":\"string\"") + expect(serializedTaskSchema).toContain("\"sshCommand\":{\"type\":\"string\"") + })) }) diff --git a/packages/openapi/openapi.json b/packages/openapi/openapi.json index 02f3c51a..1e797e52 100644 --- a/packages/openapi/openapi.json +++ b/packages/openapi/openapi.json @@ -101,6 +101,174 @@ } } } + }, + "401": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "404": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "409": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "500": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } } } } @@ -254,6 +422,174 @@ } } } + }, + "401": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "404": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "409": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "500": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } } } }, @@ -492,14 +828,182 @@ } } } - } - }, - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [], + }, + "401": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "404": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "409": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "500": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [], "properties": { "repoUrl": { "type": "string" @@ -645,89 +1149,27 @@ "parameters": [], "security": [], "responses": { - "204": { - "description": "Success" - }, - "400": { - "description": "The request did not match the expected schema", + "200": { + "description": "Success", "content": { "application/json": { "schema": { - "anyOf": [ - { - "$ref": "#/components/schemas/HttpApiDecodeError" - }, - { - "type": "object", - "required": [ - "error" - ], - "properties": { - "error": { - "type": "object", - "required": [ - "message", - "type" - ], - "properties": { - "command": { - "type": "string" - }, - "details": { - "$id": "/schemas/unknown", - "title": "unknown" - }, - "message": { - "type": "string" - }, - "provider": { - "type": "string" - }, - "type": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false + "type": "object", + "required": [ + "ok" + ], + "properties": { + "ok": { + "type": "boolean", + "enum": [ + true + ] } - ] + }, + "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", @@ -777,166 +1219,122 @@ } } } - } - } - } - }, - "/projects/{projectId}": { - "get": { - "tags": [ - "projects" - ], - "operationId": "projects.getProject", - "parameters": [ - { - "name": "projectId", - "in": "path", - "schema": { - "type": "string" - }, - "required": true - } - ], - "security": [], - "responses": { - "200": { - "description": "Success", + }, + "401": { + "description": "Error", "content": { "application/json": { "schema": { "type": "object", "required": [ - "project" + "error" ], "properties": { - "ok": { - "type": "boolean" - }, - "project": { + "error": { "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" + "message", + "type" ], "properties": { - "clonedOnHostname": { - "type": "string" - }, - "containerName": { - "type": "string" - }, - "displayName": { - "type": "string" - }, - "id": { + "command": { "type": "string" }, - "projectKey": { - "type": "string" + "details": { + "$id": "/schemas/unknown", + "title": "unknown" }, - "repoRef": { + "message": { "type": "string" }, - "repoUrl": { + "provider": { "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": { "type": "string" - }, - "authorizedKeysExists": { - "type": "boolean" - }, - "authorizedKeysPath": { + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "404": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { "type": "string" }, - "codexAuthPath": { - "type": "string" + "details": { + "$id": "/schemas/unknown", + "title": "unknown" }, - "codexHome": { + "message": { "type": "string" }, - "envGlobalPath": { + "provider": { "type": "string" }, - "envProjectPath": { + "type": { "type": "string" - }, - "gpu": { - "type": "string", - "enum": [ - "none", - "all" - ] - }, - "projectDir": { + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "409": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { "type": "string" }, - "serviceName": { - "type": "string" + "details": { + "$id": "/schemas/unknown", + "title": "unknown" }, - "sshCommand": { + "message": { "type": "string" }, - "sshPort": { - "type": "number" - }, - "sshUser": { + "provider": { "type": "string" }, - "targetDir": { + "type": { "type": "string" } }, @@ -948,76 +1346,98 @@ } } }, - "400": { - "description": "The request did not match the expected schema", + "500": { + "description": "Error", "content": { "application/json": { "schema": { - "anyOf": [ - { - "$ref": "#/components/schemas/HttpApiDecodeError" - }, - { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { "type": "object", "required": [ - "error" + "message", + "type" ], "properties": { - "error": { - "type": "object", - "required": [ - "message", - "type" - ], - "properties": { - "command": { - "type": "string" - }, - "details": { - "$id": "/schemas/unknown", - "title": "unknown" - }, - "message": { - "type": "string" - }, - "provider": { - "type": "string" - }, - "type": { - "type": "string" - } - }, - "additionalProperties": false + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" } }, "additionalProperties": false } - ] + }, + "additionalProperties": false } } } } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [], + "properties": { + "activeOnly": { + "type": "boolean" + } + }, + "additionalProperties": false + } + } + }, + "required": true } - }, - "delete": { + } + }, + "/projects/down-all": { + "post": { "tags": [ "projects" ], - "operationId": "projects.deleteProject", - "parameters": [ - { - "name": "projectId", - "in": "path", - "schema": { - "type": "string" - }, - "required": true - } - ], + "operationId": "projects.downAllProjects", + "parameters": [], "security": [], "responses": { - "204": { - "description": "Success" + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "ok" + ], + "properties": { + "ok": { + "type": "boolean", + "enum": [ + true + ] + } + }, + "additionalProperties": false + } + } + } }, "400": { "description": "The request did not match the expected schema", @@ -1067,76 +1487,171 @@ } } } - } - } - } - }, - "/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", + "401": { + "description": "Error", "content": { "application/json": { "schema": { - "anyOf": [ - { - "$ref": "#/components/schemas/HttpApiDecodeError" - }, - { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { "type": "object", "required": [ - "error" + "message", + "type" ], "properties": { - "error": { - "type": "object", - "required": [ - "message", - "type" - ], - "properties": { - "command": { - "type": "string" - }, - "details": { - "$id": "/schemas/unknown", - "title": "unknown" - }, - "message": { - "type": "string" - }, - "provider": { - "type": "string" - }, - "type": { - "type": "string" - } - }, - "additionalProperties": false + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" } }, "additionalProperties": false } - ] + }, + "additionalProperties": false + } + } + } + }, + "404": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "409": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "500": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false } } } @@ -1144,12 +1659,12 @@ } } }, - "/projects/{projectId}/apply": { - "post": { + "/projects/{projectId}": { + "get": { "tags": [ "projects" ], - "operationId": "projects.applyProject", + "operationId": "projects.getProject", "parameters": [ { "name": "projectId", @@ -1359,199 +1874,164 @@ } } } - } - }, - "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", + "401": { + "description": "Error", "content": { "application/json": { "schema": { "type": "object", "required": [ - "project" + "error" ], "properties": { - "ok": { - "type": "boolean" - }, - "project": { + "error": { "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" + "message", + "type" ], "properties": { - "clonedOnHostname": { - "type": "string" - }, - "containerName": { - "type": "string" - }, - "displayName": { + "command": { "type": "string" }, - "id": { - "type": "string" + "details": { + "$id": "/schemas/unknown", + "title": "unknown" }, - "projectKey": { + "message": { "type": "string" }, - "repoRef": { + "provider": { "type": "string" }, - "repoUrl": { + "type": { "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": { + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "404": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { "type": "string" }, - "authorizedKeysExists": { - "type": "boolean" - }, - "authorizedKeysPath": { - "type": "string" + "details": { + "$id": "/schemas/unknown", + "title": "unknown" }, - "codexAuthPath": { + "message": { "type": "string" }, - "codexHome": { + "provider": { "type": "string" }, - "envGlobalPath": { + "type": { "type": "string" - }, - "envProjectPath": { + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "409": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { "type": "string" }, - "gpu": { - "type": "string", - "enum": [ - "none", - "all" - ] + "details": { + "$id": "/schemas/unknown", + "title": "unknown" }, - "projectDir": { + "message": { "type": "string" }, - "serviceName": { + "provider": { "type": "string" }, - "sshCommand": { + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "500": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { "type": "string" }, - "sshPort": { - "type": "number" + "details": { + "$id": "/schemas/unknown", + "title": "unknown" }, - "sshUser": { + "message": { "type": "string" }, - "targetDir": { + "provider": { + "type": "string" + }, + "type": { "type": "string" } }, @@ -1562,6 +2042,47 @@ } } } + } + } + }, + "delete": { + "tags": [ + "projects" + ], + "operationId": "projects.deleteProject", + "parameters": [ + { + "name": "projectId", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "ok" + ], + "properties": { + "ok": { + "type": "boolean", + "enum": [ + true + ] + } + }, + "additionalProperties": false + } + } + } }, "400": { "description": "The request did not match the expected schema", @@ -1611,186 +2132,164 @@ } } } - } - }, - "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", + "401": { + "description": "Error", "content": { "application/json": { "schema": { "type": "object", "required": [ - "project" + "error" ], "properties": { - "ok": { - "type": "boolean" - }, - "project": { + "error": { "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" + "message", + "type" ], "properties": { - "clonedOnHostname": { - "type": "string" - }, - "containerName": { - "type": "string" - }, - "displayName": { + "command": { "type": "string" }, - "id": { - "type": "string" + "details": { + "$id": "/schemas/unknown", + "title": "unknown" }, - "projectKey": { + "message": { "type": "string" }, - "repoRef": { + "provider": { "type": "string" }, - "repoUrl": { + "type": { "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": { + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "404": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { "type": "string" }, - "authorizedKeysExists": { - "type": "boolean" - }, - "authorizedKeysPath": { - "type": "string" + "details": { + "$id": "/schemas/unknown", + "title": "unknown" }, - "codexAuthPath": { + "message": { "type": "string" }, - "codexHome": { + "provider": { "type": "string" }, - "envGlobalPath": { + "type": { "type": "string" - }, - "envProjectPath": { + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "409": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { "type": "string" }, - "gpu": { - "type": "string", - "enum": [ - "none", - "all" - ] + "details": { + "$id": "/schemas/unknown", + "title": "unknown" }, - "projectDir": { + "message": { "type": "string" }, - "serviceName": { + "provider": { "type": "string" }, - "sshCommand": { + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "500": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { "type": "string" }, - "sshPort": { - "type": "number" + "details": { + "$id": "/schemas/unknown", + "title": "unknown" }, - "sshUser": { + "message": { "type": "string" }, - "targetDir": { + "provider": { + "type": "string" + }, + "type": { "type": "string" } }, @@ -1801,6 +2300,49 @@ } } } + } + } + } + }, + "/projects/{projectId}/down": { + "post": { + "tags": [ + "projects" + ], + "operationId": "projects.downProject", + "parameters": [ + { + "name": "projectId", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + } + ], + "security": [], + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "ok" + ], + "properties": { + "ok": { + "type": "boolean", + "enum": [ + true + ] + } + }, + "additionalProperties": false + } + } + } }, "400": { "description": "The request did not match the expected schema", @@ -1850,86 +2392,254 @@ } } } - } - } - } - }, - "/projects/{projectId}/suspend": { - "post": { - "tags": [ - "projects" - ], - "operationId": "projects.suspendProject", - "parameters": [ - { - "name": "projectId", - "in": "path", - "schema": { - "type": "string" - }, - "required": true - } - ], - "security": [], - "responses": { - "200": { - "description": "Success", + }, + "401": { + "description": "Error", "content": { "application/json": { "schema": { "type": "object", "required": [ - "project" + "error" ], "properties": { - "ok": { - "type": "boolean" - }, - "project": { + "error": { "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" + "message", + "type" ], "properties": { - "clonedOnHostname": { + "command": { "type": "string" }, - "containerName": { + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { "type": "string" }, - "displayName": { + "provider": { "type": "string" }, - "id": { + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "404": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { "type": "string" }, - "projectKey": { + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { "type": "string" }, - "repoRef": { + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "409": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "500": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "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": { @@ -2069,40 +2779,42 @@ } } } - } - } - } - }, - "/projects/{projectId}/ps": { - "get": { - "tags": [ - "projects" - ], - "operationId": "projects.projectPs", - "parameters": [ - { - "name": "projectId", - "in": "path", - "schema": { - "type": "string" - }, - "required": true - } - ], - "security": [], - "responses": { - "200": { - "description": "Success", + }, + "401": { + "description": "Error", "content": { "application/json": { "schema": { "type": "object", "required": [ - "output" + "error" ], "properties": { - "output": { - "type": "string" + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -2110,64 +2822,174 @@ } } }, - "400": { - "description": "The request did not match the expected schema", + "404": { + "description": "Error", "content": { "application/json": { "schema": { - "anyOf": [ - { - "$ref": "#/components/schemas/HttpApiDecodeError" - }, - { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { "type": "object", "required": [ - "error" + "message", + "type" ], "properties": { - "error": { - "type": "object", - "required": [ - "message", - "type" - ], - "properties": { - "command": { - "type": "string" - }, - "details": { - "$id": "/schemas/unknown", - "title": "unknown" - }, - "message": { - "type": "string" - }, - "provider": { - "type": "string" - }, - "type": { - "type": "string" - } - }, - "additionalProperties": false + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" } }, "additionalProperties": false } - ] + }, + "additionalProperties": false } } } - } - } - } - }, - "/projects/{projectId}/logs": { - "get": { - "tags": [ + }, + "409": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "500": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "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.projectLogs", + "operationId": "projects.upProject", "parameters": [ { "name": "projectId", @@ -2187,11 +3009,141 @@ "schema": { "type": "object", "required": [ - "output" + "project" ], "properties": { - "output": { - "type": "string" + "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 @@ -2247,112 +3199,42 @@ } } } - } - } - } - }, - "/projects/{projectId}/ports": { - "get": { - "tags": [ - "projectPorts" - ], - "operationId": "projectPorts.listProjectPorts", - "parameters": [ - { - "name": "projectId", - "in": "path", - "schema": { - "type": "string" - }, - "required": true - } - ], - "security": [], - "responses": { - "200": { - "description": "Success", + }, + "401": { + "description": "Error", "content": { "application/json": { "schema": { "type": "object", "required": [ - "forwards" + "error" ], "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" - } + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" }, - "additionalProperties": false - } + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -2360,151 +3242,79 @@ } } }, - "400": { - "description": "The request did not match the expected schema", + "404": { + "description": "Error", "content": { "application/json": { "schema": { - "anyOf": [ - { - "$ref": "#/components/schemas/HttpApiDecodeError" - }, - { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { "type": "object", "required": [ - "error" + "message", + "type" ], "properties": { - "error": { - "type": "object", - "required": [ - "message", - "type" - ], - "properties": { - "command": { - "type": "string" - }, - "details": { - "$id": "/schemas/unknown", - "title": "unknown" - }, - "message": { - "type": "string" - }, - "provider": { - "type": "string" - }, - "type": { - "type": "string" - } - }, - "additionalProperties": false + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" } }, "additionalProperties": false } - ] + }, + "additionalProperties": false } } } - } - } - }, - "post": { - "tags": [ - "projectPorts" - ], - "operationId": "projectPorts.createProjectPort", - "parameters": [ - { - "name": "projectId", - "in": "path", - "schema": { - "type": "string" - }, - "required": true - } - ], - "security": [], - "responses": { - "201": { - "description": "Success", + }, + "409": { + "description": "Error", "content": { "application/json": { "schema": { "type": "object", "required": [ - "forward" + "error" ], "properties": { - "forward": { + "error": { "type": "object", "required": [ - "bindHost", - "containerName", - "createdAt", - "hostPort", - "id", - "projectId", - "projectKey", - "proxyPath", - "publicHost", - "status", - "targetContainerName", - "targetPort", - "url" + "message", + "type" ], "properties": { - "bindHost": { - "type": "string" - }, - "containerName": { + "command": { "type": "string" }, - "createdAt": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ] - }, - "hostPort": { - "type": "number" - }, - "id": { - "type": "string" - }, - "projectId": { - "type": "string" - }, - "projectKey": { - "type": "string" - }, - "proxyPath": { - "type": "string" + "details": { + "$id": "/schemas/unknown", + "title": "unknown" }, - "publicHost": { + "message": { "type": "string" }, - "status": { - "type": "string", - "enum": [ - "running", - "stopped", - "unknown" - ] - }, - "targetContainerName": { + "provider": { "type": "string" }, - "targetPort": { - "type": "number" - }, - "url": { + "type": { "type": "string" } }, @@ -2516,51 +3326,44 @@ } } }, - "400": { - "description": "The request did not match the expected schema", + "500": { + "description": "Error", "content": { "application/json": { "schema": { - "anyOf": [ - { - "$ref": "#/components/schemas/HttpApiDecodeError" - }, - { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { "type": "object", "required": [ - "error" + "message", + "type" ], "properties": { - "error": { - "type": "object", - "required": [ - "message", - "type" - ], - "properties": { - "command": { - "type": "string" - }, - "details": { - "$id": "/schemas/unknown", - "title": "unknown" - }, - "message": { - "type": "string" - }, - "provider": { - "type": "string" - }, - "type": { - "type": "string" - } - }, - "additionalProperties": false + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" } }, "additionalProperties": false } - ] + }, + "additionalProperties": false } } } @@ -2571,15 +3374,13 @@ "application/json": { "schema": { "type": "object", - "required": [ - "targetPort" - ], + "required": [], "properties": { - "hostPort": { - "type": "number" + "authorizedKeysContents": { + "type": "string" }, - "targetPort": { - "type": "number" + "useManagedAuthorizedKeys": { + "type": "boolean" } }, "additionalProperties": false @@ -2590,93 +3391,12 @@ } } }, - "/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" - ], - "properties": { - "error": { - "type": "object", - "required": [ - "message", - "type" - ], - "properties": { - "command": { - "type": "string" - }, - "details": { - "$id": "/schemas/unknown", - "title": "unknown" - }, - "message": { - "type": "string" - }, - "provider": { - "type": "string" - }, - "type": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - } - ] - } - } - } - } - } - } - }, - "/projects/{projectId}/browser": { - "get": { + "/projects/{projectId}/resume": { + "post": { "tags": [ - "projectBrowser" + "projects" ], - "operationId": "projectBrowser.readProjectBrowser", + "operationId": "projects.resumeProject", "parameters": [ { "name": "projectId", @@ -2696,51 +3416,138 @@ "schema": { "type": "object", "required": [ - "browser" + "project" ], "properties": { - "browser": { + "ok": { + "type": "boolean" + }, + "project": { "type": "object", "required": [ - "cdpPath", - "cdpUrl", "containerName", - "noVncPath", - "noVncUrl", - "projectId", + "displayName", + "id", "projectKey", - "status" + "repoRef", + "repoUrl", + "sshSessions", + "startedAtEpochMs", + "startedAtIso", + "status", + "statusLabel", + "authorizedKeysExists", + "authorizedKeysPath", + "codexAuthPath", + "codexHome", + "envGlobalPath", + "envProjectPath", + "gpu", + "projectDir", + "serviceName", + "sshCommand", + "sshPort", + "sshUser", + "targetDir" ], "properties": { - "cdpPath": { + "clonedOnHostname": { "type": "string" }, - "cdpUrl": { + "containerName": { "type": "string" }, - "containerName": { + "displayName": { "type": "string" }, - "noVncPath": { + "id": { "type": "string" }, - "noVncUrl": { + "projectKey": { "type": "string" }, - "projectId": { + "repoRef": { "type": "string" }, - "projectKey": { + "repoUrl": { "type": "string" }, + "sshSessions": { + "type": "number" + }, + "startedAtEpochMs": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ] + }, + "startedAtIso": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, "status": { "type": "string", "enum": [ "running", "stopped", - "missing", "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 @@ -2799,19 +3606,187 @@ } } } - } - } - } - }, - "/projects/{projectId}/browser/start": { - "post": { - "tags": [ - "projectBrowser" - ], - "operationId": "projectBrowser.startProjectBrowser", - "parameters": [ - { - "name": "projectId", + }, + "401": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "404": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "409": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "500": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + } + } + } + }, + "/projects/{projectId}/suspend": { + "post": { + "tags": [ + "projects" + ], + "operationId": "projects.suspendProject", + "parameters": [ + { + "name": "projectId", "in": "path", "schema": { "type": "string" @@ -2828,51 +3803,138 @@ "schema": { "type": "object", "required": [ - "browser" + "project" ], "properties": { - "browser": { + "ok": { + "type": "boolean" + }, + "project": { "type": "object", "required": [ - "cdpPath", - "cdpUrl", "containerName", - "noVncPath", - "noVncUrl", - "projectId", + "displayName", + "id", "projectKey", - "status" + "repoRef", + "repoUrl", + "sshSessions", + "startedAtEpochMs", + "startedAtIso", + "status", + "statusLabel", + "authorizedKeysExists", + "authorizedKeysPath", + "codexAuthPath", + "codexHome", + "envGlobalPath", + "envProjectPath", + "gpu", + "projectDir", + "serviceName", + "sshCommand", + "sshPort", + "sshUser", + "targetDir" ], "properties": { - "cdpPath": { + "clonedOnHostname": { "type": "string" }, - "cdpUrl": { + "containerName": { "type": "string" }, - "containerName": { + "displayName": { "type": "string" }, - "noVncPath": { + "id": { "type": "string" }, - "noVncUrl": { + "projectKey": { "type": "string" }, - "projectId": { + "repoRef": { "type": "string" }, - "projectKey": { + "repoUrl": { "type": "string" }, + "sshSessions": { + "type": "number" + }, + "startedAtEpochMs": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ] + }, + "startedAtIso": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, "status": { "type": "string", "enum": [ "running", "stopped", - "missing", "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 @@ -2931,16 +3993,184 @@ } } } + }, + "401": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "404": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "409": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "500": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } } } } }, - "/projects/{projectId}/databases/profiles": { + "/projects/{projectId}/ps": { "get": { "tags": [ - "projectDatabases" + "projects" ], - "operationId": "projectDatabases.listDatabaseProfiles", + "operationId": "projects.projectPs", "parameters": [ { "name": "projectId", @@ -2960,64 +4190,11 @@ "schema": { "type": "object", "required": [ - "profiles" + "output" ], "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 - } + "output": { + "type": "string" } }, "additionalProperties": false @@ -3073,84 +4250,38 @@ } } } - } - } - }, - "post": { - "tags": [ - "projectDatabases" - ], - "operationId": "projectDatabases.saveDatabaseProfile", - "parameters": [ - { - "name": "projectId", - "in": "path", - "schema": { - "type": "string" - }, - "required": true - } - ], - "security": [], - "responses": { - "201": { - "description": "Success", + }, + "401": { + "description": "Error", "content": { "application/json": { "schema": { "type": "object", "required": [ - "profile" + "error" ], "properties": { - "profile": { + "error": { "type": "object", "required": [ - "createdAt", - "database", - "engine", - "host", - "id", - "label", - "maskedConnectionString", - "port", - "updatedAt", - "user" + "message", + "type" ], "properties": { - "createdAt": { - "type": "string" - }, - "database": { - "type": "string" - }, - "engine": { - "type": "string", - "enum": [ - "postgres", - "mysql", - "mariadb" - ] - }, - "host": { + "command": { "type": "string" }, - "id": { - "type": "string" - }, - "label": { - "type": "string" + "details": { + "$id": "/schemas/unknown", + "title": "unknown" }, - "maskedConnectionString": { + "message": { "type": "string" }, - "port": { - "type": "number" - }, - "updatedAt": { + "provider": { "type": "string" }, - "user": { + "type": { "type": "string" } }, @@ -3162,93 +4293,141 @@ } } }, - "400": { - "description": "The request did not match the expected schema", + "404": { + "description": "Error", "content": { "application/json": { "schema": { - "anyOf": [ - { - "$ref": "#/components/schemas/HttpApiDecodeError" - }, - { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { "type": "object", "required": [ - "error" + "message", + "type" ], "properties": { - "error": { - "type": "object", - "required": [ - "message", - "type" - ], - "properties": { - "command": { - "type": "string" - }, - "details": { - "$id": "/schemas/unknown", - "title": "unknown" - }, - "message": { - "type": "string" - }, - "provider": { - "type": "string" - }, - "type": { - "type": "string" - } - }, - "additionalProperties": false + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" } }, "additionalProperties": false } - ] + }, + "additionalProperties": false } } } - } - }, - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "connectionString" - ], - "properties": { - "connectionString": { - "type": "string" - }, - "label": { - "anyOf": [ - { - "type": "string" + }, + "409": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } }, - { - "type": "null" - } - ] - } - }, - "additionalProperties": false + "additionalProperties": false + } + }, + "additionalProperties": false + } } } }, - "required": true + "500": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + } } } }, - "/projects/{projectId}/databases/profiles/{profileId}": { - "delete": { + "/projects/{projectId}/logs": { + "get": { "tags": [ - "projectDatabases" + "projects" ], - "operationId": "projectDatabases.deleteDatabaseProfile", + "operationId": "projects.projectLogs", "parameters": [ { "name": "projectId", @@ -3257,20 +4436,28 @@ "type": "string" }, "required": true - }, - { - "name": "profileId", - "in": "path", - "schema": { - "type": "string" - }, - "required": true } ], "security": [], "responses": { - "204": { - "description": "Success" + "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", @@ -3320,135 +4507,81 @@ } } } - } - } - } - }, - "/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": { - "201": { - "description": "Success", + "401": { + "description": "Error", "content": { "application/json": { "schema": { "type": "object", "required": [ - "forward" + "error" ], "properties": { - "forward": { + "error": { "type": "object", "required": [ - "bindHost", - "containerName", - "createdAt", - "database", - "engine", - "externalConnectionString", - "hostPort", - "id", - "maskedExternalConnectionString", - "profileId", - "profileLabel", - "projectId", - "projectKey", - "publicHost", - "status", - "targetHost", - "targetPort" + "message", + "type" ], "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": { + "command": { "type": "string" }, - "maskedExternalConnectionString": { - "type": "string" + "details": { + "$id": "/schemas/unknown", + "title": "unknown" }, - "profileId": { + "message": { "type": "string" }, - "profileLabel": { + "provider": { "type": "string" }, - "projectId": { + "type": { "type": "string" - }, - "projectKey": { + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "404": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { "type": "string" }, - "publicHost": { - "type": "string" + "details": { + "$id": "/schemas/unknown", + "title": "unknown" }, - "status": { - "type": "string", - "enum": [ - "running", - "stopped", - "unknown" - ] + "message": { + "type": "string" }, - "targetHost": { + "provider": { "type": "string" }, - "targetPort": { - "type": "number" + "type": { + "type": "string" } }, "additionalProperties": false @@ -3459,130 +4592,86 @@ } } }, - "400": { - "description": "The request did not match the expected schema", + "409": { + "description": "Error", "content": { "application/json": { "schema": { - "anyOf": [ - { - "$ref": "#/components/schemas/HttpApiDecodeError" - }, - { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { "type": "object", "required": [ - "error" + "message", + "type" ], "properties": { - "error": { - "type": "object", - "required": [ - "message", - "type" - ], - "properties": { - "command": { - "type": "string" - }, - "details": { - "$id": "/schemas/unknown", - "title": "unknown" - }, - "message": { - "type": "string" - }, - "provider": { - "type": "string" - }, - "type": { - "type": "string" - } - }, - "additionalProperties": false + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" } }, "additionalProperties": false } - ] + }, + "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", + "500": { + "description": "Error", "content": { "application/json": { "schema": { - "anyOf": [ - { - "$ref": "#/components/schemas/HttpApiDecodeError" - }, - { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { "type": "object", "required": [ - "error" + "message", + "type" ], "properties": { - "error": { - "type": "object", - "required": [ - "message", - "type" - ], - "properties": { - "command": { - "type": "string" - }, - "details": { - "$id": "/schemas/unknown", - "title": "unknown" - }, - "message": { - "type": "string" - }, - "provider": { - "type": "string" - }, - "type": { - "type": "string" - } - }, - "additionalProperties": false + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" } }, "additionalProperties": false } - ] + }, + "additionalProperties": false } } } @@ -3590,12 +4679,12 @@ } } }, - "/projects/{projectId}/databases/forwards": { + "/projects/{projectId}/ports": { "get": { "tags": [ - "projectDatabases" + "projectPorts" ], - "operationId": "projectDatabases.listDatabaseForwards", + "operationId": "projectPorts.listProjectPorts", "parameters": [ { "name": "projectId", @@ -3626,20 +4715,16 @@ "bindHost", "containerName", "createdAt", - "database", - "engine", - "externalConnectionString", "hostPort", "id", - "maskedExternalConnectionString", - "profileId", - "profileLabel", "projectId", "projectKey", + "proxyPath", "publicHost", "status", - "targetHost", - "targetPort" + "targetContainerName", + "targetPort", + "url" ], "properties": { "bindHost": { @@ -3658,41 +4743,21 @@ } ] }, - "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" }, + "proxyPath": { + "type": "string" + }, "publicHost": { "type": "string" }, @@ -3704,11 +4769,14 @@ "unknown" ] }, - "targetHost": { + "targetContainerName": { "type": "string" }, "targetPort": { "type": "number" + }, + "url": { + "type": "string" } }, "additionalProperties": false @@ -3768,76 +4836,39 @@ } } } - } - } - } - }, - "/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", + }, + "401": { + "description": "Error", "content": { "application/json": { "schema": { "type": "object", "required": [ - "session" + "error" ], "properties": { - "session": { + "error": { "type": "object", "required": [ - "configHash", - "containerName", - "editorPath", - "editorUrl", - "projectId", - "projectKey", - "status" + "message", + "type" ], "properties": { - "configHash": { + "command": { "type": "string" }, - "containerName": { + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { "type": "string" }, - "editorPath": { + "provider": { "type": "string" }, - "editorUrl": { - "type": "string" - }, - "projectId": { + "type": { "type": "string" - }, - "projectKey": { - "type": "string" - }, - "status": { - "type": "string", - "enum": [ - "running", - "stopped", - "missing", - "unknown" - ] } }, "additionalProperties": false @@ -3848,124 +4879,80 @@ } } }, - "400": { - "description": "The request did not match the expected schema", + "404": { + "description": "Error", "content": { "application/json": { "schema": { - "anyOf": [ - { - "$ref": "#/components/schemas/HttpApiDecodeError" - }, - { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { "type": "object", "required": [ - "error" + "message", + "type" ], "properties": { - "error": { - "type": "object", - "required": [ - "message", - "type" - ], - "properties": { - "command": { - "type": "string" - }, - "details": { - "$id": "/schemas/unknown", - "title": "unknown" - }, - "message": { - "type": "string" - }, - "provider": { - "type": "string" - }, - "type": { - "type": "string" - } - }, - "additionalProperties": false + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" } }, "additionalProperties": false } - ] + }, + "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", + }, + "409": { + "description": "Error", "content": { "application/json": { "schema": { "type": "object", "required": [ - "session" + "error" ], "properties": { - "session": { + "error": { "type": "object", "required": [ - "configHash", - "containerName", - "editorPath", - "editorUrl", - "projectId", - "projectKey", - "status" + "message", + "type" ], "properties": { - "configHash": { - "type": "string" - }, - "containerName": { + "command": { "type": "string" }, - "editorPath": { - "type": "string" + "details": { + "$id": "/schemas/unknown", + "title": "unknown" }, - "editorUrl": { + "message": { "type": "string" }, - "projectId": { + "provider": { "type": "string" }, - "projectKey": { + "type": { "type": "string" - }, - "status": { - "type": "string", - "enum": [ - "running", - "stopped", - "missing", - "unknown" - ] } }, "additionalProperties": false @@ -3976,64 +4963,55 @@ } } }, - "400": { - "description": "The request did not match the expected schema", + "500": { + "description": "Error", "content": { "application/json": { "schema": { - "anyOf": [ - { - "$ref": "#/components/schemas/HttpApiDecodeError" - }, - { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { "type": "object", "required": [ - "error" + "message", + "type" ], "properties": { - "error": { - "type": "object", - "required": [ - "message", - "type" - ], - "properties": { - "command": { - "type": "string" - }, - "details": { - "$id": "/schemas/unknown", - "title": "unknown" - }, - "message": { - "type": "string" - }, - "provider": { - "type": "string" - }, - "type": { - "type": "string" - } - }, - "additionalProperties": false + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" } }, "additionalProperties": false } - ] + }, + "additionalProperties": false } } } } } - } - }, - "/projects/{projectId}/databases/restart": { + }, "post": { "tags": [ - "projectDatabases" + "projectPorts" ], - "operationId": "projectDatabases.restartDatabaseEditor", + "operationId": "projectPorts.createProjectPort", "parameters": [ { "name": "projectId", @@ -4046,38 +5024,54 @@ ], "security": [], "responses": { - "200": { + "201": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "required": [ - "session" + "forward" ], "properties": { - "session": { + "forward": { "type": "object", "required": [ - "configHash", + "bindHost", "containerName", - "editorPath", - "editorUrl", + "createdAt", + "hostPort", + "id", "projectId", "projectKey", - "status" + "proxyPath", + "publicHost", + "status", + "targetContainerName", + "targetPort", + "url" ], "properties": { - "configHash": { + "bindHost": { "type": "string" }, "containerName": { "type": "string" }, - "editorPath": { - "type": "string" + "createdAt": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] }, - "editorUrl": { + "hostPort": { + "type": "number" + }, + "id": { "type": "string" }, "projectId": { @@ -4086,14 +5080,28 @@ "projectKey": { "type": "string" }, + "proxyPath": { + "type": "string" + }, + "publicHost": { + "type": "string" + }, "status": { "type": "string", "enum": [ "running", "stopped", - "missing", "unknown" ] + }, + "targetContainerName": { + "type": "string" + }, + "targetPort": { + "type": "number" + }, + "url": { + "type": "string" } }, "additionalProperties": false @@ -4152,80 +5160,165 @@ } } } - } - } - } - }, - "/auth/github/status": { - "get": { - "tags": [ - "auth" - ], - "operationId": "auth.githubStatus", - "parameters": [], - "security": [], - "responses": { - "200": { - "description": "Success", + }, + "401": { + "description": "Error", "content": { "application/json": { "schema": { "type": "object", "required": [ - "status" + "error" ], "properties": { - "ok": { - "type": "boolean" - }, - "status": { + "error": { "type": "object", "required": [ - "summary", - "tokens" + "message", + "type" ], "properties": { - "summary": { + "command": { "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 - } + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "404": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "409": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "500": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" } }, "additionalProperties": false @@ -4235,6 +5328,79 @@ } } } + } + }, + "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": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "ok" + ], + "properties": { + "ok": { + "type": "boolean", + "enum": [ + true + ] + } + }, + "additionalProperties": false + } + } + } }, "400": { "description": "The request did not match the expected schema", @@ -4284,80 +5450,248 @@ } } } - } - } - } - }, - "/auth/gitlab/status": { - "get": { - "tags": [ - "auth" - ], - "operationId": "auth.gitlabStatus", - "parameters": [], - "security": [], - "responses": { - "200": { - "description": "Success", + }, + "401": { + "description": "Error", "content": { "application/json": { "schema": { "type": "object", "required": [ - "status" + "error" ], "properties": { - "ok": { - "type": "boolean" - }, - "status": { + "error": { "type": "object", "required": [ - "summary", - "tokens" + "message", + "type" ], "properties": { - "summary": { + "command": { "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 - } + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "404": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "409": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "500": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "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 @@ -4416,59 +5750,8726 @@ } } } - } - } - } - }, - "/auth/git/status": { - "get": { - "tags": [ - "auth" - ], - "operationId": "auth.gitStatus", - "parameters": [], - "security": [], - "responses": { - "200": { - "description": "Success", + }, + "401": { + "description": "Error", "content": { "application/json": { "schema": { "type": "object", "required": [ - "status" + "error" ], "properties": { - "ok": { - "type": "boolean" - }, - "status": { + "error": { "type": "object", "required": [ - "connections", - "summary" + "message", + "type" ], "properties": { - "connections": { - "type": "array", - "items": { - "type": "object", - "required": [ - "host", - "user" - ], - "properties": { - "host": { - "type": "string" - }, - "user": { - "type": "string" - } - }, - "additionalProperties": false - } + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "404": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "409": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "500": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "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" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + } + } + } + }, + "401": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "404": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "409": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "500": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "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" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + } + } + } + }, + "401": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "404": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "409": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "500": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + } + } + }, + "post": { + "tags": [ + "projectDatabases" + ], + "operationId": "projectDatabases.saveDatabaseProfile", + "parameters": [ + { + "name": "projectId", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + } + ], + "security": [], + "responses": { + "201": { + "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" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + } + } + } + }, + "401": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "404": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "409": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "500": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "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": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "ok" + ], + "properties": { + "ok": { + "type": "boolean", + "enum": [ + true + ] + } + }, + "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" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + } + } + } + }, + "401": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "404": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "409": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "500": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "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": { + "201": { + "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" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + } + } + } + }, + "401": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "404": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "409": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "500": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "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": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "ok" + ], + "properties": { + "ok": { + "type": "boolean", + "enum": [ + true + ] + } + }, + "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" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + } + } + } + }, + "401": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "404": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "409": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "500": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "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" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + } + } + } + }, + "401": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "404": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "409": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "500": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "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" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + } + } + } + }, + "401": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "404": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "409": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "500": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "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" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + } + } + } + }, + "401": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "404": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "409": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "500": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "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" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + } + } + } + }, + "401": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "404": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "409": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "500": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "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" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + } + } + } + }, + "401": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "404": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "409": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "500": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "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" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + } + } + } + }, + "401": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "404": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "409": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "500": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "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" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + } + } + } + }, + "401": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "404": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "409": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "500": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "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" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + } + } + } + }, + "401": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "404": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "409": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "500": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "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" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + } + } + } + }, + "401": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "404": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "409": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "500": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "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" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + } + } + } + }, + "401": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "404": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "409": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "500": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "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" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + } + } + } + }, + "401": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "404": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "409": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "500": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "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" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + } + } + } + }, + "401": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "404": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "409": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "500": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "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" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + } + } + } + }, + "401": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "404": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "409": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "500": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "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" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + } + } + } + }, + "401": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "404": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "409": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "500": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "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" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + } + } + } + }, + "401": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "404": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "409": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "500": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "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" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + } + } + } + }, + "401": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "404": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "409": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "500": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "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" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + } + } + } + }, + "401": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "404": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "409": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "500": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "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": { + "201": { + "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" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + } + } + } + }, + "401": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "404": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "409": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "500": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "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" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + } + } + } + }, + "401": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "404": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "409": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "500": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "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" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + } + } + } + }, + "401": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "404": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "409": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "500": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "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" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + } + } + } + }, + "401": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "404": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "409": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "500": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "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" + ], + "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" }, - "summary": { + "projectDir": { + "type": "string" + }, + "projectName": { "type": "string" } }, @@ -4528,70 +14529,81 @@ } } } - } - } - } - }, - "/auth/grok/status": { - "get": { - "tags": [ - "auth" - ], - "operationId": "auth.grokStatus", - "parameters": [ - { - "name": "label", - "in": "query", - "schema": { - "type": "string" - }, - "required": false - } - ], - "security": [], - "responses": { - "200": { - "description": "Success", + }, + "401": { + "description": "Error", "content": { "application/json": { "schema": { "type": "object", "required": [ - "status" + "error" ], "properties": { - "ok": { - "type": "boolean" - }, - "status": { + "error": { "type": "object", "required": [ - "authPath", - "connected", - "label", "message", - "method" + "type" ], "properties": { - "authPath": { + "command": { "type": "string" }, - "connected": { - "type": "boolean" + "details": { + "$id": "/schemas/unknown", + "title": "unknown" }, - "label": { + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "404": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { "type": "string" }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, "message": { "type": "string" }, - "method": { - "type": "string", - "enum": [ - "none", - "api-key", - "oauth" - ] + "provider": { + "type": "string" + }, + "type": { + "type": "string" } }, "additionalProperties": false @@ -4602,72 +14614,105 @@ } } }, - "400": { - "description": "The request did not match the expected schema", + "409": { + "description": "Error", "content": { "application/json": { "schema": { - "anyOf": [ - { - "$ref": "#/components/schemas/HttpApiDecodeError" - }, - { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { "type": "object", "required": [ - "error" + "message", + "type" ], "properties": { - "error": { - "type": "object", - "required": [ - "message", - "type" - ], - "properties": { - "command": { - "type": "string" - }, - "details": { - "$id": "/schemas/unknown", - "title": "unknown" - }, - "message": { - "type": "string" - }, - "provider": { - "type": "string" - }, - "type": { - "type": "string" - } - }, - "additionalProperties": false + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" } }, "additionalProperties": false } - ] + }, + "additionalProperties": false + } + } + } + }, + "500": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false } } } } } - } - }, - "/auth/codex/status": { - "get": { + }, + "post": { "tags": [ - "auth" + "projectAuth" ], - "operationId": "auth.codexStatus", + "operationId": "projectAuth.projectAuthAction", "parameters": [ { - "name": "label", - "in": "query", + "name": "projectId", + "in": "path", "schema": { "type": "string" }, - "required": false + "required": true } ], "security": [], @@ -4679,23 +14724,73 @@ "schema": { "type": "object", "required": [ - "status" + "snapshot" ], "properties": { "ok": { "type": "boolean" }, - "status": { + "snapshot": { "type": "object", "required": [ - "account", - "authPath", - "label", - "message", - "present" + "activeClaudeLabel", + "activeGeminiLabel", + "activeGitLabel", + "activeGithubLabel", + "activeGrokLabel", + "claudeAuthEntries", + "claudeAuthPath", + "envGlobalPath", + "envProjectPath", + "geminiAuthEntries", + "geminiAuthPath", + "gitTokenEntries", + "githubTokenEntries", + "projectDir", + "projectName" ], "properties": { - "account": { + "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" @@ -4705,17 +14800,47 @@ } ] }, - "authPath": { + "claudeAuthEntries": { + "type": "number" + }, + "claudeAuthPath": { + "type": "string" + }, + "codexAuthEntries": { + "type": "number" + }, + "codexAuthPath": { + "type": "string" + }, + "envGlobalPath": { "type": "string" }, - "label": { + "envProjectPath": { "type": "string" }, - "message": { + "geminiAuthEntries": { + "type": "number" + }, + "geminiAuthPath": { "type": "string" }, - "present": { - "type": "boolean" + "gitTokenEntries": { + "type": "number" + }, + "githubTokenEntries": { + "type": "number" + }, + "grokAuthEntries": { + "type": "number" + }, + "grokAuthPath": { + "type": "string" + }, + "projectDir": { + "type": "string" + }, + "projectName": { + "type": "string" } }, "additionalProperties": false @@ -4774,80 +14899,39 @@ } } } - } - } - } - }, - "/auth/github/login": { - "post": { - "tags": [ - "auth" - ], - "operationId": "auth.githubLogin", - "parameters": [], - "security": [], - "responses": { - "201": { - "description": "Success", + }, + "401": { + "description": "Error", "content": { "application/json": { "schema": { "type": "object", "required": [ - "status" + "error" ], "properties": { - "ok": { - "type": "boolean" - }, - "status": { + "error": { "type": "object", "required": [ - "summary", - "tokens" + "message", + "type" ], "properties": { - "summary": { + "command": { "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 - } + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" } }, "additionalProperties": false @@ -4858,172 +14942,80 @@ } } }, - "400": { - "description": "The request did not match the expected schema", + "404": { + "description": "Error", "content": { "application/json": { "schema": { - "anyOf": [ - { - "$ref": "#/components/schemas/HttpApiDecodeError" - }, - { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { "type": "object", "required": [ - "error" + "message", + "type" ], "properties": { - "error": { - "type": "object", - "required": [ - "message", - "type" - ], - "properties": { - "command": { - "type": "string" - }, - "details": { - "$id": "/schemas/unknown", - "title": "unknown" - }, - "message": { - "type": "string" - }, - "provider": { - "type": "string" - }, - "type": { - "type": "string" - } - }, - "additionalProperties": false + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "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 + "additionalProperties": false + } } } }, - "required": true - } - } - }, - "/auth/github/logout": { - "post": { - "tags": [ - "auth" - ], - "operationId": "auth.githubLogout", - "parameters": [], - "security": [], - "responses": { - "200": { - "description": "Success", + "409": { + "description": "Error", "content": { "application/json": { "schema": { "type": "object", "required": [ - "status" + "error" ], "properties": { - "ok": { - "type": "boolean" - }, - "status": { + "error": { "type": "object", "required": [ - "summary", - "tokens" + "message", + "type" ], "properties": { - "summary": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { "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 @@ -5034,51 +15026,44 @@ } } }, - "400": { - "description": "The request did not match the expected schema", + "500": { + "description": "Error", "content": { "application/json": { "schema": { - "anyOf": [ - { - "$ref": "#/components/schemas/HttpApiDecodeError" - }, - { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { "type": "object", "required": [ - "error" + "message", + "type" ], "properties": { - "error": { - "type": "object", - "required": [ - "message", - "type" - ], - "properties": { - "command": { - "type": "string" - }, - "details": { - "$id": "/schemas/unknown", - "title": "unknown" - }, - "message": { - "type": "string" - }, - "provider": { - "type": "string" - }, - "type": { - "type": "string" - } - }, - "additionalProperties": false + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" } }, "additionalProperties": false } - ] + }, + "additionalProperties": false } } } @@ -5089,8 +15074,25 @@ "application/json": { "schema": { "type": "object", - "required": [], + "required": [ + "flow" + ], "properties": { + "flow": { + "type": "string", + "enum": [ + "ProjectGithubConnect", + "ProjectGithubDisconnect", + "ProjectGitConnect", + "ProjectGitDisconnect", + "ProjectClaudeConnect", + "ProjectClaudeDisconnect", + "ProjectGeminiConnect", + "ProjectGeminiDisconnect", + "ProjectGrokConnect", + "ProjectGrokDisconnect" + ] + }, "label": { "anyOf": [ { @@ -5110,13 +15112,22 @@ } } }, - "/auth/gitlab/login": { + "/projects/by-key/{projectKey}/terminal-sessions": { "post": { "tags": [ - "auth" + "terminal" + ], + "operationId": "terminal.createTerminalByKey", + "parameters": [ + { + "name": "projectKey", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + } ], - "operationId": "auth.gitlabLogin", - "parameters": [], "security": [], "responses": { "201": { @@ -5126,60 +15137,188 @@ "schema": { "type": "object", "required": [ - "status" + "project", + "session" ], "properties": { "ok": { "type": "boolean" }, - "status": { + "project": { "type": "object", "required": [ - "summary", - "tokens" + "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": { - "summary": { + "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" }, - "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 - } + "startedAt": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "ready", + "attached", + "exited", + "failed" + ] } }, "additionalProperties": false @@ -5238,117 +15377,266 @@ } } } - } - }, - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [], - "properties": { - "label": { - "anyOf": [ - { - "type": "string" + }, + "401": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } }, - { - "type": "null" - } - ] + "additionalProperties": false + } }, - "token": { - "anyOf": [ - { - "type": "string" + "additionalProperties": false + } + } + } + }, + "404": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } }, - { - "type": "null" - } - ] - } - }, - "additionalProperties": false + "additionalProperties": false + } + }, + "additionalProperties": false + } } } }, - "required": true - } - } - }, - "/auth/gitlab/logout": { - "post": { - "tags": [ - "auth" - ], - "operationId": "auth.gitlabLogout", - "parameters": [], - "security": [], - "responses": { - "200": { - "description": "Success", + "409": { + "description": "Error", "content": { "application/json": { "schema": { "type": "object", "required": [ - "status" + "error" ], "properties": { - "ok": { - "type": "boolean" - }, - "status": { + "error": { "type": "object", "required": [ - "summary", - "tokens" + "message", + "type" ], "properties": { - "summary": { + "command": { "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 - } + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "500": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "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" } - }, - "additionalProperties": false + ] + }, + "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 @@ -5404,83 +15692,80 @@ } } } - } - }, - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [], - "properties": { - "label": { - "anyOf": [ - { - "type": "string" + }, + "401": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } }, - { - "type": "null" - } - ] - } - }, - "additionalProperties": false + "additionalProperties": false + } + }, + "additionalProperties": false + } } } }, - "required": true - } - } - }, - "/auth/git/login": { - "post": { - "tags": [ - "auth" - ], - "operationId": "auth.gitLogin", - "parameters": [], - "security": [], - "responses": { - "201": { - "description": "Success", + "404": { + "description": "Error", "content": { "application/json": { "schema": { "type": "object", "required": [ - "status" + "error" ], "properties": { - "ok": { - "type": "boolean" - }, - "status": { + "error": { "type": "object", "required": [ - "connections", - "summary" + "message", + "type" ], "properties": { - "connections": { - "type": "array", - "items": { - "type": "object", - "required": [ - "host", - "user" - ], - "properties": { - "host": { - "type": "string" - }, - "user": { - "type": "string" - } - }, - "additionalProperties": false - } + "command": { + "type": "string" }, - "summary": { + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { "type": "string" } }, @@ -5492,150 +15777,138 @@ } } }, - "400": { - "description": "The request did not match the expected schema", + "409": { + "description": "Error", "content": { "application/json": { "schema": { - "anyOf": [ - { - "$ref": "#/components/schemas/HttpApiDecodeError" - }, - { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { "type": "object", "required": [ - "error" + "message", + "type" ], "properties": { - "error": { - "type": "object", - "required": [ - "message", - "type" - ], - "properties": { - "command": { - "type": "string" - }, - "details": { - "$id": "/schemas/unknown", - "title": "unknown" - }, - "message": { - "type": "string" - }, - "provider": { - "type": "string" - }, - "type": { - "type": "string" - } - }, - "additionalProperties": false + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" } }, "additionalProperties": false } - ] + }, + "additionalProperties": false } } } - } - }, - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "host" - ], - "properties": { - "host": { - "type": "string" - }, - "token": { - "anyOf": [ - { - "type": "string" + }, + "500": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } }, - { - "type": "null" - } - ] + "additionalProperties": false + } }, - "user": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ] - } - }, - "additionalProperties": false + "additionalProperties": false + } } } - }, - "required": true + } } } }, - "/auth/git/logout": { + "/projects/by-key/{projectKey}/terminal-sessions/start": { "post": { "tags": [ - "auth" + "terminal" + ], + "operationId": "terminal.startTerminalByKey", + "parameters": [ + { + "name": "projectKey", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + } ], - "operationId": "auth.gitLogout", - "parameters": [], "security": [], "responses": { - "200": { + "202": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "required": [ - "status" + "accepted", + "cursor", + "projectId", + "requestId" ], "properties": { - "ok": { - "type": "boolean" + "accepted": { + "type": "boolean", + "enum": [ + true + ] }, - "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 + "cursor": { + "type": "number" + }, + "projectId": { + "type": "string" + }, + "requestId": { + "type": "string" } }, "additionalProperties": false @@ -5691,103 +15964,39 @@ } } } - } - }, - "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", + "401": { + "description": "Error", "content": { "application/json": { "schema": { "type": "object", "required": [ - "snapshot" + "error" ], "properties": { - "ok": { - "type": "boolean" - }, - "snapshot": { + "error": { "type": "object", "required": [ - "claudeAuthEntries", - "claudeAuthPath", - "geminiAuthEntries", - "geminiAuthPath", - "gitTokenEntries", - "gitUserEntries", - "githubTokenEntries", - "globalEnvPath", - "totalEntries" + "message", + "type" ], "properties": { - "claudeAuthEntries": { - "type": "number" - }, - "claudeAuthPath": { + "command": { "type": "string" }, - "codexAuthEntries": { - "type": "number" - }, - "codexAuthPath": { - "type": "string" - }, - "geminiAuthEntries": { - "type": "number" + "details": { + "$id": "/schemas/unknown", + "title": "unknown" }, - "geminiAuthPath": { + "message": { "type": "string" }, - "gitTokenEntries": { - "type": "number" - }, - "gitUserEntries": { - "type": "number" - }, - "githubTokenEntries": { - "type": "number" - }, - "globalEnvPath": { + "provider": { "type": "string" }, - "grokAuthEntries": { - "type": "number" - }, - "grokAuthPath": { + "type": { "type": "string" - }, - "totalEntries": { - "type": "number" } }, "additionalProperties": false @@ -5798,130 +16007,80 @@ } } }, - "400": { - "description": "The request did not match the expected schema", + "404": { + "description": "Error", "content": { "application/json": { "schema": { - "anyOf": [ - { - "$ref": "#/components/schemas/HttpApiDecodeError" - }, - { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { "type": "object", "required": [ - "error" + "message", + "type" ], "properties": { - "error": { - "type": "object", - "required": [ - "message", - "type" - ], - "properties": { - "command": { - "type": "string" - }, - "details": { - "$id": "/schemas/unknown", - "title": "unknown" - }, - "message": { - "type": "string" - }, - "provider": { - "type": "string" - }, - "type": { - "type": "string" - } - }, - "additionalProperties": false + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" } }, "additionalProperties": false } - ] + }, + "additionalProperties": false } } } - } - } - }, - "post": { - "tags": [ - "auth" - ], - "operationId": "auth.authMenuAction", - "parameters": [], - "security": [], - "responses": { - "200": { - "description": "Success", + }, + "409": { + "description": "Error", "content": { "application/json": { "schema": { "type": "object", "required": [ - "snapshot" + "error" ], "properties": { - "ok": { - "type": "boolean" - }, - "snapshot": { + "error": { "type": "object", "required": [ - "claudeAuthEntries", - "claudeAuthPath", - "geminiAuthEntries", - "geminiAuthPath", - "gitTokenEntries", - "gitUserEntries", - "githubTokenEntries", - "globalEnvPath", - "totalEntries" + "message", + "type" ], "properties": { - "claudeAuthEntries": { - "type": "number" - }, - "claudeAuthPath": { - "type": "string" - }, - "codexAuthEntries": { - "type": "number" - }, - "codexAuthPath": { + "command": { "type": "string" }, - "geminiAuthEntries": { - "type": "number" + "details": { + "$id": "/schemas/unknown", + "title": "unknown" }, - "geminiAuthPath": { + "message": { "type": "string" }, - "gitTokenEntries": { - "type": "number" - }, - "gitUserEntries": { - "type": "number" - }, - "githubTokenEntries": { - "type": "number" - }, - "globalEnvPath": { + "provider": { "type": "string" }, - "grokAuthEntries": { - "type": "number" - }, - "grokAuthPath": { + "type": { "type": "string" - }, - "totalEntries": { - "type": "number" } }, "additionalProperties": false @@ -5932,51 +16091,44 @@ } } }, - "400": { - "description": "The request did not match the expected schema", + "500": { + "description": "Error", "content": { "application/json": { "schema": { - "anyOf": [ - { - "$ref": "#/components/schemas/HttpApiDecodeError" - }, - { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { "type": "object", "required": [ - "error" + "message", + "type" ], "properties": { - "error": { - "type": "object", - "required": [ - "message", - "type" - ], - "properties": { - "command": { - "type": "string" - }, - "details": { - "$id": "/schemas/unknown", - "title": "unknown" - }, - "message": { - "type": "string" - }, - "provider": { - "type": "string" - }, - "type": { - "type": "string" - } - }, - "additionalProperties": false + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" } }, "additionalProperties": false } - ] + }, + "additionalProperties": false } } } @@ -5988,61 +16140,11 @@ "schema": { "type": "object", "required": [ - "flow" + "requestId" ], "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" - } - ] + "requestId": { + "$ref": "#/components/schemas/UUID" } }, "additionalProperties": false @@ -6053,16 +16155,33 @@ } } }, - "/auth/terminal-sessions": { - "post": { + "/projects/by-key/{projectKey}/terminal-sessions/{sessionId}": { + "get": { "tags": [ - "auth" + "terminal" + ], + "operationId": "terminal.getTerminalByKey", + "parameters": [ + { + "name": "projectKey", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + }, + { + "name": "sessionId", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + } ], - "operationId": "auth.authTerminalSession", - "parameters": [], "security": [], "responses": { - "201": { + "200": { "description": "Success", "content": { "application/json": { @@ -6072,9 +16191,6 @@ "session" ], "properties": { - "ok": { - "type": "boolean" - }, "session": { "type": "object", "required": [ @@ -6174,101 +16290,169 @@ }, "additionalProperties": false } - ] - } - } - } - } - }, - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "flow" - ], - "properties": { - "flow": { - "type": "string", - "enum": [ - "ClaudeOauth", - "GeminiOauth", - "GrokOauth" - ] + ] + } + } + } + }, + "401": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "404": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "409": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } }, - "label": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ] - } - }, - "additionalProperties": false + "additionalProperties": false + } } } }, - "required": true - } - } - }, - "/auth/codex/import": { - "post": { - "tags": [ - "auth" - ], - "operationId": "auth.codexImport", - "parameters": [], - "security": [], - "responses": { - "201": { - "description": "Success", + "500": { + "description": "Error", "content": { "application/json": { "schema": { "type": "object", "required": [ - "status" + "error" ], "properties": { - "ok": { - "type": "boolean" - }, - "status": { + "error": { "type": "object", "required": [ - "account", - "authPath", - "label", "message", - "present" + "type" ], "properties": { - "account": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ] - }, - "authPath": { + "command": { "type": "string" }, - "label": { - "type": "string" + "details": { + "$id": "/schemas/unknown", + "title": "unknown" }, "message": { "type": "string" }, - "present": { - "type": "boolean" + "provider": { + "type": "string" + }, + "type": { + "type": "string" } }, "additionalProperties": false @@ -6278,6 +16462,55 @@ } } } + } + } + }, + "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": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "ok" + ], + "properties": { + "ok": { + "type": "boolean", + "enum": [ + true + ] + } + }, + "additionalProperties": false + } + } + } }, "400": { "description": "The request did not match the expected schema", @@ -6327,92 +16560,39 @@ } } } - } - }, - "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", + "401": { + "description": "Error", "content": { "application/json": { "schema": { "type": "object", "required": [ - "status" + "error" ], "properties": { - "ok": { - "type": "boolean" - }, - "status": { + "error": { "type": "object", "required": [ - "account", - "authPath", - "label", "message", - "present" + "type" ], "properties": { - "account": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ] - }, - "authPath": { + "command": { "type": "string" }, - "label": { - "type": "string" + "details": { + "$id": "/schemas/unknown", + "title": "unknown" }, "message": { "type": "string" }, - "present": { - "type": "boolean" + "provider": { + "type": "string" + }, + "type": { + "type": "string" } }, "additionalProperties": false @@ -6423,133 +16603,80 @@ } } }, - "400": { - "description": "The request did not match the expected schema", + "404": { + "description": "Error", "content": { "application/json": { "schema": { - "anyOf": [ - { - "$ref": "#/components/schemas/HttpApiDecodeError" - }, - { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { "type": "object", "required": [ - "error" + "message", + "type" ], "properties": { - "error": { - "type": "object", - "required": [ - "message", - "type" - ], - "properties": { - "command": { - "type": "string" - }, - "details": { - "$id": "/schemas/unknown", - "title": "unknown" - }, - "message": { - "type": "string" - }, - "provider": { - "type": "string" - }, - "type": { - "type": "string" - } - }, - "additionalProperties": false + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" } }, "additionalProperties": false } - ] + }, + "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", + "409": { + "description": "Error", "content": { "application/json": { "schema": { "type": "object", "required": [ - "status" + "error" ], "properties": { - "ok": { - "type": "boolean" - }, - "status": { + "error": { "type": "object", "required": [ - "authPath", - "connected", - "label", "message", - "method" + "type" ], "properties": { - "authPath": { + "command": { "type": "string" }, - "connected": { - "type": "boolean" + "details": { + "$id": "/schemas/unknown", + "title": "unknown" }, - "label": { + "message": { "type": "string" }, - "message": { + "provider": { "type": "string" }, - "method": { - "type": "string", - "enum": [ - "none", - "api-key", - "oauth" - ] + "type": { + "type": "string" } }, "additionalProperties": false @@ -6560,91 +16687,60 @@ } } }, - "400": { - "description": "The request did not match the expected schema", + "500": { + "description": "Error", "content": { "application/json": { "schema": { - "anyOf": [ - { - "$ref": "#/components/schemas/HttpApiDecodeError" - }, - { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { "type": "object", "required": [ - "error" + "message", + "type" ], "properties": { - "error": { - "type": "object", - "required": [ - "message", - "type" - ], - "properties": { - "command": { - "type": "string" - }, - "details": { - "$id": "/schemas/unknown", - "title": "unknown" - }, - "message": { - "type": "string" - }, - "provider": { - "type": "string" - }, - "type": { - "type": "string" - } - }, - "additionalProperties": false + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" } }, "additionalProperties": false } - ] + }, + "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": { + "/projects/by-key/{projectKey}/terminal-sessions/active": { + "put": { "tags": [ - "projectAuth" + "terminal" ], - "operationId": "projectAuth.projectAuth", + "operationId": "terminal.setActiveTerminalByKey", "parameters": [ { - "name": "projectId", + "name": "projectKey", "in": "path", "schema": { "type": "string" @@ -6661,127 +16757,57 @@ "schema": { "type": "object", "required": [ - "snapshot" + "session" ], "properties": { "ok": { "type": "boolean" }, - "snapshot": { + "session": { "type": "object", "required": [ - "activeClaudeLabel", - "activeGeminiLabel", - "activeGitLabel", - "activeGithubLabel", - "activeGrokLabel", - "claudeAuthEntries", - "claudeAuthPath", - "envGlobalPath", - "envProjectPath", - "geminiAuthEntries", - "geminiAuthPath", - "gitTokenEntries", - "githubTokenEntries", - "projectDir", - "projectName", - "totalEntries" + "createdAt", + "id", + "projectId", + "sshCommand", + "status" ], "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": { + "attachedClients": { "type": "number" }, - "codexAuthPath": { - "type": "string" - }, - "envGlobalPath": { + "closedAt": { "type": "string" }, - "envProjectPath": { + "createdAt": { "type": "string" }, - "geminiAuthEntries": { + "exitCode": { "type": "number" }, - "geminiAuthPath": { + "id": { "type": "string" }, - "gitTokenEntries": { - "type": "number" - }, - "githubTokenEntries": { - "type": "number" + "projectId": { + "type": "string" }, - "grokAuthEntries": { + "signal": { "type": "number" }, - "grokAuthPath": { - "type": "string" - }, - "projectDir": { + "sshCommand": { "type": "string" }, - "projectName": { + "startedAt": { "type": "string" }, - "totalEntries": { - "type": "number" + "status": { + "type": "string", + "enum": [ + "ready", + "attached", + "exited", + "failed" + ] } }, "additionalProperties": false @@ -6840,17 +16866,206 @@ } } } + }, + "401": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "404": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "409": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "500": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "sessionId" + ], + "properties": { + "sessionId": { + "type": "string" + } + }, + "additionalProperties": false + } + } + }, + "required": true } - }, - "post": { + } + }, + "/terminal-sessions/{sessionId}": { + "get": { "tags": [ - "projectAuth" + "terminal" ], - "operationId": "projectAuth.projectAuthAction", + "operationId": "terminal.lookupTerminal", "parameters": [ { - "name": "projectId", + "name": "sessionId", "in": "path", "schema": { "type": "string" @@ -6867,127 +17082,62 @@ "schema": { "type": "object", "required": [ - "snapshot" + "projectDisplayName", + "projectKey", + "session" ], "properties": { - "ok": { - "type": "boolean" + "projectDisplayName": { + "type": "string" }, - "snapshot": { + "projectKey": { + "type": "string" + }, + "session": { "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": { + "createdAt", + "id", + "projectId", + "sshCommand", + "status" + ], + "properties": { + "attachedClients": { "type": "number" }, - "codexAuthPath": { - "type": "string" - }, - "envGlobalPath": { + "closedAt": { "type": "string" }, - "envProjectPath": { + "createdAt": { "type": "string" }, - "geminiAuthEntries": { + "exitCode": { "type": "number" }, - "geminiAuthPath": { + "id": { "type": "string" }, - "gitTokenEntries": { - "type": "number" - }, - "githubTokenEntries": { - "type": "number" + "projectId": { + "type": "string" }, - "grokAuthEntries": { + "signal": { "type": "number" }, - "grokAuthPath": { - "type": "string" - }, - "projectDir": { + "sshCommand": { "type": "string" }, - "projectName": { + "startedAt": { "type": "string" }, - "totalEntries": { - "type": "number" + "status": { + "type": "string", + "enum": [ + "ready", + "attached", + "exited", + "failed" + ] } }, "additionalProperties": false @@ -7046,258 +17196,123 @@ } } } - } - }, - "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": { - "201": { - "description": "Success", + "401": { + "description": "Error", "content": { "application/json": { "schema": { "type": "object", "required": [ - "project", - "session" + "error" ], "properties": { - "ok": { - "type": "boolean" - }, - "project": { + "error": { "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" + "message", + "type" ], "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": { + "command": { "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" - ] + "details": { + "$id": "/schemas/unknown", + "title": "unknown" }, - "projectDir": { - "type": "string" - }, - "serviceName": { - "type": "string" - }, - "sshCommand": { + "message": { "type": "string" }, - "sshPort": { - "type": "number" - }, - "sshUser": { + "provider": { "type": "string" }, - "targetDir": { + "type": { "type": "string" } }, "additionalProperties": false - }, - "session": { + } + }, + "additionalProperties": false + } + } + } + }, + "404": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { "type": "object", "required": [ - "createdAt", - "id", - "projectId", - "sshCommand", - "status" + "message", + "type" ], - "properties": { - "attachedClients": { - "type": "number" - }, - "closedAt": { + "properties": { + "command": { "type": "string" }, - "createdAt": { - "type": "string" + "details": { + "$id": "/schemas/unknown", + "title": "unknown" }, - "exitCode": { - "type": "number" + "message": { + "type": "string" }, - "id": { + "provider": { "type": "string" }, - "projectId": { + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "409": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { "type": "string" }, - "signal": { - "type": "number" + "details": { + "$id": "/schemas/unknown", + "title": "unknown" }, - "sshCommand": { + "message": { "type": "string" }, - "startedAt": { + "provider": { "type": "string" }, - "status": { - "type": "string", - "enum": [ - "ready", - "attached", - "exited", - "failed" - ] + "type": { + "type": "string" } }, "additionalProperties": false @@ -7308,65 +17323,60 @@ } } }, - "400": { - "description": "The request did not match the expected schema", + "500": { + "description": "Error", "content": { "application/json": { "schema": { - "anyOf": [ - { - "$ref": "#/components/schemas/HttpApiDecodeError" - }, - { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { "type": "object", "required": [ - "error" + "message", + "type" ], "properties": { - "error": { - "type": "object", - "required": [ - "message", - "type" - ], - "properties": { - "command": { - "type": "string" - }, - "details": { - "$id": "/schemas/unknown", - "title": "unknown" - }, - "message": { - "type": "string" - }, - "provider": { - "type": "string" - }, - "type": { - "type": "string" - } - }, - "additionalProperties": false + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" } }, "additionalProperties": false } - ] + }, + "additionalProperties": false } } } } } - }, - "get": { + } + }, + "/auth/terminal-sessions/{sessionId}": { + "delete": { "tags": [ "terminal" ], - "operationId": "terminal.listTerminalsByKey", + "operationId": "terminal.deleteAuthTerminal", "parameters": [ { - "name": "projectKey", + "name": "sessionId", "in": "path", "schema": { "type": "string" @@ -7383,71 +17393,14 @@ "schema": { "type": "object", "required": [ - "activeSessionId", - "sessions" + "ok" ], "properties": { - "activeSessionId": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } + "ok": { + "type": "boolean", + "enum": [ + true ] - }, - "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 @@ -7503,55 +17456,126 @@ } } } - } - } - } - }, - "/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", + }, + "401": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "404": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "409": { + "description": "Error", "content": { "application/json": { "schema": { "type": "object", "required": [ - "accepted", - "cursor", - "projectId", - "requestId" + "error" ], "properties": { - "accepted": { - "type": "boolean", - "enum": [ - true - ] - }, - "cursor": { - "type": "number" - }, - "projectId": { - "type": "string" - }, - "requestId": { - "type": "string" + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false } }, "additionalProperties": false @@ -7559,94 +17583,60 @@ } } }, - "400": { - "description": "The request did not match the expected schema", + "500": { + "description": "Error", "content": { "application/json": { "schema": { - "anyOf": [ - { - "$ref": "#/components/schemas/HttpApiDecodeError" - }, - { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { "type": "object", "required": [ - "error" + "message", + "type" ], "properties": { - "error": { - "type": "object", - "required": [ - "message", - "type" - ], - "properties": { - "command": { - "type": "string" - }, - "details": { - "$id": "/schemas/unknown", - "title": "unknown" - }, - "message": { - "type": "string" - }, - "provider": { - "type": "string" - }, - "type": { - "type": "string" - } - }, - "additionalProperties": false + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" } }, "additionalProperties": false } - ] + }, + "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}": { + "/projects/{projectId}/prompts": { "get": { "tags": [ - "terminal" + "prompts" ], - "operationId": "terminal.getTerminalByKey", + "operationId": "prompts.listPrompts", "parameters": [ { - "name": "projectKey", - "in": "path", - "schema": { - "type": "string" - }, - "required": true - }, - { - "name": "sessionId", + "name": "projectId", "in": "path", "schema": { "type": "string" @@ -7663,142 +17653,83 @@ "schema": { "type": "object", "required": [ - "session" + "snapshot" ], "properties": { - "session": { + "ok": { + "type": "boolean" + }, + "snapshot": { "type": "object", "required": [ - "createdAt", - "id", + "projectDir", "projectId", - "sshCommand", - "status" + "projectKey", + "prompts" ], "properties": { - "attachedClients": { - "type": "number" - }, - "closedAt": { - "type": "string" - }, - "createdAt": { - "type": "string" - }, - "exitCode": { - "type": "number" - }, - "id": { + "projectDir": { "type": "string" }, "projectId": { "type": "string" }, - "signal": { - "type": "number" - }, - "sshCommand": { - "type": "string" - }, - "startedAt": { + "projectKey": { "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" - ], - "properties": { - "error": { - "type": "object", - "required": [ - "message", - "type" - ], - "properties": { - "command": { - "type": "string" - }, - "details": { - "$id": "/schemas/unknown", - "title": "unknown" - }, - "message": { - "type": "string" - }, - "provider": { - "type": "string" - }, - "type": { - "type": "string" - } - }, - "additionalProperties": false + "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 } } } - } - } - }, - "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", @@ -7848,19 +17779,195 @@ } } } + }, + "401": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "404": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "409": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "500": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } } } } }, - "/projects/by-key/{projectKey}/terminal-sessions/active": { + "/projects/{projectId}/prompts/{kind}": { "put": { "tags": [ - "terminal" + "prompts" ], - "operationId": "terminal.setActiveTerminalByKey", + "operationId": "prompts.writePrompt", "parameters": [ { - "name": "projectKey", + "name": "projectId", + "in": "path", + "schema": { + "type": "string" + }, + "required": true + }, + { + "name": "kind", "in": "path", "schema": { "type": "string" @@ -7877,54 +17984,117 @@ "schema": { "type": "object", "required": [ - "session" + "prompt", + "snapshot" ], "properties": { - "session": { + "ok": { + "type": "boolean" + }, + "prompt": { "type": "object", "required": [ - "createdAt", - "id", - "projectId", - "sshCommand", - "status" + "absolutePath", + "bytes", + "content", + "exists", + "fileName", + "kind", + "relativePath" ], "properties": { - "attachedClients": { + "absolutePath": { + "type": "string" + }, + "bytes": { "type": "number" }, - "closedAt": { + "content": { "type": "string" }, - "createdAt": { + "exists": { + "type": "boolean" + }, + "fileName": { "type": "string" }, - "exitCode": { - "type": "number" + "kind": { + "type": "string", + "enum": [ + "claude", + "codex", + "gemini", + "grok" + ] }, - "id": { + "relativePath": { "type": "string" - }, - "projectId": { + } + }, + "additionalProperties": false + }, + "snapshot": { + "type": "object", + "required": [ + "projectDir", + "projectId", + "projectKey", + "prompts" + ], + "properties": { + "projectDir": { "type": "string" }, - "signal": { - "type": "number" - }, - "sshCommand": { + "projectId": { "type": "string" }, - "startedAt": { + "projectKey": { "type": "string" }, - "status": { - "type": "string", - "enum": [ - "ready", - "attached", - "exited", - "failed" - ] + "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 @@ -7983,110 +18153,81 @@ } } } - } - }, - "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", + "401": { + "description": "Error", "content": { "application/json": { "schema": { "type": "object", "required": [ - "projectDisplayName", - "projectKey", - "session" + "error" ], "properties": { - "projectDisplayName": { - "type": "string" - }, - "projectKey": { - "type": "string" - }, - "session": { + "error": { "type": "object", "required": [ - "createdAt", - "id", - "projectId", - "sshCommand", - "status" + "message", + "type" ], "properties": { - "attachedClients": { - "type": "number" - }, - "closedAt": { + "command": { "type": "string" }, - "createdAt": { - "type": "string" + "details": { + "$id": "/schemas/unknown", + "title": "unknown" }, - "exitCode": { - "type": "number" + "message": { + "type": "string" }, - "id": { + "provider": { "type": "string" }, - "projectId": { + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "404": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { "type": "string" }, - "signal": { - "type": "number" + "details": { + "$id": "/schemas/unknown", + "title": "unknown" }, - "sshCommand": { + "message": { "type": "string" }, - "startedAt": { + "provider": { "type": "string" }, - "status": { - "type": "string", - "enum": [ - "ready", - "attached", - "exited", - "failed" - ] + "type": { + "type": "string" } }, "additionalProperties": false @@ -8097,137 +18238,116 @@ } } }, - "400": { - "description": "The request did not match the expected schema", + "409": { + "description": "Error", "content": { "application/json": { "schema": { - "anyOf": [ - { - "$ref": "#/components/schemas/HttpApiDecodeError" - }, - { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { "type": "object", "required": [ - "error" + "message", + "type" ], "properties": { - "error": { - "type": "object", - "required": [ - "message", - "type" - ], - "properties": { - "command": { - "type": "string" - }, - "details": { - "$id": "/schemas/unknown", - "title": "unknown" - }, - "message": { - "type": "string" - }, - "provider": { - "type": "string" - }, - "type": { - "type": "string" - } - }, - "additionalProperties": false + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" } }, "additionalProperties": false } - ] + }, + "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", + "500": { + "description": "Error", "content": { "application/json": { "schema": { - "anyOf": [ - { - "$ref": "#/components/schemas/HttpApiDecodeError" - }, - { - "type": "object", - "required": [ - "error" - ], - "properties": { - "error": { - "type": "object", - "required": [ - "message", - "type" - ], - "properties": { - "command": { - "type": "string" - }, - "details": { - "$id": "/schemas/unknown", - "title": "unknown" - }, - "message": { - "type": "string" - }, - "provider": { - "type": "string" - }, - "type": { - "type": "string" - } - }, - "additionalProperties": false + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" } }, "additionalProperties": false } - ] + }, + "additionalProperties": false } } } } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "content" + ], + "properties": { + "content": { + "type": "string" + } + }, + "additionalProperties": false + } + } + }, + "required": true } - } - }, - "/projects/{projectId}/prompts": { - "get": { + }, + "delete": { "tags": [ "prompts" ], - "operationId": "prompts.listPrompts", + "operationId": "prompts.deletePrompt", "parameters": [ { "name": "projectId", @@ -8236,6 +18356,14 @@ "type": "string" }, "required": true + }, + { + "name": "kind", + "in": "path", + "schema": { + "type": "string" + }, + "required": true } ], "security": [], @@ -8373,154 +18501,81 @@ } } } - } - } - } - }, - "/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", + "401": { + "description": "Error", "content": { "application/json": { "schema": { "type": "object", "required": [ - "prompt", - "snapshot" + "error" ], "properties": { - "ok": { - "type": "boolean" - }, - "prompt": { + "error": { "type": "object", "required": [ - "absolutePath", - "bytes", - "content", - "exists", - "fileName", - "kind", - "relativePath" + "message", + "type" ], "properties": { - "absolutePath": { + "command": { "type": "string" }, - "bytes": { - "type": "number" + "details": { + "$id": "/schemas/unknown", + "title": "unknown" }, - "content": { + "message": { "type": "string" }, - "exists": { - "type": "boolean" - }, - "fileName": { + "provider": { "type": "string" }, - "kind": { - "type": "string", - "enum": [ - "claude", - "codex", - "gemini", - "grok" - ] - }, - "relativePath": { + "type": { "type": "string" } }, "additionalProperties": false - }, - "snapshot": { + } + }, + "additionalProperties": false + } + } + } + }, + "404": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { "type": "object", "required": [ - "projectDir", - "projectId", - "projectKey", - "prompts" + "message", + "type" ], "properties": { - "projectDir": { + "command": { "type": "string" }, - "projectId": { + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { "type": "string" }, - "projectKey": { + "provider": { "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 - } + "type": { + "type": "string" } }, "additionalProperties": false @@ -8531,81 +18586,99 @@ } } }, - "400": { - "description": "The request did not match the expected schema", + "409": { + "description": "Error", "content": { "application/json": { "schema": { - "anyOf": [ - { - "$ref": "#/components/schemas/HttpApiDecodeError" - }, - { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { "type": "object", "required": [ - "error" + "message", + "type" ], "properties": { - "error": { - "type": "object", - "required": [ - "message", - "type" - ], - "properties": { - "command": { - "type": "string" - }, - "details": { - "$id": "/schemas/unknown", - "title": "unknown" - }, - "message": { - "type": "string" - }, - "provider": { - "type": "string" - }, - "type": { - "type": "string" - } - }, - "additionalProperties": false + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "500": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" } }, "additionalProperties": false } - ] + }, + "additionalProperties": false } } } } - }, - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "required": [ - "content" - ], - "properties": { - "content": { - "type": "string" - } - }, - "additionalProperties": false - } - } - }, - "required": true } - }, - "delete": { + } + }, + "/projects/{projectId}/skills": { + "get": { "tags": [ - "prompts" + "skills" ], - "operationId": "prompts.deletePrompt", + "operationId": "skills.listSkills", "parameters": [ { "name": "projectId", @@ -8614,14 +18687,6 @@ "type": "string" }, "required": true - }, - { - "name": "kind", - "in": "path", - "schema": { - "type": "string" - }, - "required": true } ], "security": [], @@ -8645,7 +18710,8 @@ "projectDir", "projectId", "projectKey", - "prompts" + "scopes", + "skills" ], "properties": { "projectDir": { @@ -8657,7 +18723,39 @@ "projectKey": { "type": "string" }, - "prompts": { + "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", @@ -8665,10 +18763,11 @@ "absolutePath", "bytes", "content", - "exists", - "fileName", - "kind", - "relativePath" + "id", + "name", + "relativePath", + "scope", + "updatedAtIso" ], "properties": { "absolutePath": { @@ -8680,23 +18779,36 @@ "content": { "type": "string" }, - "exists": { - "type": "boolean" + "id": { + "type": "string" }, - "fileName": { + "name": { "type": "string" }, - "kind": { + "relativePath": { + "type": "string" + }, + "scope": { "type": "string", "enum": [ - "claude", - "codex", - "gemini", - "grok" + "skills", + "agents/skills", + "agents/.skills", + "claude/skills", + "codex/skills", + "gemini/skills", + "grok/skills" ] }, - "relativePath": { - "type": "string" + "updatedAtIso": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] } }, "additionalProperties": false @@ -8759,150 +18871,123 @@ } } } - } - } - } - }, - "/projects/{projectId}/skills": { - "get": { - "tags": [ - "skills" - ], - "operationId": "skills.listSkills", - "parameters": [ - { - "name": "projectId", - "in": "path", - "schema": { - "type": "string" - }, - "required": true - } - ], - "security": [], - "responses": { - "200": { - "description": "Success", + }, + "401": { + "description": "Error", "content": { "application/json": { "schema": { "type": "object", "required": [ - "snapshot" + "error" ], "properties": { - "ok": { - "type": "boolean" - }, - "snapshot": { + "error": { "type": "object", "required": [ - "projectDir", - "projectId", - "projectKey", - "scopes", - "skills" + "message", + "type" ], "properties": { - "projectDir": { + "command": { "type": "string" }, - "projectId": { + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { "type": "string" }, - "projectKey": { + "provider": { "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 - } + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "404": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "409": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" }, - "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 - } + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" } }, "additionalProperties": false @@ -8913,51 +18998,44 @@ } } }, - "400": { - "description": "The request did not match the expected schema", + "500": { + "description": "Error", "content": { "application/json": { "schema": { - "anyOf": [ - { - "$ref": "#/components/schemas/HttpApiDecodeError" - }, - { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { "type": "object", "required": [ - "error" + "message", + "type" ], "properties": { - "error": { - "type": "object", - "required": [ - "message", - "type" - ], - "properties": { - "command": { - "type": "string" - }, - "details": { - "$id": "/schemas/unknown", - "title": "unknown" - }, - "message": { - "type": "string" - }, - "provider": { - "type": "string" - }, - "type": { - "type": "string" - } - }, - "additionalProperties": false + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" } }, "additionalProperties": false } - ] + }, + "additionalProperties": false } } } @@ -9185,36 +19263,204 @@ "error" ], "properties": { - "error": { - "type": "object", - "required": [ - "message", - "type" - ], - "properties": { - "command": { - "type": "string" - }, - "details": { - "$id": "/schemas/unknown", - "title": "unknown" - }, - "message": { - "type": "string" - }, - "provider": { - "type": "string" - }, - "type": { - "type": "string" - } - }, - "additionalProperties": false + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + ] + } + } + } + }, + "401": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "404": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "409": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "500": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" } }, "additionalProperties": false } - ] + }, + "additionalProperties": false } } } @@ -9472,6 +19718,174 @@ } } } + }, + "401": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "404": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "409": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "500": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } } } } @@ -9515,13 +19929,105 @@ "snapshot": { "type": "object", "required": [ + "agents", "containerName", "generatedAt", "projectId", "sshConnections", - "tasks" + "tasks", + "terminalSessions" ], "properties": { + "agents": { + "type": "array", + "items": { + "type": "object", + "required": [ + "id", + "projectId", + "provider", + "label", + "command", + "containerName", + "status", + "source", + "pidFile", + "hostPid", + "startedAt", + "updatedAt" + ], + "properties": { + "id": { + "type": "string" + }, + "projectId": { + "type": "string" + }, + "provider": { + "type": "string", + "enum": [ + "codex", + "opencode", + "claude", + "grok", + "custom" + ] + }, + "label": { + "type": "string" + }, + "command": { + "type": "string" + }, + "containerName": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "starting", + "running", + "stopping", + "stopped", + "exited", + "failed" + ] + }, + "source": { + "type": "string" + }, + "pidFile": { + "type": "string" + }, + "hostPid": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ] + }, + "startedAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + }, + "stoppedAt": { + "type": "string" + }, + "exitCode": { + "type": "number" + }, + "signal": { + "type": "string" + } + }, + "additionalProperties": false + } + }, "containerName": { "type": "string" }, @@ -9593,6 +20099,58 @@ }, "additionalProperties": false } + }, + "terminalSessions": { + "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 @@ -9651,6 +20209,174 @@ } } } + }, + "401": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "404": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "409": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "500": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } } } } @@ -9681,8 +20407,27 @@ ], "security": [], "responses": { - "204": { - "description": "Success" + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "ok" + ], + "properties": { + "ok": { + "type": "boolean", + "enum": [ + true + ] + } + }, + "additionalProperties": false + } + } + } }, "400": { "description": "The request did not match the expected schema", @@ -9728,7 +20473,175 @@ }, "additionalProperties": false } - ] + ] + } + } + } + }, + "401": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "404": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "409": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "500": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false } } } @@ -9837,6 +20750,174 @@ } } } + }, + "401": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "404": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "409": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "500": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } } } } @@ -9987,7 +21068,175 @@ }, "additionalProperties": false } - ] + ] + } + } + } + }, + "401": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "404": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "409": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "500": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false } } } @@ -10143,6 +21392,174 @@ } } } + }, + "401": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "404": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "409": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "500": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } } }, "requestBody": { @@ -10314,6 +21731,174 @@ } } } + }, + "401": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "404": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "409": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } + }, + "500": { + "description": "Error", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "error" + ], + "properties": { + "error": { + "type": "object", + "required": [ + "message", + "type" + ], + "properties": { + "command": { + "type": "string" + }, + "details": { + "$id": "/schemas/unknown", + "title": "unknown" + }, + "message": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + } } } } diff --git a/packages/openapi/src/openapi-paths.ts b/packages/openapi/src/openapi-paths.ts index 19bc4aab..21e1bdd6 100644 --- a/packages/openapi/src/openapi-paths.ts +++ b/packages/openapi/src/openapi-paths.ts @@ -953,6 +953,78 @@ export interface operations { }; }; }; + /** @description Error */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; }; }; "projects.listProjects": { @@ -1007,6 +1079,78 @@ export interface operations { }; }; }; + /** @description Error */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; }; }; "projects.createProject": { @@ -1139,37 +1283,13 @@ export interface operations { }; }; }; - }; - }; - "projects.applyAllProjects": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": { - activeOnly?: boolean; - }; - }; - }; - responses: { - /** @description Success */ - 204: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description The request did not match the expected schema */ - 400: { + /** @description Error */ + 401: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["HttpApiDecodeError"] | { + "application/json": { error: { command?: string; /** unknown */ @@ -1181,31 +1301,13 @@ export interface operations { }; }; }; - }; - }; - "projects.downAllProjects": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Success */ - 204: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description The request did not match the expected schema */ - 400: { + /** @description Error */ + 404: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["HttpApiDecodeError"] | { + "application/json": { error: { command?: string; /** unknown */ @@ -1217,66 +1319,31 @@ export interface operations { }; }; }; - }; - }; - "projects.getProject": { - parameters: { - query?: never; - header?: never; - path: { - projectId: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Success */ - 200: { + /** @description Error */ + 409: { headers: { [name: string]: unknown; }; content: { "application/json": { - ok?: boolean; - project: { - clonedOnHostname?: string; - containerName: string; - displayName: string; - id: string; - projectKey: string; - repoRef: string; - repoUrl: string; - sshSessions: number; - startedAtEpochMs: number | null; - startedAtIso: string | null; - /** @enum {string} */ - status: "running" | "stopped" | "unknown"; - statusLabel: string; - authorizedKeysExists: boolean; - authorizedKeysPath: string; - codexAuthPath: string; - codexHome: string; - envGlobalPath: string; - envProjectPath: string; - /** @enum {string} */ - gpu: "none" | "all"; - projectDir: string; - serviceName: string; - sshCommand: string; - sshPort: number; - sshUser: string; - targetDir: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; }; }; }; }; - /** @description The request did not match the expected schema */ - 400: { + /** @description Error */ + 500: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["HttpApiDecodeError"] | { + "application/json": { error: { command?: string; /** unknown */ @@ -1290,23 +1357,32 @@ export interface operations { }; }; }; - "projects.deleteProject": { + "projects.applyAllProjects": { parameters: { query?: never; header?: never; - path: { - projectId: string; - }; + path?: never; cookie?: never; }; - requestBody?: never; + requestBody: { + content: { + "application/json": { + activeOnly?: boolean; + }; + }; + }; responses: { /** @description Success */ - 204: { + 200: { headers: { [name: string]: unknown; }; - content?: never; + content: { + "application/json": { + /** @enum {boolean} */ + ok: true; + }; + }; }; /** @description The request did not match the expected schema */ 400: { @@ -1326,33 +1402,31 @@ export interface operations { }; }; }; - }; - }; - "projects.downProject": { - parameters: { - query?: never; - header?: never; - path: { - projectId: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Success */ - 204: { + /** @description Error */ + 401: { headers: { [name: string]: unknown; }; - content?: never; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; }; - /** @description The request did not match the expected schema */ - 400: { + /** @description Error */ + 404: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["HttpApiDecodeError"] | { + "application/json": { error: { command?: string; /** unknown */ @@ -1364,77 +1438,31 @@ export interface operations { }; }; }; - }; - }; - "projects.applyProject": { - parameters: { - query?: never; - header?: never; - path: { - projectId: string; - }; - cookie?: never; - }; - requestBody: { - content: { - "application/json": { - cpuLimit?: string; - ramLimit?: string; - playwrightCpuLimit?: string; - playwrightRamLimit?: string; - /** @enum {string} */ - gpu?: "none" | "all"; - }; - }; - }; - responses: { - /** @description Success */ - 200: { + /** @description Error */ + 409: { headers: { [name: string]: unknown; }; content: { "application/json": { - ok?: boolean; - project: { - clonedOnHostname?: string; - containerName: string; - displayName: string; - id: string; - projectKey: string; - repoRef: string; - repoUrl: string; - sshSessions: number; - startedAtEpochMs: number | null; - startedAtIso: string | null; - /** @enum {string} */ - status: "running" | "stopped" | "unknown"; - statusLabel: string; - authorizedKeysExists: boolean; - authorizedKeysPath: string; - codexAuthPath: string; - codexHome: string; - envGlobalPath: string; - envProjectPath: string; - /** @enum {string} */ - gpu: "none" | "all"; - projectDir: string; - serviceName: string; - sshCommand: string; - sshPort: number; - sshUser: string; - targetDir: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; }; }; }; }; - /** @description The request did not match the expected schema */ - 400: { + /** @description Error */ + 500: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["HttpApiDecodeError"] | { + "application/json": { error: { command?: string; /** unknown */ @@ -1448,23 +1476,14 @@ export interface operations { }; }; }; - "projects.upProject": { + "projects.downAllProjects": { parameters: { query?: never; header?: never; - path: { - projectId: string; - }; + path?: never; cookie?: never; }; - requestBody: { - content: { - "application/json": { - authorizedKeysContents?: string; - useManagedAuthorizedKeys?: boolean; - }; - }; - }; + requestBody?: never; responses: { /** @description Success */ 200: { @@ -1473,36 +1492,8 @@ export interface operations { }; content: { "application/json": { - ok?: boolean; - project: { - clonedOnHostname?: string; - containerName: string; - displayName: string; - id: string; - projectKey: string; - repoRef: string; - repoUrl: string; - sshSessions: number; - startedAtEpochMs: number | null; - startedAtIso: string | null; - /** @enum {string} */ - status: "running" | "stopped" | "unknown"; - statusLabel: string; - authorizedKeysExists: boolean; - authorizedKeysPath: string; - codexAuthPath: string; - codexHome: string; - envGlobalPath: string; - envProjectPath: string; - /** @enum {string} */ - gpu: "none" | "all"; - projectDir: string; - serviceName: string; - sshCommand: string; - sshPort: number; - sshUser: string; - targetDir: string; - }; + /** @enum {boolean} */ + ok: true; }; }; }; @@ -1524,66 +1515,67 @@ export interface operations { }; }; }; - }; - }; - "projects.resumeProject": { - parameters: { - query?: never; - header?: never; - path: { - projectId: string; + /** @description Error */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Success */ - 200: { + /** @description Error */ + 404: { headers: { [name: string]: unknown; }; content: { "application/json": { - ok?: boolean; - project: { - clonedOnHostname?: string; - containerName: string; - displayName: string; - id: string; - projectKey: string; - repoRef: string; - repoUrl: string; - sshSessions: number; - startedAtEpochMs: number | null; - startedAtIso: string | null; - /** @enum {string} */ - status: "running" | "stopped" | "unknown"; - statusLabel: string; - authorizedKeysExists: boolean; - authorizedKeysPath: string; - codexAuthPath: string; - codexHome: string; - envGlobalPath: string; - envProjectPath: string; - /** @enum {string} */ - gpu: "none" | "all"; - projectDir: string; - serviceName: string; - sshCommand: string; - sshPort: number; - sshUser: string; - targetDir: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; }; }; }; }; - /** @description The request did not match the expected schema */ - 400: { + /** @description Error */ + 409: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["HttpApiDecodeError"] | { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { error: { command?: string; /** unknown */ @@ -1597,7 +1589,7 @@ export interface operations { }; }; }; - "projects.suspendProject": { + "projects.getProject": { parameters: { query?: never; header?: never; @@ -1666,37 +1658,31 @@ export interface operations { }; }; }; - }; - }; - "projects.projectPs": { - parameters: { - query?: never; - header?: never; - path: { - projectId: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Success */ - 200: { + /** @description Error */ + 401: { headers: { [name: string]: unknown; }; content: { "application/json": { - output: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; - /** @description The request did not match the expected schema */ - 400: { + /** @description Error */ + 404: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["HttpApiDecodeError"] | { + "application/json": { error: { command?: string; /** unknown */ @@ -1708,37 +1694,31 @@ export interface operations { }; }; }; - }; - }; - "projects.projectLogs": { - parameters: { - query?: never; - header?: never; - path: { - projectId: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Success */ - 200: { + /** @description Error */ + 409: { headers: { [name: string]: unknown; }; content: { "application/json": { - output: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; - /** @description The request did not match the expected schema */ - 400: { + /** @description Error */ + 500: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["HttpApiDecodeError"] | { + "application/json": { error: { command?: string; /** unknown */ @@ -1752,7 +1732,7 @@ export interface operations { }; }; }; - "projectPorts.listProjectPorts": { + "projects.deleteProject": { parameters: { query?: never; header?: never; @@ -1770,22 +1750,8 @@ export interface operations { }; content: { "application/json": { - forwards: { - bindHost: string; - containerName: string; - createdAt: string | null; - hostPort: number; - id: string; - projectId: string; - projectKey: string; - proxyPath: string; - publicHost: string; - /** @enum {string} */ - status: "running" | "stopped" | "unknown"; - targetContainerName: string; - targetPort: number; - url: string; - }[]; + /** @enum {boolean} */ + ok: true; }; }; }; @@ -1807,59 +1773,31 @@ export interface operations { }; }; }; - }; - }; - "projectPorts.createProjectPort": { - parameters: { - query?: never; - header?: never; - path: { - projectId: string; - }; - cookie?: never; - }; - requestBody: { - content: { - "application/json": { - hostPort?: number; - targetPort: number; - }; - }; - }; - responses: { - /** @description Success */ - 201: { + /** @description Error */ + 401: { headers: { [name: string]: unknown; }; content: { "application/json": { - forward: { - bindHost: string; - containerName: string; - createdAt: string | null; - hostPort: number; - id: string; - projectId: string; - projectKey: string; - proxyPath: string; - publicHost: string; - /** @enum {string} */ - status: "running" | "stopped" | "unknown"; - targetContainerName: string; - targetPort: number; - url: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; }; }; }; }; - /** @description The request did not match the expected schema */ - 400: { + /** @description Error */ + 404: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["HttpApiDecodeError"] | { + "application/json": { error: { command?: string; /** unknown */ @@ -1871,34 +1809,31 @@ export interface operations { }; }; }; - }; - }; - "projectPorts.deleteProjectPort": { - parameters: { - query?: never; - header?: never; - path: { - projectId: string; - targetPort: components["schemas"]["NumberFromString"]; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Success */ - 204: { + /** @description Error */ + 409: { headers: { [name: string]: unknown; }; - content?: never; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; }; - /** @description The request did not match the expected schema */ - 400: { + /** @description Error */ + 500: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["HttpApiDecodeError"] | { + "application/json": { error: { command?: string; /** unknown */ @@ -1912,7 +1847,7 @@ export interface operations { }; }; }; - "projectBrowser.readProjectBrowser": { + "projects.downProject": { parameters: { query?: never; header?: never; @@ -1930,17 +1865,8 @@ export interface operations { }; content: { "application/json": { - browser: { - cdpPath: string; - cdpUrl: string; - containerName: string; - noVncPath: string; - noVncUrl: string; - projectId: string; - projectKey: string; - /** @enum {string} */ - status: "running" | "stopped" | "missing" | "unknown"; - }; + /** @enum {boolean} */ + ok: true; }; }; }; @@ -1962,47 +1888,31 @@ export interface operations { }; }; }; - }; - }; - "projectBrowser.startProjectBrowser": { - parameters: { - query?: never; - header?: never; - path: { - projectId: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Success */ - 200: { + /** @description Error */ + 401: { headers: { [name: string]: unknown; }; content: { "application/json": { - browser: { - cdpPath: string; - cdpUrl: string; - containerName: string; - noVncPath: string; - noVncUrl: string; - projectId: string; - projectKey: string; - /** @enum {string} */ - status: "running" | "stopped" | "missing" | "unknown"; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; }; }; }; }; - /** @description The request did not match the expected schema */ - 400: { + /** @description Error */ + 404: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["HttpApiDecodeError"] | { + "application/json": { error: { command?: string; /** unknown */ @@ -2014,49 +1924,31 @@ export interface operations { }; }; }; - }; - }; - "projectDatabases.listDatabaseProfiles": { - parameters: { - query?: never; - header?: never; - path: { - projectId: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Success */ - 200: { + /** @description Error */ + 409: { headers: { [name: string]: unknown; }; content: { "application/json": { - profiles: { - createdAt: string; - database: string; - /** @enum {string} */ - engine: "postgres" | "mysql" | "mariadb"; - host: string; - id: string; - label: string; - maskedConnectionString: string; - port: number; - updatedAt: string; - user: string; - }[]; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; - /** @description The request did not match the expected schema */ - 400: { + /** @description Error */ + 500: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["HttpApiDecodeError"] | { + "application/json": { error: { command?: string; /** unknown */ @@ -2070,7 +1962,7 @@ export interface operations { }; }; }; - "projectDatabases.saveDatabaseProfile": { + "projects.applyProject": { parameters: { query?: never; header?: never; @@ -2082,74 +1974,56 @@ export interface operations { requestBody: { content: { "application/json": { - connectionString: string; - label?: string | null; + cpuLimit?: string; + ramLimit?: string; + playwrightCpuLimit?: string; + playwrightRamLimit?: string; + /** @enum {string} */ + gpu?: "none" | "all"; }; }; }; responses: { /** @description Success */ - 201: { + 200: { headers: { [name: string]: unknown; }; content: { "application/json": { - profile: { - createdAt: string; - database: string; - /** @enum {string} */ - engine: "postgres" | "mysql" | "mariadb"; - host: string; + ok?: boolean; + project: { + clonedOnHostname?: string; + containerName: string; + displayName: string; id: string; - label: string; - maskedConnectionString: string; - port: number; - updatedAt: string; - user: string; - }; - }; - }; - }; - /** @description The request did not match the expected schema */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["HttpApiDecodeError"] | { - error: { - command?: string; - /** unknown */ - details?: unknown; - message: string; - provider?: string; - type: string; + projectKey: string; + repoRef: string; + repoUrl: string; + sshSessions: number; + startedAtEpochMs: number | null; + startedAtIso: string | null; + /** @enum {string} */ + status: "running" | "stopped" | "unknown"; + statusLabel: string; + authorizedKeysExists: boolean; + authorizedKeysPath: string; + codexAuthPath: string; + codexHome: string; + envGlobalPath: string; + envProjectPath: string; + /** @enum {string} */ + gpu: "none" | "all"; + projectDir: string; + serviceName: string; + sshCommand: string; + sshPort: number; + sshUser: string; + targetDir: string; }; }; }; }; - }; - }; - "projectDatabases.deleteDatabaseProfile": { - parameters: { - query?: never; - header?: never; - path: { - projectId: string; - profileId: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Success */ - 204: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; /** @description The request did not match the expected schema */ 400: { headers: { @@ -2168,58 +2042,31 @@ export interface operations { }; }; }; - }; - }; - "projectDatabases.exposeDatabaseProfile": { - parameters: { - query?: never; - header?: never; - path: { - projectId: string; - profileId: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Success */ - 201: { + /** @description Error */ + 401: { headers: { [name: string]: unknown; }; content: { "application/json": { - forward: { - bindHost: string; - containerName: string; - createdAt: string | null; - database: string; - /** @enum {string} */ - engine: "postgres" | "mysql" | "mariadb"; - externalConnectionString: string; - hostPort: number; - id: string; - maskedExternalConnectionString: string; - profileId: string; - profileLabel: string; - projectId: string; - projectKey: string; - publicHost: string; - /** @enum {string} */ - status: "running" | "stopped" | "unknown"; - targetHost: string; - targetPort: number; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; }; }; }; }; - /** @description The request did not match the expected schema */ - 400: { + /** @description Error */ + 404: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["HttpApiDecodeError"] | { + "application/json": { error: { command?: string; /** unknown */ @@ -2231,34 +2078,31 @@ export interface operations { }; }; }; - }; - }; - "projectDatabases.deleteDatabaseForward": { - parameters: { - query?: never; - header?: never; - path: { - projectId: string; - profileId: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Success */ - 204: { + /** @description Error */ + 409: { headers: { [name: string]: unknown; }; - content?: never; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; }; - /** @description The request did not match the expected schema */ - 400: { + /** @description Error */ + 500: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["HttpApiDecodeError"] | { + "application/json": { error: { command?: string; /** unknown */ @@ -2272,7 +2116,7 @@ export interface operations { }; }; }; - "projectDatabases.listDatabaseForwards": { + "projects.upProject": { parameters: { query?: never; header?: never; @@ -2281,7 +2125,14 @@ export interface operations { }; cookie?: never; }; - requestBody?: never; + requestBody: { + content: { + "application/json": { + authorizedKeysContents?: string; + useManagedAuthorizedKeys?: boolean; + }; + }; + }; responses: { /** @description Success */ 200: { @@ -2290,27 +2141,36 @@ export interface operations { }; content: { "application/json": { - forwards: { - bindHost: string; + ok?: boolean; + project: { + clonedOnHostname?: string; containerName: string; - createdAt: string | null; - database: string; - /** @enum {string} */ - engine: "postgres" | "mysql" | "mariadb"; - externalConnectionString: string; - hostPort: number; + displayName: string; id: string; - maskedExternalConnectionString: string; - profileId: string; - profileLabel: string; - projectId: string; projectKey: string; - publicHost: string; + repoRef: string; + repoUrl: string; + sshSessions: number; + startedAtEpochMs: number | null; + startedAtIso: string | null; /** @enum {string} */ status: "running" | "stopped" | "unknown"; - targetHost: string; - targetPort: number; - }[]; + statusLabel: string; + authorizedKeysExists: boolean; + authorizedKeysPath: string; + codexAuthPath: string; + codexHome: string; + envGlobalPath: string; + envProjectPath: string; + /** @enum {string} */ + gpu: "none" | "all"; + projectDir: string; + serviceName: string; + sshCommand: string; + sshPort: number; + sshUser: string; + targetDir: string; + }; }; }; }; @@ -2332,46 +2192,31 @@ export interface operations { }; }; }; - }; - }; - "projectDatabases.readDatabaseSession": { - parameters: { - query?: never; - header?: never; - path: { - projectId: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Success */ - 200: { + /** @description Error */ + 401: { headers: { [name: string]: unknown; }; content: { "application/json": { - session: { - configHash: string; - containerName: string; - editorPath: string; - editorUrl: string; - projectId: string; - projectKey: string; - /** @enum {string} */ - status: "running" | "stopped" | "missing" | "unknown"; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; }; }; }; }; - /** @description The request did not match the expected schema */ - 400: { + /** @description Error */ + 404: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["HttpApiDecodeError"] | { + "application/json": { error: { command?: string; /** unknown */ @@ -2383,46 +2228,31 @@ export interface operations { }; }; }; - }; - }; - "projectDatabases.openDatabaseEditor": { - parameters: { - query?: never; - header?: never; - path: { - projectId: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Success */ - 200: { + /** @description Error */ + 409: { headers: { [name: string]: unknown; }; content: { "application/json": { - session: { - configHash: string; - containerName: string; - editorPath: string; - editorUrl: string; - projectId: string; - projectKey: string; - /** @enum {string} */ - status: "running" | "stopped" | "missing" | "unknown"; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; }; }; }; }; - /** @description The request did not match the expected schema */ - 400: { + /** @description Error */ + 500: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["HttpApiDecodeError"] | { + "application/json": { error: { command?: string; /** unknown */ @@ -2436,7 +2266,7 @@ export interface operations { }; }; }; - "projectDatabases.restartDatabaseEditor": { + "projects.resumeProject": { parameters: { query?: never; header?: never; @@ -2454,15 +2284,35 @@ export interface operations { }; content: { "application/json": { - session: { - configHash: string; + ok?: boolean; + project: { + clonedOnHostname?: string; containerName: string; - editorPath: string; - editorUrl: string; - projectId: string; + displayName: string; + id: string; projectKey: string; - /** @enum {string} */ - status: "running" | "stopped" | "missing" | "unknown"; + repoRef: string; + repoUrl: string; + sshSessions: number; + startedAtEpochMs: number | null; + startedAtIso: string | null; + /** @enum {string} */ + status: "running" | "stopped" | "unknown"; + statusLabel: string; + authorizedKeysExists: boolean; + authorizedKeysPath: string; + codexAuthPath: string; + codexHome: string; + envGlobalPath: string; + envProjectPath: string; + /** @enum {string} */ + gpu: "none" | "all"; + projectDir: string; + serviceName: string; + sshCommand: string; + sshPort: number; + sshUser: string; + targetDir: string; }; }; }; @@ -2485,45 +2335,31 @@ export interface operations { }; }; }; - }; - }; - "auth.githubStatus": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Success */ - 200: { + /** @description Error */ + 401: { headers: { [name: string]: unknown; }; content: { "application/json": { - ok?: boolean; - status: { - summary: string; - tokens: { - key: string; - label: string; - login: string | null; - /** @enum {string} */ - status: "valid" | "invalid" | "unknown"; - }[]; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; }; }; }; }; - /** @description The request did not match the expected schema */ - 400: { + /** @description Error */ + 404: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["HttpApiDecodeError"] | { + "application/json": { error: { command?: string; /** unknown */ @@ -2535,45 +2371,31 @@ export interface operations { }; }; }; - }; - }; - "auth.gitlabStatus": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Success */ - 200: { + /** @description Error */ + 409: { headers: { [name: string]: unknown; }; content: { "application/json": { - ok?: boolean; - status: { - summary: string; - tokens: { - key: string; - label: string; - login: string | null; - /** @enum {string} */ - status: "valid" | "invalid" | "unknown"; - }[]; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; }; }; }; }; - /** @description The request did not match the expected schema */ - 400: { + /** @description Error */ + 500: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["HttpApiDecodeError"] | { + "application/json": { error: { command?: string; /** unknown */ @@ -2587,11 +2409,13 @@ export interface operations { }; }; }; - "auth.gitStatus": { + "projects.suspendProject": { parameters: { query?: never; header?: never; - path?: never; + path: { + projectId: string; + }; cookie?: never; }; requestBody?: never; @@ -2604,12 +2428,34 @@ export interface operations { content: { "application/json": { ok?: boolean; - status: { - connections: { - host: string; - user: string; - }[]; - summary: string; + project: { + clonedOnHostname?: string; + containerName: string; + displayName: string; + id: string; + projectKey: string; + repoRef: string; + repoUrl: string; + sshSessions: number; + startedAtEpochMs: number | null; + startedAtIso: string | null; + /** @enum {string} */ + status: "running" | "stopped" | "unknown"; + statusLabel: string; + authorizedKeysExists: boolean; + authorizedKeysPath: string; + codexAuthPath: string; + codexHome: string; + envGlobalPath: string; + envProjectPath: string; + /** @enum {string} */ + gpu: "none" | "all"; + projectDir: string; + serviceName: string; + sshCommand: string; + sshPort: number; + sshUser: string; + targetDir: string; }; }; }; @@ -2632,45 +2478,31 @@ export interface operations { }; }; }; - }; - }; - "auth.grokStatus": { - parameters: { - query?: { - label?: string; - }; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Success */ - 200: { + /** @description Error */ + 401: { headers: { [name: string]: unknown; }; content: { "application/json": { - ok?: boolean; - status: { - authPath: string; - connected: boolean; - label: string; + error: { + command?: string; + /** unknown */ + details?: unknown; message: string; - /** @enum {string} */ - method: "none" | "api-key" | "oauth"; + provider?: string; + type: string; }; }; }; }; - /** @description The request did not match the expected schema */ - 400: { + /** @description Error */ + 404: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["HttpApiDecodeError"] | { + "application/json": { error: { command?: string; /** unknown */ @@ -2682,44 +2514,31 @@ export interface operations { }; }; }; - }; - }; - "auth.codexStatus": { - parameters: { - query?: { - label?: string; - }; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Success */ - 200: { + /** @description Error */ + 409: { headers: { [name: string]: unknown; }; content: { "application/json": { - ok?: boolean; - status: { - account: string | null; - authPath: string; - label: string; + error: { + command?: string; + /** unknown */ + details?: unknown; message: string; - present: boolean; + provider?: string; + type: string; }; }; }; }; - /** @description The request did not match the expected schema */ - 400: { + /** @description Error */ + 500: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["HttpApiDecodeError"] | { + "application/json": { error: { command?: string; /** unknown */ @@ -2733,41 +2552,25 @@ export interface operations { }; }; }; - "auth.githubLogin": { + "projects.projectPs": { parameters: { query?: never; header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": { - label?: string | null; - token?: string | null; - scopes?: string | null; - }; + path: { + projectId: string; }; + cookie?: never; }; + requestBody?: never; responses: { /** @description Success */ - 201: { + 200: { headers: { [name: string]: unknown; }; content: { "application/json": { - ok?: boolean; - status: { - summary: string; - tokens: { - key: string; - label: string; - login: string | null; - /** @enum {string} */ - status: "valid" | "invalid" | "unknown"; - }[]; - }; + output: string; }; }; }; @@ -2789,51 +2592,31 @@ export interface operations { }; }; }; - }; - }; - "auth.githubLogout": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": { - label?: string | null; + /** @description Error */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; }; }; - }; - responses: { - /** @description Success */ - 200: { + /** @description Error */ + 404: { headers: { [name: string]: unknown; }; content: { "application/json": { - ok?: boolean; - status: { - summary: string; - tokens: { - key: string; - label: string; - login: string | null; - /** @enum {string} */ - status: "valid" | "invalid" | "unknown"; - }[]; - }; - }; - }; - }; - /** @description The request did not match the expected schema */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["HttpApiDecodeError"] | { error: { command?: string; /** unknown */ @@ -2845,52 +2628,31 @@ export interface operations { }; }; }; - }; - }; - "auth.gitlabLogin": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": { - label?: string | null; - token?: string | null; - }; - }; - }; - responses: { - /** @description Success */ - 201: { + /** @description Error */ + 409: { headers: { [name: string]: unknown; }; content: { "application/json": { - ok?: boolean; - status: { - summary: string; - tokens: { - key: string; - label: string; - login: string | null; - /** @enum {string} */ - status: "valid" | "invalid" | "unknown"; - }[]; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; }; }; }; }; - /** @description The request did not match the expected schema */ - 400: { + /** @description Error */ + 500: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["HttpApiDecodeError"] | { + "application/json": { error: { command?: string; /** unknown */ @@ -2904,20 +2666,16 @@ export interface operations { }; }; }; - "auth.gitlabLogout": { + "projects.projectLogs": { parameters: { query?: never; header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": { - label?: string | null; - }; + path: { + projectId: string; }; + cookie?: never; }; + requestBody?: never; responses: { /** @description Success */ 200: { @@ -2926,17 +2684,7 @@ export interface operations { }; content: { "application/json": { - ok?: boolean; - status: { - summary: string; - tokens: { - key: string; - label: string; - login: string | null; - /** @enum {string} */ - status: "valid" | "invalid" | "unknown"; - }[]; - }; + output: string; }; }; }; @@ -2958,50 +2706,31 @@ export interface operations { }; }; }; - }; - }; - "auth.gitLogin": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": { - host: string; - token?: string | null; - user?: string | null; - }; - }; - }; - responses: { - /** @description Success */ - 201: { + /** @description Error */ + 401: { headers: { [name: string]: unknown; }; content: { "application/json": { - ok?: boolean; - status: { - connections: { - host: string; - user: string; - }[]; - summary: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; }; }; }; }; - /** @description The request did not match the expected schema */ - 400: { + /** @description Error */ + 404: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["HttpApiDecodeError"] | { + "application/json": { error: { command?: string; /** unknown */ @@ -3013,48 +2742,31 @@ export interface operations { }; }; }; - }; - }; - "auth.gitLogout": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": { - host: string; - }; - }; - }; - responses: { - /** @description Success */ - 200: { + /** @description Error */ + 409: { headers: { [name: string]: unknown; }; content: { "application/json": { - ok?: boolean; - status: { - connections: { - host: string; - user: string; - }[]; - summary: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; }; }; }; }; - /** @description The request did not match the expected schema */ - 400: { + /** @description Error */ + 500: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["HttpApiDecodeError"] | { + "application/json": { error: { command?: string; /** unknown */ @@ -3068,11 +2780,13 @@ export interface operations { }; }; }; - "auth.authMenu": { + "projectPorts.listProjectPorts": { parameters: { query?: never; header?: never; - path?: never; + path: { + projectId: string; + }; cookie?: never; }; requestBody?: never; @@ -3084,22 +2798,22 @@ export interface operations { }; content: { "application/json": { - ok?: boolean; - snapshot: { - claudeAuthEntries: number; - claudeAuthPath: string; - codexAuthEntries?: number; - codexAuthPath?: string; - geminiAuthEntries: number; - geminiAuthPath: string; - gitTokenEntries: number; - gitUserEntries: number; - githubTokenEntries: number; - globalEnvPath: string; - grokAuthEntries?: number; - grokAuthPath?: string; - totalEntries: number; - }; + forwards: { + bindHost: string; + containerName: string; + createdAt: string | null; + hostPort: number; + id: string; + projectId: string; + projectKey: string; + proxyPath: string; + publicHost: string; + /** @enum {string} */ + status: "running" | "stopped" | "unknown"; + targetContainerName: string; + targetPort: number; + url: string; + }[]; }; }; }; @@ -3121,61 +2835,31 @@ export interface operations { }; }; }; - }; - }; - "auth.authMenuAction": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": { - /** @enum {string} */ - flow: "GithubRemove" | "GitSet" | "GitRemove" | "ClaudeLogout" | "GeminiApiKey" | "GeminiLogout" | "GrokApiKey" | "GrokLogout"; - label?: string | null; - token?: string | null; - user?: string | null; - apiKey?: string | null; - }; - }; - }; - responses: { - /** @description Success */ - 200: { + /** @description Error */ + 401: { headers: { [name: string]: unknown; }; content: { "application/json": { - ok?: boolean; - snapshot: { - claudeAuthEntries: number; - claudeAuthPath: string; - codexAuthEntries?: number; - codexAuthPath?: string; - geminiAuthEntries: number; - geminiAuthPath: string; - gitTokenEntries: number; - gitUserEntries: number; - githubTokenEntries: number; - globalEnvPath: string; - grokAuthEntries?: number; - grokAuthPath?: string; - totalEntries: number; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; }; }; }; }; - /** @description The request did not match the expected schema */ - 400: { + /** @description Error */ + 404: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["HttpApiDecodeError"] | { + "application/json": { error: { command?: string; /** unknown */ @@ -3187,56 +2871,31 @@ export interface operations { }; }; }; - }; - }; - "auth.authTerminalSession": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": { - /** @enum {string} */ - flow: "ClaudeOauth" | "GeminiOauth" | "GrokOauth"; - label?: string | null; - }; - }; - }; - responses: { - /** @description Success */ - 201: { + /** @description Error */ + 409: { headers: { [name: string]: unknown; }; content: { "application/json": { - ok?: boolean; - session: { - attachedClients?: number; - closedAt?: string; - createdAt: string; - exitCode?: number; - id: string; - projectId: string; - signal?: number; - sshCommand: string; - startedAt?: string; - /** @enum {string} */ - status: "ready" | "attached" | "exited" | "failed"; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; }; }; }; }; - /** @description The request did not match the expected schema */ - 400: { + /** @description Error */ + 500: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["HttpApiDecodeError"] | { + "application/json": { error: { command?: string; /** unknown */ @@ -3250,18 +2909,20 @@ export interface operations { }; }; }; - "auth.codexImport": { + "projectPorts.createProjectPort": { parameters: { query?: never; header?: never; - path?: never; + path: { + projectId: string; + }; cookie?: never; }; requestBody: { content: { "application/json": { - label?: string | null; - authText: string; + hostPort?: number; + targetPort: number; }; }; }; @@ -3273,13 +2934,21 @@ export interface operations { }; content: { "application/json": { - ok?: boolean; - status: { - account: string | null; - authPath: string; - label: string; - message: string; - present: boolean; + forward: { + bindHost: string; + containerName: string; + createdAt: string | null; + hostPort: number; + id: string; + projectId: string; + projectKey: string; + proxyPath: string; + publicHost: string; + /** @enum {string} */ + status: "running" | "stopped" | "unknown"; + targetContainerName: string; + targetPort: number; + url: string; }; }; }; @@ -3302,48 +2971,31 @@ export interface operations { }; }; }; - }; - }; - "auth.codexLogout": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": { - label?: string | null; - }; - }; - }; - responses: { - /** @description Success */ - 200: { + /** @description Error */ + 401: { headers: { [name: string]: unknown; }; content: { "application/json": { - ok?: boolean; - status: { - account: string | null; - authPath: string; - label: string; + error: { + command?: string; + /** unknown */ + details?: unknown; message: string; - present: boolean; + provider?: string; + type: string; }; }; }; }; - /** @description The request did not match the expected schema */ - 400: { + /** @description Error */ + 404: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["HttpApiDecodeError"] | { + "application/json": { error: { command?: string; /** unknown */ @@ -3355,49 +3007,31 @@ export interface operations { }; }; }; - }; - }; - "auth.grokLogout": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": { - label?: string | null; - }; - }; - }; - responses: { - /** @description Success */ - 200: { + /** @description Error */ + 409: { headers: { [name: string]: unknown; }; content: { "application/json": { - ok?: boolean; - status: { - authPath: string; - connected: boolean; - label: string; + error: { + command?: string; + /** unknown */ + details?: unknown; message: string; - /** @enum {string} */ - method: "none" | "api-key" | "oauth"; + provider?: string; + type: string; }; }; }; }; - /** @description The request did not match the expected schema */ - 400: { + /** @description Error */ + 500: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["HttpApiDecodeError"] | { + "application/json": { error: { command?: string; /** unknown */ @@ -3411,12 +3045,13 @@ export interface operations { }; }; }; - "projectAuth.projectAuth": { + "projectPorts.deleteProjectPort": { parameters: { query?: never; header?: never; path: { projectId: string; + targetPort: components["schemas"]["NumberFromString"]; }; cookie?: never; }; @@ -3429,29 +3064,8 @@ export interface operations { }; content: { "application/json": { - ok?: boolean; - snapshot: { - activeClaudeLabel: string | null; - activeGeminiLabel: string | null; - activeGitLabel: string | null; - activeGithubLabel: string | null; - activeGrokLabel: string | null; - claudeAuthEntries: number; - claudeAuthPath: string; - codexAuthEntries?: number; - codexAuthPath?: string; - envGlobalPath: string; - envProjectPath: string; - geminiAuthEntries: number; - geminiAuthPath: string; - gitTokenEntries: number; - githubTokenEntries: number; - grokAuthEntries?: number; - grokAuthPath?: string; - projectDir: string; - projectName: string; - totalEntries: number; - }; + /** @enum {boolean} */ + ok: true; }; }; }; @@ -3473,67 +3087,67 @@ export interface operations { }; }; }; - }; - }; - "projectAuth.projectAuthAction": { - parameters: { - query?: never; - header?: never; - path: { - projectId: string; + /** @description Error */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; }; - cookie?: never; - }; - requestBody: { - content: { - "application/json": { - /** @enum {string} */ - flow: "ProjectGithubConnect" | "ProjectGithubDisconnect" | "ProjectGitConnect" | "ProjectGitDisconnect" | "ProjectClaudeConnect" | "ProjectClaudeDisconnect" | "ProjectGeminiConnect" | "ProjectGeminiDisconnect" | "ProjectGrokConnect" | "ProjectGrokDisconnect"; - label?: string | null; + /** @description Error */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; }; }; - }; - responses: { - /** @description Success */ - 200: { + /** @description Error */ + 409: { headers: { [name: string]: unknown; }; content: { "application/json": { - ok?: boolean; - snapshot: { - activeClaudeLabel: string | null; - activeGeminiLabel: string | null; - activeGitLabel: string | null; - activeGithubLabel: string | null; - activeGrokLabel: string | null; - claudeAuthEntries: number; - claudeAuthPath: string; - codexAuthEntries?: number; - codexAuthPath?: string; - envGlobalPath: string; - envProjectPath: string; - geminiAuthEntries: number; - geminiAuthPath: string; - gitTokenEntries: number; - githubTokenEntries: number; - grokAuthEntries?: number; - grokAuthPath?: string; - projectDir: string; - projectName: string; - totalEntries: number; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; }; }; }; }; - /** @description The request did not match the expected schema */ - 400: { + /** @description Error */ + 500: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["HttpApiDecodeError"] | { + "application/json": { error: { command?: string; /** unknown */ @@ -3547,12 +3161,12 @@ export interface operations { }; }; }; - "terminal.listTerminalsByKey": { + "projectBrowser.readProjectBrowser": { parameters: { query?: never; header?: never; path: { - projectKey: string; + projectId: string; }; cookie?: never; }; @@ -3565,20 +3179,17 @@ export interface operations { }; content: { "application/json": { - activeSessionId: string | null; - sessions: { - attachedClients?: number; - closedAt?: string; - createdAt: string; - exitCode?: number; - id: string; + browser: { + cdpPath: string; + cdpUrl: string; + containerName: string; + noVncPath: string; + noVncUrl: string; projectId: string; - signal?: number; - sshCommand: string; - startedAt?: string; + projectKey: string; /** @enum {string} */ - status: "ready" | "attached" | "exited" | "failed"; - }[]; + status: "running" | "stopped" | "missing" | "unknown"; + }; }; }; }; @@ -3600,79 +3211,31 @@ export interface operations { }; }; }; - }; - }; - "terminal.createTerminalByKey": { - parameters: { - query?: never; - header?: never; - path: { - projectKey: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Success */ - 201: { + /** @description Error */ + 401: { headers: { [name: string]: unknown; }; content: { "application/json": { - ok?: boolean; - project: { - clonedOnHostname?: string; - containerName: string; - displayName: string; - id: string; - projectKey: string; - repoRef: string; - repoUrl: string; - sshSessions: number; - startedAtEpochMs: number | null; - startedAtIso: string | null; - /** @enum {string} */ - status: "running" | "stopped" | "unknown"; - statusLabel: string; - authorizedKeysExists: boolean; - authorizedKeysPath: string; - codexAuthPath: string; - codexHome: string; - envGlobalPath: string; - envProjectPath: string; - /** @enum {string} */ - gpu: "none" | "all"; - projectDir: string; - serviceName: string; - sshCommand: string; - sshPort: number; - sshUser: string; - targetDir: string; - }; - session: { - attachedClients?: number; - closedAt?: string; - createdAt: string; - exitCode?: number; - id: string; - projectId: string; - signal?: number; - sshCommand: string; - startedAt?: string; - /** @enum {string} */ - status: "ready" | "attached" | "exited" | "failed"; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; }; }; }; }; - /** @description The request did not match the expected schema */ - 400: { + /** @description Error */ + 404: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["HttpApiDecodeError"] | { + "application/json": { error: { command?: string; /** unknown */ @@ -3684,47 +3247,31 @@ export interface operations { }; }; }; - }; - }; - "terminal.startTerminalByKey": { - parameters: { - query?: never; - header?: never; - path: { - projectKey: string; - }; - cookie?: never; - }; - requestBody: { - content: { - "application/json": { - requestId: components["schemas"]["UUID"]; - }; - }; - }; - responses: { - /** @description Success */ - 202: { + /** @description Error */ + 409: { headers: { [name: string]: unknown; }; content: { "application/json": { - /** @enum {boolean} */ - accepted: true; - cursor: number; - projectId: string; - requestId: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; - /** @description The request did not match the expected schema */ - 400: { + /** @description Error */ + 500: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["HttpApiDecodeError"] | { + "application/json": { error: { command?: string; /** unknown */ @@ -3738,13 +3285,12 @@ export interface operations { }; }; }; - "terminal.getTerminalByKey": { + "projectBrowser.startProjectBrowser": { parameters: { query?: never; header?: never; path: { - projectKey: string; - sessionId: string; + projectId: string; }; cookie?: never; }; @@ -3757,18 +3303,16 @@ export interface operations { }; content: { "application/json": { - session: { - attachedClients?: number; - closedAt?: string; - createdAt: string; - exitCode?: number; - id: string; + browser: { + cdpPath: string; + cdpUrl: string; + containerName: string; + noVncPath: string; + noVncUrl: string; projectId: string; - signal?: number; - sshCommand: string; - startedAt?: string; + projectKey: string; /** @enum {string} */ - status: "ready" | "attached" | "exited" | "failed"; + status: "running" | "stopped" | "missing" | "unknown"; }; }; }; @@ -3791,34 +3335,31 @@ export interface operations { }; }; }; - }; - }; - "terminal.deleteTerminalByKey": { - parameters: { - query?: never; - header?: never; - path: { - projectKey: string; - sessionId: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Success */ - 204: { + /** @description Error */ + 401: { headers: { [name: string]: unknown; }; - content?: never; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; }; - /** @description The request did not match the expected schema */ - 400: { + /** @description Error */ + 404: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["HttpApiDecodeError"] | { + "application/json": { error: { command?: string; /** unknown */ @@ -3830,55 +3371,31 @@ export interface operations { }; }; }; - }; - }; - "terminal.setActiveTerminalByKey": { - parameters: { - query?: never; - header?: never; - path: { - projectKey: string; - }; - cookie?: never; - }; - requestBody: { - content: { - "application/json": { - sessionId: string; - }; - }; - }; - responses: { - /** @description Success */ - 200: { + /** @description Error */ + 409: { headers: { [name: string]: unknown; }; content: { "application/json": { - session: { - attachedClients?: number; - closedAt?: string; - createdAt: string; - exitCode?: number; - id: string; - projectId: string; - signal?: number; - sshCommand: string; - startedAt?: string; - /** @enum {string} */ - status: "ready" | "attached" | "exited" | "failed"; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; }; }; }; }; - /** @description The request did not match the expected schema */ - 400: { + /** @description Error */ + 500: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["HttpApiDecodeError"] | { + "application/json": { error: { command?: string; /** unknown */ @@ -3892,12 +3409,12 @@ export interface operations { }; }; }; - "terminal.lookupTerminal": { + "projectDatabases.listDatabaseProfiles": { parameters: { query?: never; header?: never; path: { - sessionId: string; + projectId: string; }; cookie?: never; }; @@ -3910,21 +3427,19 @@ export interface operations { }; content: { "application/json": { - projectDisplayName: string; - projectKey: string; - session: { - attachedClients?: number; - closedAt?: string; + profiles: { createdAt: string; - exitCode?: number; - id: string; - projectId: string; - signal?: number; - sshCommand: string; - startedAt?: string; + database: string; /** @enum {string} */ - status: "ready" | "attached" | "exited" | "failed"; - }; + engine: "postgres" | "mysql" | "mariadb"; + host: string; + id: string; + label: string; + maskedConnectionString: string; + port: number; + updatedAt: string; + user: string; + }[]; }; }; }; @@ -3946,33 +3461,31 @@ export interface operations { }; }; }; - }; - }; - "terminal.deleteAuthTerminal": { - parameters: { - query?: never; - header?: never; - path: { - sessionId: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Success */ - 204: { + /** @description Error */ + 401: { headers: { [name: string]: unknown; }; - content?: never; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; }; - /** @description The request did not match the expected schema */ - 400: { + /** @description Error */ + 404: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["HttpApiDecodeError"] | { + "application/json": { error: { command?: string; /** unknown */ @@ -3984,52 +3497,31 @@ export interface operations { }; }; }; - }; - }; - "prompts.listPrompts": { - parameters: { - query?: never; - header?: never; - path: { - projectId: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Success */ - 200: { + /** @description Error */ + 409: { headers: { [name: string]: unknown; }; content: { "application/json": { - ok?: boolean; - snapshot: { - projectDir: string; - projectId: string; - projectKey: string; - prompts: { - absolutePath: string; - bytes: number; - content: string; - exists: boolean; - fileName: string; - /** @enum {string} */ - kind: "claude" | "codex" | "gemini" | "grok"; - relativePath: string; - }[]; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; }; }; }; }; - /** @description The request did not match the expected schema */ - 400: { + /** @description Error */ + 500: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["HttpApiDecodeError"] | { + "application/json": { error: { command?: string; /** unknown */ @@ -4043,56 +3535,43 @@ export interface operations { }; }; }; - "prompts.writePrompt": { + "projectDatabases.saveDatabaseProfile": { parameters: { query?: never; header?: never; path: { projectId: string; - kind: string; }; cookie?: never; }; requestBody: { content: { "application/json": { - content: string; + connectionString: string; + label?: string | null; }; }; }; responses: { /** @description Success */ - 200: { + 201: { headers: { [name: string]: unknown; }; content: { "application/json": { - ok?: boolean; - prompt: { - absolutePath: string; - bytes: number; - content: string; - exists: boolean; - fileName: string; + profile: { + createdAt: string; + database: string; /** @enum {string} */ - kind: "claude" | "codex" | "gemini" | "grok"; - relativePath: string; - }; - snapshot: { - projectDir: string; - projectId: string; - projectKey: string; - prompts: { - absolutePath: string; - bytes: number; - content: string; - exists: boolean; - fileName: string; - /** @enum {string} */ - kind: "claude" | "codex" | "gemini" | "grok"; - relativePath: string; - }[]; + engine: "postgres" | "mysql" | "mariadb"; + host: string; + id: string; + label: string; + maskedConnectionString: string; + port: number; + updatedAt: string; + user: string; }; }; }; @@ -4115,53 +3594,31 @@ export interface operations { }; }; }; - }; - }; - "prompts.deletePrompt": { - parameters: { - query?: never; - header?: never; - path: { - projectId: string; - kind: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Success */ - 200: { + /** @description Error */ + 401: { headers: { [name: string]: unknown; }; content: { "application/json": { - ok?: boolean; - snapshot: { - projectDir: string; - projectId: string; - projectKey: string; - prompts: { - absolutePath: string; - bytes: number; - content: string; - exists: boolean; - fileName: string; - /** @enum {string} */ - kind: "claude" | "codex" | "gemini" | "grok"; - relativePath: string; - }[]; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; }; }; }; }; - /** @description The request did not match the expected schema */ - 400: { + /** @description Error */ + 404: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["HttpApiDecodeError"] | { + "application/json": { error: { command?: string; /** unknown */ @@ -4173,59 +3630,31 @@ export interface operations { }; }; }; - }; - }; - "skills.listSkills": { - parameters: { - query?: never; - header?: never; - path: { - projectId: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Success */ - 200: { + /** @description Error */ + 409: { headers: { [name: string]: unknown; }; content: { "application/json": { - ok?: boolean; - snapshot: { - projectDir: string; - projectId: string; - projectKey: string; - scopes: { - absoluteRoot: string; - relativeRoot: string; - /** @enum {string} */ - scope: "skills" | "agents/skills" | "agents/.skills" | "claude/skills" | "codex/skills" | "gemini/skills" | "grok/skills"; - }[]; - skills: { - absolutePath: string; - bytes: number; - content: string; - id: string; - name: string; - relativePath: string; - /** @enum {string} */ - scope: "skills" | "agents/skills" | "agents/.skills" | "claude/skills" | "codex/skills" | "gemini/skills" | "grok/skills"; - updatedAtIso: string | null; - }[]; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; }; }; }; }; - /** @description The request did not match the expected schema */ - 400: { + /** @description Error */ + 500: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["HttpApiDecodeError"] | { + "application/json": { error: { command?: string; /** unknown */ @@ -4239,25 +3668,17 @@ export interface operations { }; }; }; - "skills.writeSkill": { + "projectDatabases.deleteDatabaseProfile": { parameters: { query?: never; header?: never; path: { projectId: string; + profileId: string; }; cookie?: never; }; - requestBody: { - content: { - "application/json": { - /** @enum {string} */ - scope: "skills" | "agents/skills" | "agents/.skills" | "claude/skills" | "codex/skills" | "gemini/skills" | "grok/skills"; - name: string; - content: string; - }; - }; - }; + requestBody?: never; responses: { /** @description Success */ 200: { @@ -4266,40 +3687,8 @@ export interface operations { }; content: { "application/json": { - ok?: boolean; - skill: { - absolutePath: string; - bytes: number; - content: string; - id: string; - name: string; - relativePath: string; - /** @enum {string} */ - scope: "skills" | "agents/skills" | "agents/.skills" | "claude/skills" | "codex/skills" | "gemini/skills" | "grok/skills"; - updatedAtIso: string | null; - }; - snapshot: { - projectDir: string; - projectId: string; - projectKey: string; - scopes: { - absoluteRoot: string; - relativeRoot: string; - /** @enum {string} */ - scope: "skills" | "agents/skills" | "agents/.skills" | "claude/skills" | "codex/skills" | "gemini/skills" | "grok/skills"; - }[]; - skills: { - absolutePath: string; - bytes: number; - content: string; - id: string; - name: string; - relativePath: string; - /** @enum {string} */ - scope: "skills" | "agents/skills" | "agents/.skills" | "claude/skills" | "codex/skills" | "gemini/skills" | "grok/skills"; - updatedAtIso: string | null; - }[]; - }; + /** @enum {boolean} */ + ok: true; }; }; }; @@ -4321,13 +3710,4988 @@ export interface operations { }; }; }; - }; - }; - "skills.deleteSkill": { - parameters: { - query?: never; - header?: never; - path: { + /** @description Error */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + }; + }; + "projectDatabases.exposeDatabaseProfile": { + parameters: { + query?: never; + header?: never; + path: { + projectId: string; + profileId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + forward: { + bindHost: string; + containerName: string; + createdAt: string | null; + database: string; + /** @enum {string} */ + engine: "postgres" | "mysql" | "mariadb"; + externalConnectionString: string; + hostPort: number; + id: string; + maskedExternalConnectionString: string; + profileId: string; + profileLabel: string; + projectId: string; + projectKey: string; + publicHost: string; + /** @enum {string} */ + status: "running" | "stopped" | "unknown"; + targetHost: string; + targetPort: number; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + }; + }; + "projectDatabases.deleteDatabaseForward": { + parameters: { + query?: never; + header?: never; + path: { + projectId: string; + profileId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @enum {boolean} */ + ok: true; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + }; + }; + "projectDatabases.listDatabaseForwards": { + parameters: { + query?: never; + header?: never; + path: { + projectId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + forwards: { + bindHost: string; + containerName: string; + createdAt: string | null; + database: string; + /** @enum {string} */ + engine: "postgres" | "mysql" | "mariadb"; + externalConnectionString: string; + hostPort: number; + id: string; + maskedExternalConnectionString: string; + profileId: string; + profileLabel: string; + projectId: string; + projectKey: string; + publicHost: string; + /** @enum {string} */ + status: "running" | "stopped" | "unknown"; + targetHost: string; + targetPort: number; + }[]; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + }; + }; + "projectDatabases.readDatabaseSession": { + parameters: { + query?: never; + header?: never; + path: { + projectId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + session: { + configHash: string; + containerName: string; + editorPath: string; + editorUrl: string; + projectId: string; + projectKey: string; + /** @enum {string} */ + status: "running" | "stopped" | "missing" | "unknown"; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + }; + }; + "projectDatabases.openDatabaseEditor": { + parameters: { + query?: never; + header?: never; + path: { + projectId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + session: { + configHash: string; + containerName: string; + editorPath: string; + editorUrl: string; + projectId: string; + projectKey: string; + /** @enum {string} */ + status: "running" | "stopped" | "missing" | "unknown"; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + }; + }; + "projectDatabases.restartDatabaseEditor": { + parameters: { + query?: never; + header?: never; + path: { + projectId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + session: { + configHash: string; + containerName: string; + editorPath: string; + editorUrl: string; + projectId: string; + projectKey: string; + /** @enum {string} */ + status: "running" | "stopped" | "missing" | "unknown"; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + }; + }; + "auth.githubStatus": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ok?: boolean; + status: { + summary: string; + tokens: { + key: string; + label: string; + login: string | null; + /** @enum {string} */ + status: "valid" | "invalid" | "unknown"; + }[]; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + }; + }; + "auth.gitlabStatus": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ok?: boolean; + status: { + summary: string; + tokens: { + key: string; + label: string; + login: string | null; + /** @enum {string} */ + status: "valid" | "invalid" | "unknown"; + }[]; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + }; + }; + "auth.gitStatus": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ok?: boolean; + status: { + connections: { + host: string; + user: string; + }[]; + summary: string; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + }; + }; + "auth.grokStatus": { + parameters: { + query?: { + label?: string; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ok?: boolean; + status: { + authPath: string; + connected: boolean; + label: string; + message: string; + /** @enum {string} */ + method: "none" | "api-key" | "oauth"; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + }; + }; + "auth.codexStatus": { + parameters: { + query?: { + label?: string; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ok?: boolean; + status: { + account: string | null; + authPath: string; + label: string; + message: string; + present: boolean; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + }; + }; + "auth.githubLogin": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + label?: string | null; + token?: string | null; + scopes?: string | null; + }; + }; + }; + responses: { + /** @description Success */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ok?: boolean; + status: { + summary: string; + tokens: { + key: string; + label: string; + login: string | null; + /** @enum {string} */ + status: "valid" | "invalid" | "unknown"; + }[]; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + }; + }; + "auth.githubLogout": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + label?: string | null; + }; + }; + }; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ok?: boolean; + status: { + summary: string; + tokens: { + key: string; + label: string; + login: string | null; + /** @enum {string} */ + status: "valid" | "invalid" | "unknown"; + }[]; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + }; + }; + "auth.gitlabLogin": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + label?: string | null; + token?: string | null; + }; + }; + }; + responses: { + /** @description Success */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ok?: boolean; + status: { + summary: string; + tokens: { + key: string; + label: string; + login: string | null; + /** @enum {string} */ + status: "valid" | "invalid" | "unknown"; + }[]; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + }; + }; + "auth.gitlabLogout": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + label?: string | null; + }; + }; + }; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ok?: boolean; + status: { + summary: string; + tokens: { + key: string; + label: string; + login: string | null; + /** @enum {string} */ + status: "valid" | "invalid" | "unknown"; + }[]; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + }; + }; + "auth.gitLogin": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + host: string; + token?: string | null; + user?: string | null; + }; + }; + }; + responses: { + /** @description Success */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ok?: boolean; + status: { + connections: { + host: string; + user: string; + }[]; + summary: string; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + }; + }; + "auth.gitLogout": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + host: string; + }; + }; + }; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ok?: boolean; + status: { + connections: { + host: string; + user: string; + }[]; + summary: string; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + }; + }; + "auth.authMenu": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ok?: boolean; + snapshot: { + claudeAuthEntries: number; + claudeAuthPath: string; + codexAuthEntries?: number; + codexAuthPath?: string; + geminiAuthEntries: number; + geminiAuthPath: string; + gitTokenEntries: number; + gitUserEntries: number; + githubTokenEntries: number; + globalEnvPath: string; + grokAuthEntries?: number; + grokAuthPath?: string; + totalEntries: number; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + }; + }; + "auth.authMenuAction": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + /** @enum {string} */ + flow: "GithubRemove" | "GitSet" | "GitRemove" | "ClaudeLogout" | "GeminiApiKey" | "GeminiLogout" | "GrokApiKey" | "GrokLogout"; + label?: string | null; + token?: string | null; + user?: string | null; + apiKey?: string | null; + }; + }; + }; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ok?: boolean; + snapshot: { + claudeAuthEntries: number; + claudeAuthPath: string; + codexAuthEntries?: number; + codexAuthPath?: string; + geminiAuthEntries: number; + geminiAuthPath: string; + gitTokenEntries: number; + gitUserEntries: number; + githubTokenEntries: number; + globalEnvPath: string; + grokAuthEntries?: number; + grokAuthPath?: string; + totalEntries: number; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + }; + }; + "auth.authTerminalSession": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + /** @enum {string} */ + flow: "ClaudeOauth" | "GeminiOauth" | "GrokOauth"; + label?: string | null; + }; + }; + }; + responses: { + /** @description Success */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ok?: boolean; + session: { + attachedClients?: number; + closedAt?: string; + createdAt: string; + exitCode?: number; + id: string; + projectId: string; + signal?: number; + sshCommand: string; + startedAt?: string; + /** @enum {string} */ + status: "ready" | "attached" | "exited" | "failed"; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + }; + }; + "auth.codexImport": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + label?: string | null; + authText: string; + }; + }; + }; + responses: { + /** @description Success */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ok?: boolean; + status: { + account: string | null; + authPath: string; + label: string; + message: string; + present: boolean; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + }; + }; + "auth.codexLogout": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + label?: string | null; + }; + }; + }; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ok?: boolean; + status: { + account: string | null; + authPath: string; + label: string; + message: string; + present: boolean; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + }; + }; + "auth.grokLogout": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + label?: string | null; + }; + }; + }; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ok?: boolean; + status: { + authPath: string; + connected: boolean; + label: string; + message: string; + /** @enum {string} */ + method: "none" | "api-key" | "oauth"; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + }; + }; + "projectAuth.projectAuth": { + parameters: { + query?: never; + header?: never; + path: { + projectId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ok?: boolean; + snapshot: { + activeClaudeLabel: string | null; + activeGeminiLabel: string | null; + activeGitLabel: string | null; + activeGithubLabel: string | null; + activeGrokLabel: string | null; + claudeAuthEntries: number; + claudeAuthPath: string; + codexAuthEntries?: number; + codexAuthPath?: string; + envGlobalPath: string; + envProjectPath: string; + geminiAuthEntries: number; + geminiAuthPath: string; + gitTokenEntries: number; + githubTokenEntries: number; + grokAuthEntries?: number; + grokAuthPath?: string; + projectDir: string; + projectName: string; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + }; + }; + "projectAuth.projectAuthAction": { + parameters: { + query?: never; + header?: never; + path: { + projectId: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + /** @enum {string} */ + flow: "ProjectGithubConnect" | "ProjectGithubDisconnect" | "ProjectGitConnect" | "ProjectGitDisconnect" | "ProjectClaudeConnect" | "ProjectClaudeDisconnect" | "ProjectGeminiConnect" | "ProjectGeminiDisconnect" | "ProjectGrokConnect" | "ProjectGrokDisconnect"; + label?: string | null; + }; + }; + }; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ok?: boolean; + snapshot: { + activeClaudeLabel: string | null; + activeGeminiLabel: string | null; + activeGitLabel: string | null; + activeGithubLabel: string | null; + activeGrokLabel: string | null; + claudeAuthEntries: number; + claudeAuthPath: string; + codexAuthEntries?: number; + codexAuthPath?: string; + envGlobalPath: string; + envProjectPath: string; + geminiAuthEntries: number; + geminiAuthPath: string; + gitTokenEntries: number; + githubTokenEntries: number; + grokAuthEntries?: number; + grokAuthPath?: string; + projectDir: string; + projectName: string; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + }; + }; + "terminal.listTerminalsByKey": { + parameters: { + query?: never; + header?: never; + path: { + projectKey: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + activeSessionId: string | null; + sessions: { + attachedClients?: number; + closedAt?: string; + createdAt: string; + exitCode?: number; + id: string; + projectId: string; + signal?: number; + sshCommand: string; + startedAt?: string; + /** @enum {string} */ + status: "ready" | "attached" | "exited" | "failed"; + }[]; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + }; + }; + "terminal.createTerminalByKey": { + parameters: { + query?: never; + header?: never; + path: { + projectKey: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ok?: boolean; + project: { + clonedOnHostname?: string; + containerName: string; + displayName: string; + id: string; + projectKey: string; + repoRef: string; + repoUrl: string; + sshSessions: number; + startedAtEpochMs: number | null; + startedAtIso: string | null; + /** @enum {string} */ + status: "running" | "stopped" | "unknown"; + statusLabel: string; + authorizedKeysExists: boolean; + authorizedKeysPath: string; + codexAuthPath: string; + codexHome: string; + envGlobalPath: string; + envProjectPath: string; + /** @enum {string} */ + gpu: "none" | "all"; + projectDir: string; + serviceName: string; + sshCommand: string; + sshPort: number; + sshUser: string; + targetDir: string; + }; + session: { + attachedClients?: number; + closedAt?: string; + createdAt: string; + exitCode?: number; + id: string; + projectId: string; + signal?: number; + sshCommand: string; + startedAt?: string; + /** @enum {string} */ + status: "ready" | "attached" | "exited" | "failed"; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + }; + }; + "terminal.startTerminalByKey": { + parameters: { + query?: never; + header?: never; + path: { + projectKey: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + requestId: components["schemas"]["UUID"]; + }; + }; + }; + responses: { + /** @description Success */ + 202: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @enum {boolean} */ + accepted: true; + cursor: number; + projectId: string; + requestId: string; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + }; + }; + "terminal.getTerminalByKey": { + parameters: { + query?: never; + header?: never; + path: { + projectKey: string; + sessionId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + session: { + attachedClients?: number; + closedAt?: string; + createdAt: string; + exitCode?: number; + id: string; + projectId: string; + signal?: number; + sshCommand: string; + startedAt?: string; + /** @enum {string} */ + status: "ready" | "attached" | "exited" | "failed"; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + }; + }; + "terminal.deleteTerminalByKey": { + parameters: { + query?: never; + header?: never; + path: { + projectKey: string; + sessionId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @enum {boolean} */ + ok: true; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + }; + }; + "terminal.setActiveTerminalByKey": { + parameters: { + query?: never; + header?: never; + path: { + projectKey: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + sessionId: string; + }; + }; + }; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ok?: boolean; + session: { + attachedClients?: number; + closedAt?: string; + createdAt: string; + exitCode?: number; + id: string; + projectId: string; + signal?: number; + sshCommand: string; + startedAt?: string; + /** @enum {string} */ + status: "ready" | "attached" | "exited" | "failed"; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + }; + }; + "terminal.lookupTerminal": { + parameters: { + query?: never; + header?: never; + path: { + sessionId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + projectDisplayName: string; + projectKey: string; + session: { + attachedClients?: number; + closedAt?: string; + createdAt: string; + exitCode?: number; + id: string; + projectId: string; + signal?: number; + sshCommand: string; + startedAt?: string; + /** @enum {string} */ + status: "ready" | "attached" | "exited" | "failed"; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + }; + }; + "terminal.deleteAuthTerminal": { + parameters: { + query?: never; + header?: never; + path: { + sessionId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @enum {boolean} */ + ok: true; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + }; + }; + "prompts.listPrompts": { + parameters: { + query?: never; + header?: never; + path: { + projectId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ok?: boolean; + snapshot: { + projectDir: string; + projectId: string; + projectKey: string; + prompts: { + absolutePath: string; + bytes: number; + content: string; + exists: boolean; + fileName: string; + /** @enum {string} */ + kind: "claude" | "codex" | "gemini" | "grok"; + relativePath: string; + }[]; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + }; + }; + "prompts.writePrompt": { + parameters: { + query?: never; + header?: never; + path: { + projectId: string; + kind: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + content: string; + }; + }; + }; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ok?: boolean; + prompt: { + absolutePath: string; + bytes: number; + content: string; + exists: boolean; + fileName: string; + /** @enum {string} */ + kind: "claude" | "codex" | "gemini" | "grok"; + relativePath: string; + }; + snapshot: { + projectDir: string; + projectId: string; + projectKey: string; + prompts: { + absolutePath: string; + bytes: number; + content: string; + exists: boolean; + fileName: string; + /** @enum {string} */ + kind: "claude" | "codex" | "gemini" | "grok"; + relativePath: string; + }[]; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + }; + }; + "prompts.deletePrompt": { + parameters: { + query?: never; + header?: never; + path: { + projectId: string; + kind: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ok?: boolean; + snapshot: { + projectDir: string; + projectId: string; + projectKey: string; + prompts: { + absolutePath: string; + bytes: number; + content: string; + exists: boolean; + fileName: string; + /** @enum {string} */ + kind: "claude" | "codex" | "gemini" | "grok"; + relativePath: string; + }[]; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + }; + }; + "skills.listSkills": { + parameters: { + query?: never; + header?: never; + path: { + projectId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ok?: boolean; + snapshot: { + projectDir: string; + projectId: string; + projectKey: string; + scopes: { + absoluteRoot: string; + relativeRoot: string; + /** @enum {string} */ + scope: "skills" | "agents/skills" | "agents/.skills" | "claude/skills" | "codex/skills" | "gemini/skills" | "grok/skills"; + }[]; + skills: { + absolutePath: string; + bytes: number; + content: string; + id: string; + name: string; + relativePath: string; + /** @enum {string} */ + scope: "skills" | "agents/skills" | "agents/.skills" | "claude/skills" | "codex/skills" | "gemini/skills" | "grok/skills"; + updatedAtIso: string | null; + }[]; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + }; + }; + "skills.writeSkill": { + parameters: { + query?: never; + header?: never; + path: { + projectId: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": { + /** @enum {string} */ + scope: "skills" | "agents/skills" | "agents/.skills" | "claude/skills" | "codex/skills" | "gemini/skills" | "grok/skills"; + name: string; + content: string; + }; + }; + }; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + ok?: boolean; + skill: { + absolutePath: string; + bytes: number; + content: string; + id: string; + name: string; + relativePath: string; + /** @enum {string} */ + scope: "skills" | "agents/skills" | "agents/.skills" | "claude/skills" | "codex/skills" | "gemini/skills" | "grok/skills"; + updatedAtIso: string | null; + }; + snapshot: { + projectDir: string; + projectId: string; + projectKey: string; + scopes: { + absoluteRoot: string; + relativeRoot: string; + /** @enum {string} */ + scope: "skills" | "agents/skills" | "agents/.skills" | "claude/skills" | "codex/skills" | "gemini/skills" | "grok/skills"; + }[]; + skills: { + absolutePath: string; + bytes: number; + content: string; + id: string; + name: string; + relativePath: string; + /** @enum {string} */ + scope: "skills" | "agents/skills" | "agents/.skills" | "claude/skills" | "codex/skills" | "gemini/skills" | "grok/skills"; + updatedAtIso: string | null; + }[]; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + }; + }; + "skills.deleteSkill": { + parameters: { + query?: never; + header?: never; + path: { projectId: string; scopeId: string; name: string; @@ -4343,39 +8707,111 @@ export interface operations { }; content: { "application/json": { - ok?: boolean; - snapshot: { - projectDir: string; - projectId: string; - projectKey: string; - scopes: { - absoluteRoot: string; - relativeRoot: string; - /** @enum {string} */ - scope: "skills" | "agents/skills" | "agents/.skills" | "claude/skills" | "codex/skills" | "gemini/skills" | "grok/skills"; - }[]; - skills: { - absolutePath: string; - bytes: number; - content: string; - id: string; - name: string; - relativePath: string; - /** @enum {string} */ - scope: "skills" | "agents/skills" | "agents/.skills" | "claude/skills" | "codex/skills" | "gemini/skills" | "grok/skills"; - updatedAtIso: string | null; - }[]; + ok?: boolean; + snapshot: { + projectDir: string; + projectId: string; + projectKey: string; + scopes: { + absoluteRoot: string; + relativeRoot: string; + /** @enum {string} */ + scope: "skills" | "agents/skills" | "agents/.skills" | "claude/skills" | "codex/skills" | "gemini/skills" | "grok/skills"; + }[]; + skills: { + absolutePath: string; + bytes: number; + content: string; + id: string; + name: string; + relativePath: string; + /** @enum {string} */ + scope: "skills" | "agents/skills" | "agents/.skills" | "claude/skills" | "codex/skills" | "gemini/skills" | "grok/skills"; + updatedAtIso: string | null; + }[]; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; }; }; }; }; - /** @description The request did not match the expected schema */ - 400: { + /** @description Error */ + 500: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["HttpApiDecodeError"] | { + "application/json": { error: { command?: string; /** unknown */ @@ -4410,6 +8846,25 @@ export interface operations { content: { "application/json": { snapshot: { + agents: { + id: string; + projectId: string; + /** @enum {string} */ + provider: "codex" | "opencode" | "claude" | "grok" | "custom"; + label: string; + command: string; + containerName: string; + /** @enum {string} */ + status: "starting" | "running" | "stopping" | "stopped" | "exited" | "failed"; + source: string; + pidFile: string; + hostPid: number | null; + startedAt: string; + updatedAt: string; + stoppedAt?: string; + exitCode?: number; + signal?: string; + }[]; containerName: string; generatedAt: string; projectId: string; @@ -4428,10 +8883,256 @@ export interface operations { tty: string; user: string; }[]; + terminalSessions: { + attachedClients?: number; + closedAt?: string; + createdAt: string; + exitCode?: number; + id: string; + projectId: string; + signal?: number; + sshCommand: string; + startedAt?: string; + /** @enum {string} */ + status: "ready" | "attached" | "exited" | "failed"; + }[]; + }; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + }; + }; + "tasks.stopTask": { + parameters: { + query?: never; + header?: never; + path: { + projectId: string; + pid: components["schemas"]["NumberFromString"]; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + /** @enum {boolean} */ + ok: true; + }; + }; + }; + /** @description The request did not match the expected schema */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HttpApiDecodeError"] | { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; }; }; }; }; + }; + }; + "tasks.taskLogs": { + parameters: { + query?: { + lines?: string; + }; + header?: never; + path: { + projectId: string; + pid: components["schemas"]["NumberFromString"]; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Success */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + output: string; + }; + }; + }; /** @description The request did not match the expected schema */ 400: { headers: { @@ -4450,34 +9151,31 @@ export interface operations { }; }; }; - }; - }; - "tasks.stopTask": { - parameters: { - query?: never; - header?: never; - path: { - projectId: string; - pid: components["schemas"]["NumberFromString"]; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Success */ - 204: { + /** @description Error */ + 401: { headers: { [name: string]: unknown; }; - content?: never; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; }; - /** @description The request did not match the expected schema */ - 400: { + /** @description Error */ + 404: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["HttpApiDecodeError"] | { + "application/json": { error: { command?: string; /** unknown */ @@ -4489,40 +9187,31 @@ export interface operations { }; }; }; - }; - }; - "tasks.taskLogs": { - parameters: { - query?: { - lines?: string; - }; - header?: never; - path: { - projectId: string; - pid: components["schemas"]["NumberFromString"]; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Success */ - 200: { + /** @description Error */ + 409: { headers: { [name: string]: unknown; }; content: { "application/json": { - output: string; + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; }; }; }; - /** @description The request did not match the expected schema */ - 400: { + /** @description Error */ + 500: { headers: { [name: string]: unknown; }; content: { - "application/json": components["schemas"]["HttpApiDecodeError"] | { + "application/json": { error: { command?: string; /** unknown */ @@ -4584,6 +9273,78 @@ export interface operations { }; }; }; + /** @description Error */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; }; }; "sharing.startPanelCloudflareTunnel": { @@ -4640,6 +9401,78 @@ export interface operations { }; }; }; + /** @description Error */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; }; }; "sharing.stopPanelCloudflareTunnel": { @@ -4690,6 +9523,78 @@ export interface operations { }; }; }; + /** @description Error */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; + /** @description Error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + error: { + command?: string; + /** unknown */ + details?: unknown; + message: string; + provider?: string; + type: string; + }; + }; + }; + }; }; }; } From f25cc5e7750c20a609b46c60f4e8a7bd55f8082e Mon Sep 17 00:00:00 2001 From: skulidropek <66840575+skulidropek@users.noreply.github.com> Date: Thu, 18 Jun 2026 16:15:56 +0000 Subject: [PATCH 09/10] fix(app): address OpenAPI review follow-ups --- bun.lock | 2 +- packages/api/tests/openapi.test.ts | 72 +++++++++++++++---- packages/app/package.json | 2 +- packages/app/src/web/api-create-project.ts | 14 +++- .../app/src/web/api-project-create-body.ts | 48 ++++++++++++- packages/app/src/web/openapi-client.ts | 6 +- .../docker-git/api-create-project.test.ts | 35 +++++++++ .../app/tests/docker-git/api-terminal.test.ts | 3 + 8 files changed, 161 insertions(+), 21 deletions(-) diff --git a/bun.lock b/bun.lock index 05634f3c..edacee9f 100644 --- a/bun.lock +++ b/bun.lock @@ -62,6 +62,7 @@ "@effect/workflow": "^0.18.2", "@gridland/bun": "0.4.3", "@gridland/web": "0.4.3", + "@prover-coder-ai/docker-git-openapi": "workspace:*", "@prover-coder-ai/docker-git-session-sync": "workspace:*", "effect": "^3.21.3", "react": "19.2.4", @@ -78,7 +79,6 @@ "@eslint/compat": "2.1.0", "@eslint/eslintrc": "3.3.5", "@eslint/js": "10.0.1", - "@prover-coder-ai/docker-git-openapi": "workspace:*", "@prover-coder-ai/docker-git-terminal": "workspace:*", "@prover-coder-ai/eslint-plugin-suggest-members": "^0.0.26", "@ton-ai-core/vibecode-linter": "^1.0.11", diff --git a/packages/api/tests/openapi.test.ts b/packages/api/tests/openapi.test.ts index 44b3632b..dad37446 100644 --- a/packages/api/tests/openapi.test.ts +++ b/packages/api/tests/openapi.test.ts @@ -1,10 +1,23 @@ import { describe, expect, it } from "@effect/vitest" import { Effect } from "effect" +import * as fc from "fast-check" import { buildDockerGitOpenApi } from "../src/api/openapi.js" const documentedMethods = ["delete", "get", "post", "put"] as const const commonErrorStatuses = ["400", "401", "404", "409", "500"] +const okOnlyOperations = [ + { method: "post", path: "/projects/apply-all" }, + { method: "post", path: "/projects/down-all" }, + { method: "delete", path: "/projects/{projectId}" }, + { method: "post", path: "/projects/{projectId}/down" }, + { method: "delete", path: "/projects/{projectId}/ports/{targetPort}" }, + { method: "delete", path: "/projects/{projectId}/databases/profiles/{profileId}" }, + { method: "delete", path: "/projects/{projectId}/databases/profiles/{profileId}/expose" }, + { method: "delete", path: "/projects/by-key/{projectKey}/terminal-sessions/{sessionId}" }, + { method: "delete", path: "/auth/terminal-sessions/{sessionId}" }, + { method: "post", path: "/projects/{projectId}/tasks/{pid}/stop" } +] as const describe("openapi contract", () => { it.effect("documents generated REST paths from the Effect HttpApi contract", () => @@ -86,22 +99,38 @@ describe("openapi contract", () => { } })) + it.effect("preserves common error status invariant for arbitrary documented operations", () => + Effect.sync(() => { + const spec = buildDockerGitOpenApi() + const paths = spec.paths ?? {} + const operations = Object.entries(paths).flatMap(([path, item]) => + documentedMethods.flatMap((method) => { + const responses = item[method]?.responses + return responses === undefined ? [] : [{ method, path, responses }] + }) + ) + + expect(operations.length).toBeGreaterThan(0) + + fc.assert( + fc.property(fc.integer({ min: 0, max: operations.length - 1 }), (index) => { + const operation = operations[index] + if (operation === undefined) { + return false + } + expect(Object.keys(operation.responses), `${operation.method.toUpperCase()} ${operation.path}`).toEqual( + expect.arrayContaining(commonErrorStatuses) + ) + return true + }), + { numRuns: Math.max(operations.length * 2, 50) } + ) + })) + it.effect("documents ok-only HTTP handlers as 200 JSON responses", () => Effect.sync(() => { const spec = buildDockerGitOpenApi() const paths = spec.paths ?? {} - const okOnlyOperations = [ - { method: "post", path: "/projects/apply-all" }, - { method: "post", path: "/projects/down-all" }, - { method: "delete", path: "/projects/{projectId}" }, - { method: "post", path: "/projects/{projectId}/down" }, - { method: "delete", path: "/projects/{projectId}/ports/{targetPort}" }, - { method: "delete", path: "/projects/{projectId}/databases/profiles/{profileId}" }, - { method: "delete", path: "/projects/{projectId}/databases/profiles/{profileId}/expose" }, - { method: "delete", path: "/projects/by-key/{projectKey}/terminal-sessions/{sessionId}" }, - { method: "delete", path: "/auth/terminal-sessions/{sessionId}" }, - { method: "post", path: "/projects/{projectId}/tasks/{pid}/stop" } - ] as const for (const operation of okOnlyOperations) { const responses = paths[operation.path]?.[operation.method]?.responses ?? {} @@ -114,6 +143,25 @@ describe("openapi contract", () => { } })) + it.effect("preserves ok-only handler response invariant for arbitrary ok-only operations", () => + Effect.sync(() => { + const spec = buildDockerGitOpenApi() + const paths = spec.paths ?? {} + + fc.assert( + fc.property(fc.constantFrom(...okOnlyOperations), (operation) => { + const responses = paths[operation.path]?.[operation.method]?.responses ?? {} + const serializedSuccessSchema = JSON.stringify(responses["200"] ?? {}) + + expect(responses["200"], `${operation.method.toUpperCase()} ${operation.path}`).toBeDefined() + expect(responses["204"], `${operation.method.toUpperCase()} ${operation.path}`).toBeUndefined() + expect(serializedSuccessSchema).toContain("\"required\":[\"ok\"]") + expect(serializedSuccessSchema).toContain("\"ok\":{\"type\":\"boolean\",\"enum\":[true]}") + }), + { numRuns: okOnlyOperations.length * 2 } + ) + })) + it.effect("documents project auth snapshots without nonexistent totalEntries", () => Effect.sync(() => { const spec = buildDockerGitOpenApi() diff --git a/packages/app/package.json b/packages/app/package.json index 8b0b2dd4..f377aa0f 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -79,6 +79,7 @@ "@effect/workflow": "^0.18.2", "@gridland/bun": "0.4.3", "@gridland/web": "0.4.3", + "@prover-coder-ai/docker-git-openapi": "workspace:*", "@prover-coder-ai/docker-git-session-sync": "workspace:*", "effect": "^3.21.3", "react": "19.2.4", @@ -95,7 +96,6 @@ "@eslint/compat": "2.1.0", "@eslint/eslintrc": "3.3.5", "@eslint/js": "10.0.1", - "@prover-coder-ai/docker-git-openapi": "workspace:*", "@prover-coder-ai/docker-git-terminal": "workspace:*", "@prover-coder-ai/eslint-plugin-suggest-members": "^0.0.26", "@ton-ai-core/vibecode-linter": "^1.0.11", diff --git a/packages/app/src/web/api-create-project.ts b/packages/app/src/web/api-create-project.ts index 061c3a1c..ad9e74cd 100644 --- a/packages/app/src/web/api-create-project.ts +++ b/packages/app/src/web/api-create-project.ts @@ -1,14 +1,24 @@ import type { Effect } from "effect" import { + type BaseCreateProjectBody, baseCreateProjectBody, type CreateProjectRequestDraft, - optionalProjectResourceFields + optionalProjectResourceFields, + type OptionalProjectResourceFieldsBody } from "./api-project-create-body.js" import { CreateProjectAcceptedResponseSchema } from "./api-schema.js" import type { CreateProjectAcceptedResponse } from "./api-schema.js" import { openApiJsonSchema } from "./openapi-client.js" +type CreateProjectAcceptedBody = Readonly< + & BaseCreateProjectBody + & OptionalProjectResourceFieldsBody + & { + readonly async: true + } +> + /** * Builds the async POST /projects request body. * @@ -23,7 +33,7 @@ import { openApiJsonSchema } from "./openapi-client.js" * @complexity O(1). * @throws Never. */ -export const createProjectAcceptedBody = (draft: CreateProjectRequestDraft) => ({ +export const createProjectAcceptedBody = (draft: CreateProjectRequestDraft): CreateProjectAcceptedBody => ({ ...baseCreateProjectBody(draft), async: true, ...optionalProjectResourceFields(draft) diff --git a/packages/app/src/web/api-project-create-body.ts b/packages/app/src/web/api-project-create-body.ts index 109f31ea..db0f7019 100644 --- a/packages/app/src/web/api-project-create-body.ts +++ b/packages/app/src/web/api-project-create-body.ts @@ -14,6 +14,48 @@ import type { CreateProjectDraft } from "./api-schema.js" */ export type CreateProjectRequestDraft = CreateProjectDraft & ProjectResourceLimitRequest +/** + * Optional resource-limit fragment serialized into POST /projects request bodies. + * + * @pure true - structural type contract only. + * @effect none + * @invariant present fields are defined strings; undefined inputs are omitted by optionalProjectResourceFields. + * @precondition callers construct this value through optionalProjectResourceFields. + * @postcondition spreading this fragment cannot add undefined Playwright limit fields. + * @complexity O(1). + * @throws Never. + */ +export type OptionalProjectResourceFieldsBody = Readonly<{ + readonly playwrightCpuLimit?: Exclude + readonly playwrightRamLimit?: Exclude +}> + +/** + * Common POST /projects request body shared by synchronous and asynchronous create flows. + * + * @pure true - structural type contract only. + * @effect none + * @invariant sync and async create flows share identical non-async fields. + * @precondition callers construct this value through baseCreateProjectBody. + * @postcondition openSsh is false and managed authorized keys are enabled for web-created projects. + * @complexity O(1). + * @throws Never. + */ +export type BaseCreateProjectBody = Readonly<{ + readonly cpuLimit: CreateProjectDraft["cpuLimit"] + readonly enableMcpPlaywright: CreateProjectDraft["enableMcpPlaywright"] + readonly force: CreateProjectDraft["force"] + readonly forceEnv: CreateProjectDraft["forceEnv"] + readonly gpu: CreateProjectDraft["gpu"] + readonly openSsh: false + readonly outDir: CreateProjectDraft["outDir"] + readonly ramLimit: CreateProjectDraft["ramLimit"] + readonly repoRef: CreateProjectDraft["repoRef"] + readonly repoUrl: CreateProjectDraft["repoUrl"] + readonly up: CreateProjectDraft["up"] + readonly useManagedAuthorizedKeys: true +}> + /** * Serializes optional Playwright resource limits for project creation requests. * @@ -28,7 +70,9 @@ export type CreateProjectRequestDraft = CreateProjectDraft & ProjectResourceLimi * @complexity O(1). * @throws Never. */ -export const optionalProjectResourceFields = (request: ProjectResourceLimitRequest) => ({ +export const optionalProjectResourceFields = ( + request: ProjectResourceLimitRequest +): OptionalProjectResourceFieldsBody => ({ ...(request.playwrightCpuLimit !== undefined && { playwrightCpuLimit: request.playwrightCpuLimit }), ...(request.playwrightRamLimit !== undefined && { playwrightRamLimit: request.playwrightRamLimit }) }) @@ -47,7 +91,7 @@ export const optionalProjectResourceFields = (request: ProjectResourceLimitReque * @complexity O(1). * @throws Never. */ -export const baseCreateProjectBody = (draft: CreateProjectDraft) => ({ +export const baseCreateProjectBody = (draft: CreateProjectDraft): BaseCreateProjectBody => ({ cpuLimit: draft.cpuLimit, enableMcpPlaywright: draft.enableMcpPlaywright, force: draft.force, diff --git a/packages/app/src/web/openapi-client.ts b/packages/app/src/web/openapi-client.ts index 89ee5eb8..bdb26bbf 100644 --- a/packages/app/src/web/openapi-client.ts +++ b/packages/app/src/web/openapi-client.ts @@ -17,7 +17,7 @@ const openApiRuntime = makeDockerGitOpenApiRuntime({ * @complexity O(n)/O(n) for error rendering, O(1)/O(1) on local success handling. * @throws Never; failures are returned in the Effect error channel. */ -export const openApiJson = openApiRuntime.openApiJson +export const openApiJson: typeof openApiRuntime.openApiJson = openApiRuntime.openApiJson /** * Executes a docker-git OpenAPI request and decodes the response with an Effect Schema. @@ -30,7 +30,7 @@ export const openApiJson = openApiRuntime.openApiJson * @complexity O(n)/O(n) where n is the decoded response size. * @throws Never; failures are returned in the Effect error channel. */ -export const openApiJsonSchema = openApiRuntime.openApiJsonSchema +export const openApiJsonSchema: typeof openApiRuntime.openApiJsonSchema = openApiRuntime.openApiJsonSchema /** * Executes a docker-git OpenAPI request whose success response has no body. @@ -43,4 +43,4 @@ export const openApiJsonSchema = openApiRuntime.openApiJsonSchema * @complexity O(n)/O(n) for error rendering, O(1)/O(1) on local success handling. * @throws Never; failures are returned in the Effect error channel. */ -export const openApiVoid = openApiRuntime.openApiVoid +export const openApiVoid: typeof openApiRuntime.openApiVoid = openApiRuntime.openApiVoid diff --git a/packages/app/tests/docker-git/api-create-project.test.ts b/packages/app/tests/docker-git/api-create-project.test.ts index 887c9b7c..21b944c4 100644 --- a/packages/app/tests/docker-git/api-create-project.test.ts +++ b/packages/app/tests/docker-git/api-create-project.test.ts @@ -1,5 +1,6 @@ import { describe, expect, it } from "@effect/vitest" import { Effect } from "effect" +import * as fc from "fast-check" import { createProjectAcceptedBody } from "../../src/web/api-create-project.js" import type { CreateProjectRequestDraft } from "../../src/web/api-project-create-body.js" @@ -19,7 +20,41 @@ const projectDraft = { up: true } satisfies CreateProjectRequestDraft +const hasOwnField = (value: object, key: string): boolean => Object.prototype.hasOwnProperty.call(value, key) +const optionalPlaywrightLimitArbitrary = fc.option(fc.string(), { nil: undefined }) + describe("api create project request body", () => { + it.effect("preserves async create invariants for arbitrary Playwright resource limits", () => + Effect.sync(() => { + fc.assert( + fc.property( + optionalPlaywrightLimitArbitrary, + optionalPlaywrightLimitArbitrary, + (playwrightCpuLimit, playwrightRamLimit) => { + const draft: CreateProjectRequestDraft = { + ...projectDraft, + playwrightCpuLimit, + playwrightRamLimit + } + const body = createProjectAcceptedBody(draft) + + expect(body.async).toBe(true) + expect(body.openSsh).toBe(false) + expect(body.useManagedAuthorizedKeys).toBe(true) + expect(hasOwnField(body, "playwrightCpuLimit")).toBe(playwrightCpuLimit !== undefined) + expect(hasOwnField(body, "playwrightRamLimit")).toBe(playwrightRamLimit !== undefined) + if (playwrightCpuLimit !== undefined) { + expect(body.playwrightCpuLimit).toBe(playwrightCpuLimit) + } + if (playwrightRamLimit !== undefined) { + expect(body.playwrightRamLimit).toBe(playwrightRamLimit) + } + } + ), + { numRuns: 50 } + ) + })) + it.effect("serializes async create requests with Playwright resource limits", () => Effect.sync(() => { expect(createProjectAcceptedBody(projectDraft)).toEqual({ diff --git a/packages/app/tests/docker-git/api-terminal.test.ts b/packages/app/tests/docker-git/api-terminal.test.ts index d7807493..4d5e2bed 100644 --- a/packages/app/tests/docker-git/api-terminal.test.ts +++ b/packages/app/tests/docker-git/api-terminal.test.ts @@ -79,6 +79,9 @@ describe("api terminal helpers", () => { const result = yield* _(Effect.either(deleteTerminalSessionByPath("/terminal-sessions/session-1"))) expect(result._tag).toBe("Left") + if (result._tag === "Left") { + expect(result.left).toBe("Invalid terminal close path: /terminal-sessions/session-1") + } expect(capturedDeleteRequests).toEqual([]) })) }) From 7b96e06da733f42bde8c3fbb218cf02d02ca9b39 Mon Sep 17 00:00:00 2001 From: skulidropek <66840575+skulidropek@users.noreply.github.com> Date: Thu, 18 Jun 2026 16:20:05 +0000 Subject: [PATCH 10/10] fix(e2e): pack OpenAPI workspace dependency --- scripts/e2e/local-package-cli.sh | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/scripts/e2e/local-package-cli.sh b/scripts/e2e/local-package-cli.sh index f06dac31..7d895dd0 100755 --- a/scripts/e2e/local-package-cli.sh +++ b/scripts/e2e/local-package-cli.sh @@ -12,11 +12,14 @@ KEEP="${KEEP:-0}" PACK_LOG="$ROOT/bun-pack.log" SESSION_PACK_LOG="$ROOT/bun-pack-session-sync.log" +OPENAPI_PACK_LOG="$ROOT/bun-pack-openapi.log" HELP_LOG_BUN="$ROOT/docker-git-help-bun.log" TAR_LIST="$ROOT/tar-list.txt" SESSION_TAR_LIST="$ROOT/session-tar-list.txt" +OPENAPI_TAR_LIST="$ROOT/openapi-tar-list.txt" PACKED_TARBALL="" SESSION_PACKED_TARBALL="" +OPENAPI_PACKED_TARBALL="" PACKAGE_JSON="$REPO_ROOT/packages/app/package.json" PACKAGE_JSON_BACKUP="$ROOT/package.json.backup" @@ -36,6 +39,10 @@ on_error() { echo "--- bun pack session sync log ---" >&2 cat "$SESSION_PACK_LOG" >&2 || true fi + if [[ -f "$OPENAPI_PACK_LOG" ]]; then + echo "--- bun pack openapi log ---" >&2 + cat "$OPENAPI_PACK_LOG" >&2 || true + fi if [[ -f "$HELP_LOG_BUN" ]]; then echo "--- bun run docker-git --help log ---" >&2 cat "$HELP_LOG_BUN" >&2 || true @@ -56,6 +63,9 @@ cleanup() { if [[ -n "$SESSION_PACKED_TARBALL" ]] && [[ -f "$SESSION_PACKED_TARBALL" ]]; then rm -f "$SESSION_PACKED_TARBALL" >/dev/null 2>&1 || true fi + if [[ -n "$OPENAPI_PACKED_TARBALL" ]] && [[ -f "$OPENAPI_PACKED_TARBALL" ]]; then + rm -f "$OPENAPI_PACKED_TARBALL" >/dev/null 2>&1 || true + fi rm -rf "$ROOT" >/dev/null 2>&1 || true } @@ -80,8 +90,19 @@ session_first_line="$(head -n 1 "$session_entry_tmp" | tr -d '\r')" [[ "$session_first_line" == "#!/usr/bin/env bun" ]] \ || fail "packed session sync entrypoint missing shebang: expected '#!/usr/bin/env bun', got '$session_first_line'" +cd "$REPO_ROOT/packages/openapi" +OPENAPI_PACKED_TARBALL="$(bun pm pack --quiet --ignore-scripts --destination "$ROOT" | tee "$OPENAPI_PACK_LOG" | tail -n 1 | tr -d '\r')" +[[ -n "$OPENAPI_PACKED_TARBALL" ]] || fail "bun pm pack did not return openapi tarball path" +[[ -f "$OPENAPI_PACKED_TARBALL" ]] || fail "packed openapi tarball not found: $OPENAPI_PACKED_TARBALL" + +tar -tf "$OPENAPI_PACKED_TARBALL" >"$OPENAPI_TAR_LIST" +grep -Fq -- "package/src/index.ts" "$OPENAPI_TAR_LIST" \ + || fail "packed openapi tarball does not include src/index.ts" +grep -Fq -- "package/openapi.json" "$OPENAPI_TAR_LIST" \ + || fail "packed openapi tarball does not include openapi.json" + cp "$PACKAGE_JSON" "$PACKAGE_JSON_BACKUP" -SESSION_PACKED_TARBALL="$SESSION_PACKED_TARBALL" bun -e 'import { readFileSync, writeFileSync } from "node:fs"; const path = process.argv[1]; const pkg = JSON.parse(readFileSync(path, "utf8")); delete pkg.devDependencies; pkg.dependencies = pkg.dependencies ?? {}; pkg.dependencies["@prover-coder-ai/docker-git-session-sync"] = `file:${process.env.SESSION_PACKED_TARBALL}`; writeFileSync(path, JSON.stringify(pkg, null, 2) + "\n");' "$PACKAGE_JSON" +SESSION_PACKED_TARBALL="$SESSION_PACKED_TARBALL" OPENAPI_PACKED_TARBALL="$OPENAPI_PACKED_TARBALL" bun -e 'import { readFileSync, writeFileSync } from "node:fs"; const path = process.argv[1]; const pkg = JSON.parse(readFileSync(path, "utf8")); delete pkg.devDependencies; pkg.dependencies = pkg.dependencies ?? {}; pkg.dependencies["@prover-coder-ai/docker-git-session-sync"] = `file:${process.env.SESSION_PACKED_TARBALL}`; pkg.dependencies["@prover-coder-ai/docker-git-openapi"] = `file:${process.env.OPENAPI_PACKED_TARBALL}`; writeFileSync(path, JSON.stringify(pkg, null, 2) + "\n");' "$PACKAGE_JSON" cd "$REPO_ROOT/packages/app" PACKED_TARBALL="$(bun pm pack --quiet --ignore-scripts --destination "$ROOT" | tee "$PACK_LOG" | tail -n 1 | tr -d '\r')"