From ddb42b0f54d6002aa8657402829e794781f6395e Mon Sep 17 00:00:00 2001 From: Christian Alfoni Date: Thu, 2 Oct 2025 14:52:57 +0200 Subject: [PATCH 1/3] initial ports implementation --- openapi-git-spec.json | 1348 ----------- openapi-git.json | 0 openapi-port.json | 151 -- openapi-sandbox-container.json | 179 -- openapi-sandbox-fs.json | 2005 ----------------- openapi-sandbox-git.json | 1369 ----------- openapi-sandbox-setup.json | 570 ----- openapi-sandbox-shell.json | 916 -------- openapi-sandbox-system.json | 348 --- openapi-sandbox-task.json | 947 -------- openapi.json | 13 +- package-lock.json | 269 ++- package.json | 15 +- pint-openapi-bundled.json | 285 +-- src/API.ts | 5 +- src/AgentClient/AgentConnection.ts | 2 +- src/AgentClient/index.ts | 2 +- src/PintClient/index.ts | 95 + src/SandboxClient/commands.ts | 5 +- src/SandboxClient/filesystem.ts | 2 +- src/SandboxClient/index.ts | 2 +- src/SandboxClient/ports.ts | 2 +- src/SandboxClient/setup.ts | 2 +- src/SandboxClient/tasks.ts | 2 +- src/SandboxClient/terminals.ts | 2 +- .../agent-client-interface.ts | 4 +- .../client-rest-container/client.gen.ts | 5 - .../client-rest-container/index.ts | 3 - .../client-rest-container/sdk.gen.ts | 29 - .../client-rest-container/types.gen.ts | 111 - src/api-clients/client-rest-fs/client.gen.ts | 5 - src/api-clients/client-rest-fs/index.ts | 3 - src/api-clients/client-rest-fs/sdk.gen.ts | 280 --- src/api-clients/client-rest-fs/types.gen.ts | 1058 --------- src/api-clients/client-rest-git/client.gen.ts | 5 - src/api-clients/client-rest-git/index.ts | 3 - src/api-clients/client-rest-git/sdk.gen.ts | 224 -- src/api-clients/client-rest-git/types.gen.ts | 725 ------ .../client-rest-setup/client.gen.ts | 5 - src/api-clients/client-rest-setup/index.ts | 3 - src/api-clients/client-rest-setup/sdk.gen.ts | 119 - .../client-rest-setup/types.gen.ts | 295 --- .../client-rest-shell/client.gen.ts | 5 - src/api-clients/client-rest-shell/index.ts | 3 - src/api-clients/client-rest-shell/sdk.gen.ts | 149 -- .../client-rest-shell/types.gen.ts | 443 ---- .../client-rest-system/client.gen.ts | 5 - src/api-clients/client-rest-system/index.ts | 3 - src/api-clients/client-rest-system/sdk.gen.ts | 59 - .../client-rest-system/types.gen.ts | 207 -- .../client-rest-task/client.gen.ts | 5 - src/api-clients/client-rest-task/index.ts | 3 - src/api-clients/client-rest-task/sdk.gen.ts | 149 -- src/api-clients/client-rest-task/types.gen.ts | 507 ----- src/api-clients/client/client.gen.ts | 17 +- src/api-clients/client/client/client.gen.ts | 268 +++ src/api-clients/client/client/index.ts | 25 + src/api-clients/client/client/types.gen.ts | 268 +++ src/api-clients/client/client/utils.gen.ts | 331 +++ src/api-clients/client/core/auth.gen.ts | 42 + .../client/core/bodySerializer.gen.ts | 92 + src/api-clients/client/core/params.gen.ts | 153 ++ .../client/core/pathSerializer.gen.ts | 181 ++ .../client/core/serverSentEvents.gen.ts | 264 +++ src/api-clients/client/core/types.gen.ts | 118 + src/api-clients/client/core/utils.gen.ts | 143 ++ src/api-clients/client/index.ts | 5 +- src/api-clients/client/sdk.gen.ts | 97 +- src/api-clients/client/types.gen.ts | 218 +- src/api-clients/pint/client.gen.ts | 18 + src/api-clients/pint/client/client.gen.ts | 268 +++ src/api-clients/pint/client/index.ts | 25 + src/api-clients/pint/client/types.gen.ts | 268 +++ src/api-clients/pint/client/utils.gen.ts | 331 +++ src/api-clients/pint/core/auth.gen.ts | 42 + .../pint/core/bodySerializer.gen.ts | 92 + src/api-clients/pint/core/params.gen.ts | 153 ++ .../pint/core/pathSerializer.gen.ts | 181 ++ .../pint/core/serverSentEvents.gen.ts | 264 +++ src/api-clients/pint/core/types.gen.ts | 118 + src/api-clients/pint/core/utils.gen.ts | 143 ++ src/api-clients/pint/index.ts | 4 + src/api-clients/pint/sdk.gen.ts | 410 ++++ src/api-clients/pint/types.gen.ts | 1184 ++++++++++ src/utils/api.ts | 10 +- src/utils/event.ts | 50 + tests/emitter-subscription.test.ts | 212 ++ 87 files changed, 6323 insertions(+), 12623 deletions(-) delete mode 100644 openapi-git-spec.json delete mode 100644 openapi-git.json delete mode 100644 openapi-port.json delete mode 100644 openapi-sandbox-container.json delete mode 100644 openapi-sandbox-fs.json delete mode 100644 openapi-sandbox-git.json delete mode 100644 openapi-sandbox-setup.json delete mode 100644 openapi-sandbox-shell.json delete mode 100644 openapi-sandbox-system.json delete mode 100644 openapi-sandbox-task.json create mode 100644 src/PintClient/index.ts rename src/{AgentClient => }/agent-client-interface.ts (98%) delete mode 100644 src/api-clients/client-rest-container/client.gen.ts delete mode 100644 src/api-clients/client-rest-container/index.ts delete mode 100644 src/api-clients/client-rest-container/sdk.gen.ts delete mode 100644 src/api-clients/client-rest-container/types.gen.ts delete mode 100644 src/api-clients/client-rest-fs/client.gen.ts delete mode 100644 src/api-clients/client-rest-fs/index.ts delete mode 100644 src/api-clients/client-rest-fs/sdk.gen.ts delete mode 100644 src/api-clients/client-rest-fs/types.gen.ts delete mode 100644 src/api-clients/client-rest-git/client.gen.ts delete mode 100644 src/api-clients/client-rest-git/index.ts delete mode 100644 src/api-clients/client-rest-git/sdk.gen.ts delete mode 100644 src/api-clients/client-rest-git/types.gen.ts delete mode 100644 src/api-clients/client-rest-setup/client.gen.ts delete mode 100644 src/api-clients/client-rest-setup/index.ts delete mode 100644 src/api-clients/client-rest-setup/sdk.gen.ts delete mode 100644 src/api-clients/client-rest-setup/types.gen.ts delete mode 100644 src/api-clients/client-rest-shell/client.gen.ts delete mode 100644 src/api-clients/client-rest-shell/index.ts delete mode 100644 src/api-clients/client-rest-shell/sdk.gen.ts delete mode 100644 src/api-clients/client-rest-shell/types.gen.ts delete mode 100644 src/api-clients/client-rest-system/client.gen.ts delete mode 100644 src/api-clients/client-rest-system/index.ts delete mode 100644 src/api-clients/client-rest-system/sdk.gen.ts delete mode 100644 src/api-clients/client-rest-system/types.gen.ts delete mode 100644 src/api-clients/client-rest-task/client.gen.ts delete mode 100644 src/api-clients/client-rest-task/index.ts delete mode 100644 src/api-clients/client-rest-task/sdk.gen.ts delete mode 100644 src/api-clients/client-rest-task/types.gen.ts create mode 100644 src/api-clients/client/client/client.gen.ts create mode 100644 src/api-clients/client/client/index.ts create mode 100644 src/api-clients/client/client/types.gen.ts create mode 100644 src/api-clients/client/client/utils.gen.ts create mode 100644 src/api-clients/client/core/auth.gen.ts create mode 100644 src/api-clients/client/core/bodySerializer.gen.ts create mode 100644 src/api-clients/client/core/params.gen.ts create mode 100644 src/api-clients/client/core/pathSerializer.gen.ts create mode 100644 src/api-clients/client/core/serverSentEvents.gen.ts create mode 100644 src/api-clients/client/core/types.gen.ts create mode 100644 src/api-clients/client/core/utils.gen.ts create mode 100644 src/api-clients/pint/client.gen.ts create mode 100644 src/api-clients/pint/client/client.gen.ts create mode 100644 src/api-clients/pint/client/index.ts create mode 100644 src/api-clients/pint/client/types.gen.ts create mode 100644 src/api-clients/pint/client/utils.gen.ts create mode 100644 src/api-clients/pint/core/auth.gen.ts create mode 100644 src/api-clients/pint/core/bodySerializer.gen.ts create mode 100644 src/api-clients/pint/core/params.gen.ts create mode 100644 src/api-clients/pint/core/pathSerializer.gen.ts create mode 100644 src/api-clients/pint/core/serverSentEvents.gen.ts create mode 100644 src/api-clients/pint/core/types.gen.ts create mode 100644 src/api-clients/pint/core/utils.gen.ts create mode 100644 src/api-clients/pint/index.ts create mode 100644 src/api-clients/pint/sdk.gen.ts create mode 100644 src/api-clients/pint/types.gen.ts create mode 100644 tests/emitter-subscription.test.ts diff --git a/openapi-git-spec.json b/openapi-git-spec.json deleted file mode 100644 index 3a8d660..0000000 --- a/openapi-git-spec.json +++ /dev/null @@ -1,1348 +0,0 @@ -{ - "openapi": "3.0.0", - "info": { - "title": "Git API", - "description": "API for interacting with Git version control in sandboxes", - "version": "1.0.0" - }, - "paths": { - "/git/status": { - "post": { - "summary": "Get git status", - "description": "Retrieve the current git status of the repository", - "operationId": "gitStatus", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": {} - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "$ref": "#/components/schemas/GitStatus" - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error retrieving git status", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "$ref": "#/components/schemas/CommonError" - } - } - } - ] - } - } - } - } - } - } - }, - "/git/remotes": { - "post": { - "summary": "Get git remotes", - "description": "Retrieve the remote repositories configured for the git repository", - "operationId": "gitRemotes", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": {} - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "$ref": "#/components/schemas/GitRemotes" - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error retrieving git remotes", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "$ref": "#/components/schemas/CommonError" - } - } - } - ] - } - } - } - } - } - } - }, - "/git/targetDiff": { - "post": { - "summary": "Get target diff", - "description": "Retrieve the difference between the current branch and a target branch", - "operationId": "gitTargetDiff", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "branch": { - "type": "string", - "description": "Target branch name" - } - }, - "required": ["branch"] - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "$ref": "#/components/schemas/GitTargetDiff" - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error retrieving target diff", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "$ref": "#/components/schemas/CommonError" - } - } - } - ] - } - } - } - } - } - } - }, - "/git/pull": { - "post": { - "summary": "Pull changes", - "description": "Pull changes from the remote repository", - "operationId": "gitPull", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "branch": { - "type": "string", - "description": "Branch to pull from" - }, - "force": { - "type": "boolean", - "description": "Force pull" - } - } - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "type": "null" - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error pulling changes", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "$ref": "#/components/schemas/CommonError" - } - } - } - ] - } - } - } - } - } - } - }, - "/git/discard": { - "post": { - "summary": "Discard changes", - "description": "Discard changes to specified paths or all changes", - "operationId": "gitDiscard", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "paths": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Paths to discard changes for" - } - } - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "type": "object", - "properties": { - "paths": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Paths that were discarded" - } - } - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error discarding changes", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "$ref": "#/components/schemas/CommonError" - } - } - } - ] - } - } - } - } - } - } - }, - "/git/commit": { - "post": { - "summary": "Commit changes", - "description": "Commit changes to the local repository", - "operationId": "gitCommit", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "paths": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Paths to commit" - }, - "message": { - "type": "string", - "description": "Commit message" - }, - "push": { - "type": "boolean", - "description": "Whether to push after committing" - } - }, - "required": ["message"] - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "type": "object", - "properties": { - "shellId": { - "type": "string", - "description": "ID of the shell process" - } - }, - "required": ["shellId"] - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error committing changes", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "$ref": "#/components/schemas/CommonError" - } - } - } - ] - } - } - } - } - } - } - }, - "/git/push": { - "post": { - "summary": "Push changes", - "description": "Push changes to the remote repository", - "operationId": "gitPush", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": {} - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "type": "null" - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error pushing changes", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "$ref": "#/components/schemas/CommonError" - } - } - } - ] - } - } - } - } - } - } - }, - "/git/pushToRemote": { - "post": { - "summary": "Push to remote", - "description": "Push changes to a specific remote repository and branch", - "operationId": "gitPushToRemote", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "url": { - "type": "string", - "description": "URL of the remote repository" - }, - "branch": { - "type": "string", - "description": "Branch to push to" - }, - "squashAllCommits": { - "type": "boolean", - "description": "Whether to squash all commits before pushing" - } - }, - "required": ["url", "branch"] - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "type": "null" - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error pushing to remote", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "$ref": "#/components/schemas/CommonError" - } - } - } - ] - } - } - } - } - } - } - }, - "/git/renameBranch": { - "post": { - "summary": "Rename branch", - "description": "Rename a branch in the local repository", - "operationId": "gitRenameBranch", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "oldBranch": { - "type": "string", - "description": "Name of the branch to rename" - }, - "newBranch": { - "type": "string", - "description": "New name for the branch" - } - }, - "required": ["oldBranch", "newBranch"] - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "type": "null" - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error renaming branch", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "$ref": "#/components/schemas/CommonError" - } - } - } - ] - } - } - } - } - } - } - }, - "/git/remoteContent": { - "post": { - "summary": "Get remote content", - "description": "Retrieve the content of a file from a remote branch or commit", - "operationId": "gitRemoteContent", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/GitRemoteParams" - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "type": "object", - "properties": { - "content": { - "type": "string", - "description": "Content of the file" - } - }, - "required": ["content"] - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error retrieving remote content", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "$ref": "#/components/schemas/CommonError" - } - } - } - ] - } - } - } - } - } - } - }, - "/git/diffStatus": { - "post": { - "summary": "Get diff status", - "description": "Retrieve the status of changes between two references", - "operationId": "gitDiffStatus", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/GitDiffStatusParams" - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "$ref": "#/components/schemas/GitDiffStatusResult" - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error retrieving diff status", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "$ref": "#/components/schemas/CommonError" - } - } - } - ] - } - } - } - } - } - } - }, - "/git/resetLocalWithRemote": { - "post": { - "summary": "Reset local with remote", - "description": "Reset the local repository to match the remote", - "operationId": "gitResetLocalWithRemote", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": {} - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "type": "null" - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error resetting local with remote", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "$ref": "#/components/schemas/CommonError" - } - } - } - ] - } - } - } - } - } - } - }, - "/git/checkoutInitialBranch": { - "post": { - "summary": "Checkout initial branch", - "description": "Checkout the initial branch of the repository", - "operationId": "gitCheckoutInitialBranch", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": {} - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "type": "null" - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error checking out initial branch", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "$ref": "#/components/schemas/CommonError" - } - } - } - ] - } - } - } - } - } - } - }, - "/git/transposeLines": { - "post": { - "summary": "Transpose lines", - "description": "Map line numbers between different git commits", - "operationId": "gitTransposeLines", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "type": "object", - "properties": { - "sha": { - "type": "string", - "description": "Commit SHA" - }, - "path": { - "type": "string", - "description": "File path" - }, - "line": { - "type": "number", - "description": "Line number" - } - }, - "required": ["sha", "path", "line"] - } - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "type": "array", - "items": { - "type": "object", - "properties": { - "path": { - "type": "string", - "description": "File path" - }, - "line": { - "type": "number", - "description": "Line number" - } - }, - "required": ["path", "line"], - "nullable": true - } - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error transposing lines", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "$ref": "#/components/schemas/CommonError" - } - } - } - ] - } - } - } - } - } - } - } - }, - "components": { - "schemas": { - "SuccessResponse": { - "type": "object", - "properties": { - "status": { - "type": "number", - "enum": [0], - "description": "Status code for successful operations" - }, - "result": { - "type": "object", - "description": "Result payload for the operation" - } - }, - "required": ["status", "result"] - }, - "ErrorResponse": { - "type": "object", - "properties": { - "status": { - "type": "number", - "enum": [1], - "description": "Status code for error operations" - }, - "error": { - "type": "object", - "description": "Error details" - } - }, - "required": ["status", "error"] - }, - "CommonError": { - "type": "object", - "properties": { - "code": { - "type": "number", - "description": "Error code" - }, - "message": { - "type": "string", - "description": "Error message" - }, - "data": { - "type": "object", - "description": "Additional error data", - "nullable": true - } - }, - "required": ["code", "message"] - }, - "GitStatusShortFormat": { - "type": "string", - "enum": ["", "M", "A", "D", "R", "C", "U", "?"], - "description": "Git status short format codes" - }, - "GitItem": { - "type": "object", - "properties": { - "path": { - "type": "string", - "description": "File path" - }, - "index": { - "$ref": "#/components/schemas/GitStatusShortFormat" - }, - "workingTree": { - "$ref": "#/components/schemas/GitStatusShortFormat" - }, - "isStaged": { - "type": "boolean", - "description": "Whether the file is staged" - }, - "isConflicted": { - "type": "boolean", - "description": "Whether the file has conflicts" - }, - "fileId": { - "type": "string", - "description": "File ID" - } - }, - "required": ["path", "index", "workingTree", "isStaged", "isConflicted"] - }, - "GitChangedFiles": { - "type": "object", - "additionalProperties": { - "$ref": "#/components/schemas/GitItem" - }, - "description": "Map of file IDs to GitItems" - }, - "GitBranchProperties": { - "type": "object", - "properties": { - "head": { - "type": "string", - "nullable": true, - "description": "Head commit" - }, - "branch": { - "type": "string", - "nullable": true, - "description": "Branch name" - }, - "ahead": { - "type": "number", - "description": "Number of commits ahead" - }, - "behind": { - "type": "number", - "description": "Number of commits behind" - }, - "safe": { - "type": "boolean", - "description": "Whether the branch is safe to use" - } - }, - "required": ["ahead", "behind", "safe"] - }, - "GitCommit": { - "type": "object", - "properties": { - "hash": { - "type": "string", - "description": "Commit hash" - }, - "date": { - "type": "string", - "description": "Commit date" - }, - "message": { - "type": "string", - "description": "Commit message" - }, - "author": { - "type": "string", - "description": "Commit author" - } - }, - "required": ["hash", "date", "message", "author"] - }, - "GitStatus": { - "type": "object", - "properties": { - "changedFiles": { - "$ref": "#/components/schemas/GitChangedFiles" - }, - "deletedFiles": { - "type": "array", - "items": { - "$ref": "#/components/schemas/GitItem" - } - }, - "conflicts": { - "type": "boolean", - "description": "Whether there are remote conflicts" - }, - "localChanges": { - "type": "boolean", - "description": "Whether there are local changes" - }, - "remote": { - "$ref": "#/components/schemas/GitBranchProperties" - }, - "target": { - "$ref": "#/components/schemas/GitBranchProperties" - }, - "head": { - "type": "string", - "description": "Current HEAD commit" - }, - "commits": { - "type": "array", - "items": { - "$ref": "#/components/schemas/GitCommit" - } - }, - "branch": { - "type": "string", - "nullable": true, - "description": "Current branch name" - }, - "isMerging": { - "type": "boolean", - "description": "Whether a merge is in progress" - } - }, - "required": [ - "changedFiles", - "deletedFiles", - "conflicts", - "localChanges", - "remote", - "target", - "commits", - "branch", - "isMerging" - ] - }, - "GitTargetDiff": { - "type": "object", - "properties": { - "ahead": { - "type": "number", - "description": "Number of commits ahead of target" - }, - "behind": { - "type": "number", - "description": "Number of commits behind target" - }, - "commits": { - "type": "array", - "items": { - "$ref": "#/components/schemas/GitCommit" - } - } - }, - "required": ["ahead", "behind", "commits"] - }, - "GitRemotes": { - "type": "object", - "properties": { - "origin": { - "type": "string", - "description": "Origin remote URL" - }, - "upstream": { - "type": "string", - "description": "Upstream remote URL" - } - }, - "required": ["origin", "upstream"] - }, - "GitRemoteParams": { - "type": "object", - "properties": { - "reference": { - "type": "string", - "description": "Branch or commit hash" - }, - "path": { - "type": "string", - "description": "File path" - } - }, - "required": ["reference", "path"] - }, - "GitDiffStatusParams": { - "type": "object", - "properties": { - "base": { - "type": "string", - "description": "Base reference for diffing" - }, - "head": { - "type": "string", - "description": "Head reference for diffing" - } - }, - "required": ["base", "head"] - }, - "GitDiffStatusItem": { - "type": "object", - "properties": { - "status": { - "$ref": "#/components/schemas/GitStatusShortFormat" - }, - "path": { - "type": "string", - "description": "File path" - }, - "oldPath": { - "type": "string", - "description": "Original file path (for renames)" - }, - "hunks": { - "type": "array", - "items": { - "type": "object", - "properties": { - "original": { - "type": "object", - "properties": { - "start": { - "type": "number" - }, - "end": { - "type": "number" - } - }, - "required": ["start", "end"] - }, - "modified": { - "type": "object", - "properties": { - "start": { - "type": "number" - }, - "end": { - "type": "number" - } - }, - "required": ["start", "end"] - } - }, - "required": ["original", "modified"] - } - } - }, - "required": ["status", "path", "hunks"] - }, - "GitDiffStatusResult": { - "type": "object", - "properties": { - "files": { - "type": "array", - "items": { - "$ref": "#/components/schemas/GitDiffStatusItem" - } - } - }, - "required": ["files"] - } - } - } -} diff --git a/openapi-git.json b/openapi-git.json deleted file mode 100644 index e69de29..0000000 diff --git a/openapi-port.json b/openapi-port.json deleted file mode 100644 index 176f301..0000000 --- a/openapi-port.json +++ /dev/null @@ -1,151 +0,0 @@ -{ - "openapi": "3.0.0", - "info": { - "title": "Port API", - "description": "API for managing sandbox port operations", - "version": "1.0.0" - }, - "paths": { - "/port/list": { - "post": { - "summary": "List ports", - "description": "Retrieve a list of available ports and their URLs", - "operationId": "portList", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": {} - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "type": "object", - "properties": { - "list": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Port" - }, - "description": "List of available ports" - } - }, - "required": ["list"] - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error listing ports", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "$ref": "#/components/schemas/CommonError" - } - } - } - ] - } - } - } - } - } - } - } - }, - "components": { - "schemas": { - "SuccessResponse": { - "type": "object", - "properties": { - "status": { - "type": "number", - "enum": [0], - "description": "Status code for successful operations" - }, - "result": { - "type": "object", - "description": "Result payload for the operation" - } - }, - "required": ["status", "result"] - }, - "ErrorResponse": { - "type": "object", - "properties": { - "status": { - "type": "number", - "enum": [1], - "description": "Status code for error operations" - }, - "error": { - "type": "object", - "description": "Error details" - } - }, - "required": ["status", "error"] - }, - "CommonError": { - "type": "object", - "properties": { - "code": { - "type": "number", - "description": "Error code" - }, - "message": { - "type": "string", - "description": "Error message" - }, - "data": { - "type": "object", - "description": "Additional error data", - "nullable": true - } - }, - "required": ["code", "message"] - }, - "Port": { - "type": "object", - "properties": { - "port": { - "type": "number", - "description": "Port number" - }, - "url": { - "type": "string", - "description": "URL to access the service on this port" - } - }, - "required": ["port", "url"] - } - } - } -} diff --git a/openapi-sandbox-container.json b/openapi-sandbox-container.json deleted file mode 100644 index f272b37..0000000 --- a/openapi-sandbox-container.json +++ /dev/null @@ -1,179 +0,0 @@ -{ - "openapi": "3.0.0", - "info": { - "title": "Sandbox Container API", - "description": "API for managing sandbox container operations", - "version": "1.0.0" - }, - "paths": { - "/container/setup": { - "post": { - "summary": "Setup container", - "description": "Set up a new container based on a template", - "operationId": "containerSetup", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "templateId": { - "type": "string", - "description": "Identifier of the template to use" - }, - "templateArgs": { - "type": "object", - "description": "Arguments for the template", - "additionalProperties": { - "type": "string" - } - }, - "features": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "Feature identifier" - }, - "options": { - "type": "object", - "description": "Options for the feature", - "additionalProperties": { - "type": "string" - } - } - }, - "required": ["id", "options"] - }, - "nullable": true - } - }, - "required": ["templateId", "templateArgs"] - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "$ref": "#/components/schemas/TaskDTO" - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error setting up container", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "$ref": "#/components/schemas/ProtocolError" - } - } - } - ] - } - } - } - } - } - } - } - }, - "components": { - "schemas": { - "SuccessResponse": { - "type": "object", - "properties": { - "status": { - "type": "number", - "enum": [0], - "description": "Status code for successful operations" - }, - "result": { - "type": "object", - "description": "Result payload for the operation" - } - }, - "required": ["status", "result"] - }, - "ErrorResponse": { - "type": "object", - "properties": { - "status": { - "type": "number", - "enum": [1], - "description": "Status code for error operations" - }, - "error": { - "type": "object", - "description": "Error details" - } - }, - "required": ["status", "error"] - }, - "ProtocolError": { - "type": "object", - "properties": { - "code": { - "type": "string", - "description": "Error code" - }, - "message": { - "type": "string", - "description": "Error message" - }, - "data": { - "type": "object", - "description": "Additional error data", - "nullable": true - } - }, - "required": ["code", "message"] - }, - "TaskDTO": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "Task identifier" - }, - "status": { - "type": "string", - "description": "Task status" - }, - "progress": { - "type": "number", - "description": "Task progress (0-100)" - } - }, - "required": ["id", "status", "progress"] - } - } - } -} diff --git a/openapi-sandbox-fs.json b/openapi-sandbox-fs.json deleted file mode 100644 index 57c3c6c..0000000 --- a/openapi-sandbox-fs.json +++ /dev/null @@ -1,2005 +0,0 @@ -{ - "openapi": "3.0.0", - "info": { - "title": "Sandbox Rest FS API", - "description": "FS API for interacting with sandbox", - "version": "1.0.0" - }, - "paths": { - "/fs/writeFile": { - "post": { - "summary": "Write to a file", - "description": "Write content to a file at the specified path", - "operationId": "writeFile", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/WriteFileRequest" - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "type": "object", - "properties": {} - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error writing file", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "oneOf": [ - { - "$ref": "#/components/schemas/DefaultError" - }, - { - "$ref": "#/components/schemas/RawFsError" - } - ], - "discriminator": { - "propertyName": "code" - } - } - } - } - ] - } - } - } - } - } - } - }, - "/fs/read": { - "post": { - "summary": "Read file system", - "description": "Retrieve the latest snapshot of the server's MemoryFS file and children list", - "operationId": "fsRead", - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "$ref": "#/components/schemas/FSReadResult" - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error reading file system", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "$ref": "#/components/schemas/DefaultError" - } - } - } - ] - } - } - } - } - } - } - }, - "/fs/operation": { - "post": { - "summary": "Perform file system operation", - "description": "Send a tree operation reflecting filesystem operations", - "operationId": "fsOperation", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/FSOperationRequest" - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "$ref": "#/components/schemas/FSOperationResult" - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error performing operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "$ref": "#/components/schemas/DefaultError" - } - } - } - ] - } - } - } - } - } - } - }, - "/fs/search": { - "post": { - "summary": "Search files", - "description": "Search for content in files", - "operationId": "fsSearch", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/FSSearchParams" - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "type": "array", - "items": { - "$ref": "#/components/schemas/SearchResult" - } - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error searching files", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "$ref": "#/components/schemas/DefaultError" - } - } - } - ] - } - } - } - } - } - } - }, - "/fs/streamingSearch": { - "post": { - "summary": "Start streaming search", - "description": "Start a streaming search for content in files", - "operationId": "fsStreamingSearch", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/FSStreamingSearchParams" - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "type": "object", - "properties": { - "searchId": { - "type": "string", - "description": "ID of the search operation" - } - } - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error starting streaming search", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "$ref": "#/components/schemas/DefaultError" - } - } - } - ] - } - } - } - } - } - } - }, - "/fs/cancelStreamingSearch": { - "post": { - "summary": "Cancel streaming search", - "description": "Cancel an ongoing streaming search", - "operationId": "fsCancelStreamingSearch", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "searchId": { - "type": "string", - "description": "ID of the search to cancel" - } - }, - "required": ["searchId"] - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "type": "object", - "properties": { - "searchId": { - "type": "string", - "description": "ID of the cancelled search" - } - } - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error cancelling search", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "$ref": "#/components/schemas/DefaultError" - } - } - } - ] - } - } - } - } - } - } - }, - "/fs/pathSearch": { - "post": { - "summary": "Search file paths", - "description": "Search for file paths matching a pattern", - "operationId": "fsPathSearch", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PathSearchParams" - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "$ref": "#/components/schemas/PathSearchResult" - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error searching paths", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "$ref": "#/components/schemas/DefaultError" - } - } - } - ] - } - } - } - } - } - } - }, - "/fs/upload": { - "post": { - "summary": "Upload file", - "description": "Upload a file to the specified parent directory", - "operationId": "fsUpload", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "parentId": { - "type": "string", - "description": "ID of the parent directory" - }, - "filename": { - "type": "string", - "description": "Name of the file to create" - }, - "content": { - "type": "string", - "format": "binary", - "description": "File content as binary data" - } - }, - "required": ["parentId", "filename", "content"] - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "type": "object", - "properties": { - "fileId": { - "type": "string", - "description": "ID of the created file" - } - } - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error uploading file", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "oneOf": [ - { - "$ref": "#/components/schemas/DefaultError" - }, - { - "$ref": "#/components/schemas/InvalidIdError" - } - ], - "discriminator": { - "propertyName": "code" - } - } - } - } - ] - } - } - } - } - } - } - }, - "/fs/download": { - "post": { - "summary": "Download files", - "description": "Download files at a specified path as a zip", - "operationId": "fsDownload", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "path": { - "type": "string", - "description": "Path to download" - }, - "excludes": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Glob patterns of files/folders to exclude from the download" - } - }, - "required": ["path"] - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "type": "object", - "properties": { - "downloadUrl": { - "type": "string", - "description": "URL to download the files from" - } - } - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error creating download", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "$ref": "#/components/schemas/DefaultError" - } - } - } - ] - } - } - } - } - } - } - }, - "/fs/readFile": { - "post": { - "summary": "Read file content", - "description": "Read the content of a file at the specified path", - "operationId": "fsReadFile", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/FSReadFileParams" - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "$ref": "#/components/schemas/FSReadFileResult" - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error reading file", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "oneOf": [ - { - "$ref": "#/components/schemas/DefaultError" - }, - { - "$ref": "#/components/schemas/RawFsError" - } - ], - "discriminator": { - "propertyName": "code" - } - } - } - } - ] - } - } - } - } - } - } - }, - "/fs/readdir": { - "post": { - "summary": "Read directory contents", - "description": "List the contents of a directory at the specified path", - "operationId": "fsReadDir", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/FSReadDirParams" - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "$ref": "#/components/schemas/FSReadDirResult" - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error reading directory", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "oneOf": [ - { - "$ref": "#/components/schemas/DefaultError" - }, - { - "$ref": "#/components/schemas/RawFsError" - } - ], - "discriminator": { - "propertyName": "code" - } - } - } - } - ] - } - } - } - } - } - } - }, - "/fs/stat": { - "post": { - "summary": "Get file/directory stats", - "description": "Get stats for a file or directory at the specified path", - "operationId": "fsStat", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/FSStatParams" - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "$ref": "#/components/schemas/FSStatResult" - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error getting stats", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "oneOf": [ - { - "$ref": "#/components/schemas/DefaultError" - }, - { - "$ref": "#/components/schemas/RawFsError" - } - ], - "discriminator": { - "propertyName": "code" - } - } - } - } - ] - } - } - } - } - } - } - }, - "/fs/copy": { - "post": { - "summary": "Copy file/directory", - "description": "Copy a file or directory from one location to another", - "operationId": "fsCopy", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/FSCopyParams" - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "type": "object", - "properties": {} - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error copying file/directory", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "oneOf": [ - { - "$ref": "#/components/schemas/DefaultError" - }, - { - "$ref": "#/components/schemas/RawFsError" - } - ], - "discriminator": { - "propertyName": "code" - } - } - } - } - ] - } - } - } - } - } - } - }, - "/fs/rename": { - "post": { - "summary": "Rename file/directory", - "description": "Rename a file or directory (move from one location to another)", - "operationId": "fsRename", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/FSRenameParams" - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "type": "object", - "properties": {} - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error renaming file/directory", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "oneOf": [ - { - "$ref": "#/components/schemas/DefaultError" - }, - { - "$ref": "#/components/schemas/RawFsError" - } - ], - "discriminator": { - "propertyName": "code" - } - } - } - } - ] - } - } - } - } - } - } - }, - "/fs/remove": { - "post": { - "summary": "Remove file/directory", - "description": "Delete a file or directory at the specified path", - "operationId": "fsRemove", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/FSRemoveParams" - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "type": "object", - "properties": {} - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error removing file/directory", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "oneOf": [ - { - "$ref": "#/components/schemas/DefaultError" - }, - { - "$ref": "#/components/schemas/RawFsError" - } - ], - "discriminator": { - "propertyName": "code" - } - } - } - } - ] - } - } - } - } - } - } - }, - "/fs/mkdir": { - "post": { - "summary": "Create directory", - "description": "Create a new directory at the specified path", - "operationId": "fsMkdir", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/FSMkdirParams" - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "type": "object", - "properties": {} - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error creating directory", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "oneOf": [ - { - "$ref": "#/components/schemas/DefaultError" - }, - { - "$ref": "#/components/schemas/RawFsError" - } - ], - "discriminator": { - "propertyName": "code" - } - } - } - } - ] - } - } - } - } - } - } - }, - "/fs/watch": { - "post": { - "summary": "Watch file/directory", - "description": "Watch a file or directory for changes", - "operationId": "fsWatch", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/FSWatchParams" - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "$ref": "#/components/schemas/FSWatchResult" - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error watching file/directory", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "oneOf": [ - { - "$ref": "#/components/schemas/DefaultError" - }, - { - "$ref": "#/components/schemas/RawFsError" - } - ], - "discriminator": { - "propertyName": "code" - } - } - } - } - ] - } - } - } - } - } - } - }, - "/fs/unwatch": { - "post": { - "summary": "Stop watching file/directory", - "description": "Stop watching a file or directory for changes", - "operationId": "fsUnwatch", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/FSUnwatchParams" - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "type": "object", - "properties": {} - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error unwatching file/directory", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "oneOf": [ - { - "$ref": "#/components/schemas/DefaultError" - }, - { - "$ref": "#/components/schemas/RawFsError" - } - ], - "discriminator": { - "propertyName": "code" - } - } - } - } - ] - } - } - } - } - } - } - } - }, - "components": { - "schemas": { - "SuccessResponse": { - "type": "object", - "properties": { - "status": { - "type": "number", - "enum": [0], - "description": "Status code for successful operations" - }, - "result": { - "type": "object", - "description": "Result payload for the operation" - } - }, - "required": ["status", "result"] - }, - "ErrorResponse": { - "type": "object", - "properties": { - "status": { - "type": "number", - "enum": [1], - "description": "Status code for error operations" - }, - "error": { - "oneOf": [ - { - "$ref": "#/components/schemas/DefaultError" - }, - { - "$ref": "#/components/schemas/RawFsError" - } - ], - "discriminator": { - "propertyName": "code" - } - } - }, - "required": ["status", "error"] - }, - "DefaultError": { - "type": "object", - "properties": { - "code": { - "$ref": "#/components/schemas/PitcherErrorCode", - "description": "Error code identifying the type of error" - }, - "data": { - "type": "object", - "description": "Additional error details", - "nullable": true - }, - "publicMessage": { - "type": "string", - "description": "Human-readable error message that can be displayed to users", - "nullable": true - } - }, - "required": ["code"] - }, - "RawFsError": { - "type": "object", - "properties": { - "code": { - "type": "number", - "enum": [102], - "description": "RAWFS_ERROR code" - }, - "data": { - "type": "object", - "properties": { - "errno": { - "type": ["number", "null"], - "description": "File system error number, or null if not available" - } - }, - "required": ["errno"] - }, - "publicMessage": { - "type": "string", - "description": "Human-readable error message that can be displayed to users", - "nullable": true - } - }, - "required": ["code", "data"] - }, - "PitcherErrorCode": { - "type": "integer", - "description": "Enumeration of error codes", - "enum": [ - 0, 1, 2, 3, 100, 101, 102, 200, 201, 204, 300, 400, 404, 410, 420, - 430, 440, 450, 460, 470, 500, 600, 601, 602, 704, 800, 801, 802, 803, - 814 - ], - "x-enum-descriptions": [ - "CRITICAL_ERROR", - "FEATURE_UNAVAILABLE", - "NO_ACCESS", - "RATE_LIMIT", - "INVALID_ID", - "INVALID_PATH", - "RAWFS_ERROR", - "SHELL_NOT_ACCESSIBLE", - "SHELL_CLOSED", - "SHELL_NOT_FOUND", - "MODEL_NOT_FOUND", - "GIT_OPERATION_IN_PROGRESS", - "GIT_REMOTE_FILE_NOT_FOUND", - "GIT_FETCH_FAIL", - "GIT_PULL_CONFLICT", - "GIT_RESET_LOCAL_REMOTE_ERROR", - "GIT_PUSH_FAIL", - "GIT_RESET_CHECKOUT_INITIAL_BRANCH_FAIL", - "GIT_PULL_FAIL", - "GIT_TRANSPOSE_LINES_FAIL", - "CHANNEL_NOT_FOUND", - "CONFIG_FILE_ALREADY_EXISTS", - "TASK_NOT_FOUND", - "COMMAND_ALREADY_CONFIGURED", - "COMMAND_NOT_FOUND", - "AI_NOT_AVAILABLE", - "PROMPT_TOO_BIG", - "FAILED_TO_RESPOND", - "AI_TOO_FREQUENT_REQUESTS", - "AI_CHAT_NOT_FOUND" - ] - }, - "WriteFileRequest": { - "type": "object", - "properties": { - "path": { - "type": "string", - "description": "File path to write to" - }, - "content": { - "type": "string", - "format": "binary", - "description": "File content as binary data (Uint8Array)" - }, - "create": { - "type": "boolean", - "description": "Whether to create the file if it doesn't exist", - "default": false - }, - "overwrite": { - "type": "boolean", - "description": "Whether to overwrite the file if it exists", - "default": false - } - }, - "required": ["path", "content"] - }, - "FSReadResult": { - "type": "object", - "properties": { - "treeNodes": { - "type": "array", - "items": { - "type": "object", - "description": "JSON representation of a node in the file system" - } - }, - "clock": { - "type": "number", - "description": "Current clock value for the file system" - } - }, - "required": ["treeNodes", "clock"] - }, - "FSOperationRequest": { - "type": "object", - "properties": { - "operation": { - "$ref": "#/components/schemas/FSOperation" - } - }, - "required": ["operation"] - }, - "FSOperation": { - "oneOf": [ - { - "$ref": "#/components/schemas/FSCreateOperation" - }, - { - "$ref": "#/components/schemas/FSDeleteOperation" - }, - { - "$ref": "#/components/schemas/FSMoveOperation" - } - ], - "discriminator": { - "propertyName": "type" - } - }, - "FSCreateOperation": { - "type": "object", - "properties": { - "type": { - "type": "string", - "enum": ["create"] - }, - "parentId": { - "type": "string", - "description": "ID of the parent directory" - }, - "newEntry": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "ID of the new entry" - }, - "type": { - "type": "string", - "enum": ["directory", "file"], - "description": "Type of the node" - }, - "name": { - "type": "string", - "description": "Name of the new entry" - } - }, - "required": ["id", "type", "name"] - } - }, - "required": ["type", "parentId", "newEntry"] - }, - "FSDeleteOperation": { - "type": "object", - "properties": { - "type": { - "type": "string", - "enum": ["delete"] - }, - "id": { - "type": "string", - "description": "ID of the entry to delete" - } - }, - "required": ["type", "id"] - }, - "FSMoveOperation": { - "type": "object", - "properties": { - "type": { - "type": "string", - "enum": ["move"] - }, - "id": { - "type": "string", - "description": "ID of the entry to move" - }, - "parentId": { - "type": "string", - "description": "ID of the new parent directory", - "nullable": true - }, - "name": { - "type": "string", - "description": "New name for the entry", - "nullable": true - } - }, - "required": ["type", "id"] - }, - "FSOperationResult": { - "oneOf": [ - { - "type": "object", - "properties": { - "code": { - "type": "number", - "enum": [0], - "description": "Success code" - }, - "clock": { - "type": "number", - "description": "Current clock value" - } - }, - "required": ["code", "clock"] - }, - { - "type": "object", - "properties": { - "code": { - "type": "number", - "enum": [1], - "description": "Ignored code" - } - }, - "required": ["code"] - } - ], - "discriminator": { - "propertyName": "code" - } - }, - "FSSearchParams": { - "type": "object", - "properties": { - "text": { - "type": "string", - "description": "Text to search for" - }, - "glob": { - "type": "string", - "description": "Glob pattern to filter files", - "nullable": true - }, - "isRegex": { - "type": "boolean", - "description": "Whether to treat the search text as a regular expression", - "nullable": true - }, - "caseSensitivity": { - "type": "string", - "enum": ["smart", "enabled", "disabled"], - "description": "Case sensitivity setting for the search", - "nullable": true - } - }, - "required": ["text"] - }, - "SearchResult": { - "type": "object", - "properties": { - "fileId": { - "type": "string", - "description": "ID of the file containing the match" - }, - "lines": { - "type": "object", - "properties": { - "text": { - "type": "string", - "description": "Text of the line containing the match" - } - }, - "required": ["text"] - }, - "lineNumber": { - "type": "integer", - "description": "Line number of the match" - }, - "absoluteOffset": { - "type": "integer", - "description": "Absolute offset of the match in the file" - }, - "submatches": { - "type": "array", - "items": { - "$ref": "#/components/schemas/SearchSubMatch" - } - } - }, - "required": [ - "fileId", - "lines", - "lineNumber", - "absoluteOffset", - "submatches" - ] - }, - "SearchSubMatch": { - "type": "object", - "properties": { - "match": { - "type": "object", - "properties": { - "text": { - "type": "string", - "description": "Matched text" - } - }, - "required": ["text"] - }, - "start": { - "type": "integer", - "description": "Start position of the match" - }, - "end": { - "type": "integer", - "description": "End position of the match" - } - }, - "required": ["match", "start", "end"] - }, - "FSStreamingSearchParams": { - "type": "object", - "properties": { - "searchId": { - "type": "string", - "description": "ID for the search operation" - }, - "text": { - "type": "string", - "description": "Text to search for" - }, - "glob": { - "type": "string", - "description": "Glob pattern to filter files", - "nullable": true - }, - "isRegex": { - "type": "boolean", - "description": "Whether to treat the search text as a regular expression", - "nullable": true - }, - "caseSensitivity": { - "type": "string", - "enum": ["smart", "enabled", "disabled"], - "description": "Case sensitivity setting for the search", - "nullable": true - }, - "maxResults": { - "type": "integer", - "description": "Maximum number of results to return (default: 10,000)", - "nullable": true - } - }, - "required": ["searchId", "text"] - }, - "PathSearchParams": { - "type": "object", - "properties": { - "text": { - "type": "string", - "description": "Text to search for in file paths" - } - }, - "required": ["text"] - }, - "PathSearchResult": { - "type": "object", - "properties": { - "matches": { - "type": "array", - "items": { - "$ref": "#/components/schemas/PathSearchMatch" - } - } - }, - "required": ["matches"] - }, - "PathSearchMatch": { - "type": "object", - "properties": { - "path": { - "type": "string", - "description": "Path that matched the search" - }, - "submatches": { - "type": "array", - "items": { - "$ref": "#/components/schemas/SearchSubMatch" - } - } - }, - "required": ["path", "submatches"] - }, - "InvalidIdError": { - "type": "object", - "properties": { - "code": { - "type": "number", - "enum": [100], - "description": "INVALID_ID error code" - } - }, - "required": ["code"] - }, - "FSReadFileParams": { - "type": "object", - "properties": { - "path": { - "type": "string", - "description": "Path to the file to read" - } - }, - "required": ["path"] - }, - "FSReadFileResult": { - "type": "object", - "properties": { - "content": { - "type": "string", - "format": "binary", - "description": "File content as binary data" - } - }, - "required": ["content"] - }, - "FSReadDirParams": { - "type": "object", - "properties": { - "path": { - "type": "string", - "description": "Path to the directory to read" - } - }, - "required": ["path"] - }, - "FSReadDirResult": { - "type": "object", - "properties": { - "entries": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Name of the entry" - }, - "type": { - "type": "string", - "enum": ["directory", "file"], - "description": "Type of the entry" - }, - "isSymlink": { - "type": "boolean", - "description": "Whether the entry is a symlink" - } - }, - "required": ["name", "type", "isSymlink"] - } - } - }, - "required": ["entries"] - }, - "FSStatParams": { - "type": "object", - "properties": { - "path": { - "type": "string", - "description": "Path to the file or directory to stat" - } - }, - "required": ["path"] - }, - "FSStatResult": { - "type": "object", - "properties": { - "type": { - "type": "string", - "enum": ["directory", "file"], - "description": "Type of the entry" - }, - "isSymlink": { - "type": "boolean", - "description": "Whether the entry is a symlink" - }, - "size": { - "type": "integer", - "description": "Size of the file in bytes" - }, - "mtime": { - "type": "integer", - "description": "Last modified time" - }, - "ctime": { - "type": "integer", - "description": "Creation time" - }, - "atime": { - "type": "integer", - "description": "Last accessed time" - } - }, - "required": ["type", "isSymlink", "size", "mtime", "ctime", "atime"] - }, - "FSCopyParams": { - "type": "object", - "properties": { - "from": { - "type": "string", - "description": "Path to copy from" - }, - "to": { - "type": "string", - "description": "Path to copy to" - }, - "recursive": { - "type": "boolean", - "description": "Whether to copy directories recursively", - "nullable": true - }, - "overwrite": { - "type": "boolean", - "description": "Whether to overwrite existing files", - "nullable": true - } - }, - "required": ["from", "to"] - }, - "FSRenameParams": { - "type": "object", - "properties": { - "from": { - "type": "string", - "description": "Path to rename from" - }, - "to": { - "type": "string", - "description": "Path to rename to" - }, - "overwrite": { - "type": "boolean", - "description": "Whether to overwrite existing files", - "nullable": true - } - }, - "required": ["from", "to"] - }, - "FSRemoveParams": { - "type": "object", - "properties": { - "path": { - "type": "string", - "description": "Path to remove" - }, - "recursive": { - "type": "boolean", - "description": "Whether to remove directories recursively", - "nullable": true - } - }, - "required": ["path"] - }, - "FSMkdirParams": { - "type": "object", - "properties": { - "path": { - "type": "string", - "description": "Path to create directory at" - }, - "recursive": { - "type": "boolean", - "description": "Whether to create parent directories if they don't exist", - "nullable": true - } - }, - "required": ["path"] - }, - "FSWatchParams": { - "type": "object", - "properties": { - "path": { - "type": "string", - "description": "Path to watch" - }, - "recursive": { - "type": "boolean", - "description": "Whether to watch directories recursively", - "nullable": true - }, - "excludes": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Glob patterns to exclude from watching", - "nullable": true - } - }, - "required": ["path"] - }, - "FSWatchResult": { - "type": "object", - "properties": { - "watchId": { - "type": "string", - "description": "ID of the watch" - } - }, - "required": ["watchId"] - }, - "FSUnwatchParams": { - "type": "object", - "properties": { - "watchId": { - "type": "string", - "description": "ID of the watch to stop" - } - }, - "required": ["watchId"] - } - } - } -} diff --git a/openapi-sandbox-git.json b/openapi-sandbox-git.json deleted file mode 100644 index 827a6e9..0000000 --- a/openapi-sandbox-git.json +++ /dev/null @@ -1,1369 +0,0 @@ -{ - "openapi": "3.0.0", - "info": { - "title": "Sandbox Git API", - "description": "API for managing git operations in CodeSandbox", - "version": "1.0.0" - }, - "paths": { - "/git/status": { - "post": { - "summary": "Get git status", - "description": "Retrieve current git status including changed files, branch information, and commits", - "operationId": "gitStatus", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": {} - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "$ref": "#/components/schemas/GitStatus" - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error retrieving git status", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "$ref": "#/components/schemas/CommonError" - } - } - } - ] - } - } - } - } - } - } - }, - "/git/remotes": { - "post": { - "summary": "Get git remotes", - "description": "Retrieve git remote information", - "operationId": "gitRemotes", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": {} - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "$ref": "#/components/schemas/GitRemotes" - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error retrieving git remotes", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "$ref": "#/components/schemas/CommonError" - } - } - } - ] - } - } - } - } - } - } - }, - "/git/targetDiff": { - "post": { - "summary": "Get git target diff", - "description": "Retrieve diff between current branch and target branch", - "operationId": "gitTargetDiff", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "branch": { - "type": "string", - "description": "Branch to compare against" - } - }, - "required": ["branch"] - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "$ref": "#/components/schemas/GitTargetDiff" - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error retrieving git target diff", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "$ref": "#/components/schemas/CommonError" - } - } - } - ] - } - } - } - } - } - } - }, - "/git/pull": { - "post": { - "summary": "Pull from remote", - "description": "Pull changes from remote repository", - "operationId": "gitPull", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "branch": { - "type": "string", - "description": "Branch to pull from" - }, - "force": { - "type": "boolean", - "description": "Force pull even if there are conflicts" - } - } - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "type": "null" - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error pulling from remote", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "$ref": "#/components/schemas/CommonError" - } - } - } - ] - } - } - } - } - } - } - }, - "/git/discard": { - "post": { - "summary": "Discard changes", - "description": "Discard local changes for specified paths", - "operationId": "gitDiscard", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "paths": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Paths of files to discard changes" - } - } - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "type": "object", - "properties": { - "paths": { - "type": "array", - "items": { - "type": "string" - } - } - } - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error discarding changes", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "$ref": "#/components/schemas/CommonError" - } - } - } - ] - } - } - } - } - } - } - }, - "/git/commit": { - "post": { - "summary": "Commit changes", - "description": "Commit changes to the repository", - "operationId": "gitCommit", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "paths": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Paths of files to commit" - }, - "message": { - "type": "string", - "description": "Commit message" - }, - "push": { - "type": "boolean", - "description": "Whether to push the commit immediately" - } - }, - "required": ["message"] - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "type": "object", - "properties": { - "shellId": { - "type": "string", - "description": "ID of the shell process" - } - }, - "required": ["shellId"] - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error committing changes", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "$ref": "#/components/schemas/CommonError" - } - } - } - ] - } - } - } - } - } - } - }, - "/git/push": { - "post": { - "summary": "Push changes", - "description": "Push local commits to remote repository", - "operationId": "gitPush", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": {} - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "type": "null" - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error pushing changes", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "$ref": "#/components/schemas/CommonError" - } - } - } - ] - } - } - } - } - } - } - }, - "/git/pushToRemote": { - "post": { - "summary": "Push to remote", - "description": "Push to a specific remote repository", - "operationId": "gitPushToRemote", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "url": { - "type": "string", - "description": "URL of the remote repository" - }, - "branch": { - "type": "string", - "description": "Branch to push to" - }, - "squashAllCommits": { - "type": "boolean", - "description": "Whether to squash all commits into one" - } - }, - "required": ["url", "branch"] - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "type": "null" - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error pushing to remote", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "$ref": "#/components/schemas/CommonError" - } - } - } - ] - } - } - } - } - } - } - }, - "/git/renameBranch": { - "post": { - "summary": "Rename branch", - "description": "Rename a git branch", - "operationId": "gitRenameBranch", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "oldBranch": { - "type": "string", - "description": "Current branch name" - }, - "newBranch": { - "type": "string", - "description": "New branch name" - } - }, - "required": ["oldBranch", "newBranch"] - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "type": "null" - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error renaming branch", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "$ref": "#/components/schemas/CommonError" - } - } - } - ] - } - } - } - } - } - } - }, - "/git/remoteContent": { - "post": { - "summary": "Get remote content", - "description": "Retrieve content from a remote repository", - "operationId": "gitRemoteContent", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/GitRemoteParams" - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "type": "object", - "properties": { - "content": { - "type": "string", - "description": "Content of the file" - } - }, - "required": ["content"] - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error retrieving remote content", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "$ref": "#/components/schemas/CommonError" - } - } - } - ] - } - } - } - } - } - } - }, - "/git/diffStatus": { - "post": { - "summary": "Get diff status", - "description": "Retrieve diff status between two git references", - "operationId": "gitDiffStatus", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/GitDiffStatusParams" - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "$ref": "#/components/schemas/GitDiffStatusResult" - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error retrieving diff status", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "$ref": "#/components/schemas/CommonError" - } - } - } - ] - } - } - } - } - } - } - }, - "/git/resetLocalWithRemote": { - "post": { - "summary": "Reset local with remote", - "description": "Reset local repository to match the remote state", - "operationId": "gitResetLocalWithRemote", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": {} - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "type": "null" - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error resetting local with remote", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "$ref": "#/components/schemas/CommonError" - } - } - } - ] - } - } - } - } - } - } - }, - "/git/checkoutInitialBranch": { - "post": { - "summary": "Checkout initial branch", - "description": "Checkout the initial branch of the repository", - "operationId": "gitCheckoutInitialBranch", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": {} - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "type": "null" - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error checking out initial branch", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "$ref": "#/components/schemas/CommonError" - } - } - } - ] - } - } - } - } - } - } - }, - "/git/transposeLines": { - "post": { - "summary": "Transpose lines", - "description": "Transpose line numbers from one git reference to another", - "operationId": "gitTransposeLines", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "array", - "items": { - "type": "object", - "properties": { - "sha": { - "type": "string", - "description": "Git commit SHA" - }, - "path": { - "type": "string", - "description": "Path to the file" - }, - "line": { - "type": "number", - "description": "Line number to transpose" - } - }, - "required": ["sha", "path", "line"] - } - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "type": "array", - "items": { - "oneOf": [ - { - "type": "object", - "properties": { - "path": { - "type": "string" - }, - "line": { - "type": "number" - } - }, - "required": ["path", "line"] - }, - { - "type": "null" - } - ] - } - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error transposing lines", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "$ref": "#/components/schemas/CommonError" - } - } - } - ] - } - } - } - } - } - } - } - }, - "components": { - "schemas": { - "SuccessResponse": { - "type": "object", - "properties": { - "status": { - "type": "number", - "enum": [0], - "description": "Status code for successful operations" - }, - "result": { - "type": "object", - "description": "Result payload for the operation" - } - }, - "required": ["status", "result"] - }, - "ErrorResponse": { - "type": "object", - "properties": { - "status": { - "type": "number", - "enum": [1], - "description": "Status code for error operations" - }, - "error": { - "type": "object", - "description": "Error details" - } - }, - "required": ["status", "error"] - }, - "CommonError": { - "oneOf": [ - { - "type": "object", - "properties": { - "code": { - "type": "string", - "enum": [ - "GIT_OPERATION_IN_PROGRESS", - "GIT_REMOTE_FILE_NOT_FOUND" - ], - "description": "Error code" - }, - "message": { - "type": "string", - "description": "Error message" - } - }, - "required": ["code", "message"] - }, - { - "type": "object", - "properties": { - "code": { - "type": "string", - "description": "Protocol error code" - }, - "message": { - "type": "string", - "description": "Error message" - }, - "data": { - "type": "object", - "description": "Additional error data" - } - }, - "required": ["code", "message"] - } - ] - }, - "GitStatusShortFormat": { - "type": "string", - "enum": ["", "M", "A", "D", "R", "C", "U", "?"], - "description": "Git status short format codes" - }, - "GitItem": { - "type": "object", - "properties": { - "path": { - "type": "string", - "description": "File path" - }, - "index": { - "$ref": "#/components/schemas/GitStatusShortFormat" - }, - "workingTree": { - "$ref": "#/components/schemas/GitStatusShortFormat" - }, - "isStaged": { - "type": "boolean", - "description": "Whether the file is staged" - }, - "isConflicted": { - "type": "boolean", - "description": "Whether the file has conflicts" - }, - "fileId": { - "type": "string", - "description": "Unique identifier for the file" - } - }, - "required": ["path", "index", "workingTree", "isStaged", "isConflicted"] - }, - "GitChangedFiles": { - "type": "object", - "additionalProperties": { - "$ref": "#/components/schemas/GitItem" - }, - "description": "Map of file IDs to Git items" - }, - "GitBranchProperties": { - "type": "object", - "properties": { - "head": { - "type": ["string", "null"], - "description": "Current HEAD reference" - }, - "branch": { - "type": ["string", "null"], - "description": "Current branch name" - }, - "ahead": { - "type": "number", - "description": "Number of commits ahead of the remote" - }, - "behind": { - "type": "number", - "description": "Number of commits behind the remote" - }, - "safe": { - "type": "boolean", - "description": "Whether the branch is safe to operate on" - } - }, - "required": ["ahead", "behind", "safe"] - }, - "GitCommit": { - "type": "object", - "properties": { - "hash": { - "type": "string", - "description": "Commit hash" - }, - "date": { - "type": "string", - "description": "Commit date" - }, - "message": { - "type": "string", - "description": "Commit message" - }, - "author": { - "type": "string", - "description": "Commit author" - } - }, - "required": ["hash", "date", "message", "author"] - }, - "GitStatus": { - "type": "object", - "properties": { - "changedFiles": { - "$ref": "#/components/schemas/GitChangedFiles" - }, - "deletedFiles": { - "type": "array", - "items": { - "$ref": "#/components/schemas/GitItem" - } - }, - "conflicts": { - "type": "boolean", - "description": "Whether there are remote conflicts" - }, - "localChanges": { - "type": "boolean", - "description": "Whether there are local changes" - }, - "remote": { - "$ref": "#/components/schemas/GitBranchProperties" - }, - "target": { - "$ref": "#/components/schemas/GitBranchProperties" - }, - "head": { - "type": "string", - "description": "Current HEAD reference" - }, - "commits": { - "type": "array", - "items": { - "$ref": "#/components/schemas/GitCommit" - } - }, - "branch": { - "type": ["string", "null"], - "description": "Current branch name" - }, - "isMerging": { - "type": "boolean", - "description": "Whether a merge is in progress" - } - }, - "required": [ - "changedFiles", - "deletedFiles", - "conflicts", - "localChanges", - "remote", - "target", - "commits", - "branch", - "isMerging" - ] - }, - "GitTargetDiff": { - "type": "object", - "properties": { - "ahead": { - "type": "number", - "description": "Number of commits ahead of the target" - }, - "behind": { - "type": "number", - "description": "Number of commits behind the target" - }, - "commits": { - "type": "array", - "items": { - "$ref": "#/components/schemas/GitCommit" - } - } - }, - "required": ["ahead", "behind", "commits"] - }, - "GitRemotes": { - "type": "object", - "properties": { - "origin": { - "type": "string", - "description": "Origin remote URL" - }, - "upstream": { - "type": "string", - "description": "Upstream remote URL" - } - }, - "required": ["origin", "upstream"] - }, - "GitRemoteParams": { - "type": "object", - "properties": { - "reference": { - "type": "string", - "description": "Branch or commit hash" - }, - "path": { - "type": "string", - "description": "Path to the file" - } - }, - "required": ["reference", "path"] - }, - "GitDiffStatusParams": { - "type": "object", - "properties": { - "base": { - "type": "string", - "description": "Base reference used for diffing" - }, - "head": { - "type": "string", - "description": "Head reference used for diffing" - } - }, - "required": ["base", "head"] - }, - "GitDiffStatusItem": { - "type": "object", - "properties": { - "status": { - "$ref": "#/components/schemas/GitStatusShortFormat" - }, - "path": { - "type": "string", - "description": "Path to the file" - }, - "oldPath": { - "type": "string", - "description": "Original path for renamed files" - }, - "hunks": { - "type": "array", - "items": { - "type": "object", - "properties": { - "original": { - "type": "object", - "properties": { - "start": { - "type": "number" - }, - "end": { - "type": "number" - } - }, - "required": ["start", "end"] - }, - "modified": { - "type": "object", - "properties": { - "start": { - "type": "number" - }, - "end": { - "type": "number" - } - }, - "required": ["start", "end"] - } - }, - "required": ["original", "modified"] - } - } - }, - "required": ["status", "path", "hunks"] - }, - "GitDiffStatusResult": { - "type": "object", - "properties": { - "files": { - "type": "array", - "items": { - "$ref": "#/components/schemas/GitDiffStatusItem" - } - } - }, - "required": ["files"] - } - } - } -} diff --git a/openapi-sandbox-setup.json b/openapi-sandbox-setup.json deleted file mode 100644 index bc8b5c4..0000000 --- a/openapi-sandbox-setup.json +++ /dev/null @@ -1,570 +0,0 @@ -{ - "openapi": "3.0.0", - "info": { - "title": "Sandbox Setup API", - "description": "API for managing sandbox setup operations", - "version": "1.0.0" - }, - "paths": { - "/setup/get": { - "post": { - "summary": "Get setup progress", - "description": "Retrieve the current setup progress status", - "operationId": "setupGet", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": {} - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "$ref": "#/components/schemas/SetupProgress" - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error retrieving setup progress", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "$ref": "#/components/schemas/ProtocolError" - } - } - } - ] - } - } - } - } - } - } - }, - "/setup/skip": { - "post": { - "summary": "Skip setup step", - "description": "Skip a specific step in the setup process", - "operationId": "setupSkipStep", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "stepIndexToSkip": { - "type": "number", - "description": "Index of the step to skip" - } - }, - "required": ["stepIndexToSkip"] - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "$ref": "#/components/schemas/SetupProgress" - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error skipping step", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "$ref": "#/components/schemas/ProtocolError" - } - } - } - ] - } - } - } - } - } - } - }, - "/setup/skipAll": { - "post": { - "summary": "Skip all setup steps", - "description": "Skip all remaining steps in the setup process", - "operationId": "setupSkipAll", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "null" - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "$ref": "#/components/schemas/SetupProgress" - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error skipping all steps", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "$ref": "#/components/schemas/ProtocolError" - } - } - } - ] - } - } - } - } - } - } - }, - "/setup/disable": { - "post": { - "summary": "Disable setup", - "description": "Disable the setup process", - "operationId": "setupDisable", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "null" - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "$ref": "#/components/schemas/SetupProgress" - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error disabling setup", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "$ref": "#/components/schemas/ProtocolError" - } - } - } - ] - } - } - } - } - } - } - }, - "/setup/enable": { - "post": { - "summary": "Enable setup", - "description": "Enable the setup process", - "operationId": "setupEnable", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "null" - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "$ref": "#/components/schemas/SetupProgress" - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error enabling setup", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "$ref": "#/components/schemas/ProtocolError" - } - } - } - ] - } - } - } - } - } - } - }, - "/setup/init": { - "post": { - "summary": "Initialize setup", - "description": "Initialize the setup process", - "operationId": "setupInit", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "null" - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "$ref": "#/components/schemas/SetupProgress" - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error initializing setup", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "$ref": "#/components/schemas/ProtocolError" - } - } - } - ] - } - } - } - } - } - } - }, - "/setup/setStep": { - "post": { - "summary": "Set current setup step", - "description": "Set the current step in the setup process (used for restarting)", - "operationId": "setupSetStep", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "stepIndex": { - "type": "number", - "description": "Index of the step to set as current" - } - }, - "required": ["stepIndex"] - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "$ref": "#/components/schemas/SetupProgress" - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error setting current step", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "$ref": "#/components/schemas/ProtocolError" - } - } - } - ] - } - } - } - } - } - } - } - }, - "components": { - "schemas": { - "SuccessResponse": { - "type": "object", - "properties": { - "status": { - "type": "number", - "enum": [0], - "description": "Status code for successful operations" - }, - "result": { - "type": "object", - "description": "Result payload for the operation" - } - }, - "required": ["status", "result"] - }, - "ErrorResponse": { - "type": "object", - "properties": { - "status": { - "type": "number", - "enum": [1], - "description": "Status code for error operations" - }, - "error": { - "type": "object", - "description": "Error details" - } - }, - "required": ["status", "error"] - }, - "ProtocolError": { - "type": "object", - "properties": { - "code": { - "type": "number", - "description": "Error code" - }, - "message": { - "type": "string", - "description": "Error message" - }, - "data": { - "type": "object", - "description": "Additional error data", - "nullable": true - } - }, - "required": ["code", "message"] - }, - "SetupShellStatus": { - "type": "string", - "enum": ["SUCCEEDED", "FAILED", "SKIPPED"], - "description": "Status of a setup shell step" - }, - "Step": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Name of the setup step" - }, - "command": { - "type": "string", - "description": "Command to execute for this step" - }, - "shellId": { - "type": "string", - "description": "ID of the shell executing the command", - "nullable": true - }, - "finishStatus": { - "$ref": "#/components/schemas/SetupShellStatus", - "nullable": true, - "description": "Status of the step after completion" - } - }, - "required": ["name", "command", "shellId", "finishStatus"] - }, - "SetupProgress": { - "type": "object", - "properties": { - "state": { - "type": "string", - "enum": ["IDLE", "IN_PROGRESS", "FINISHED", "STOPPED"], - "description": "Current state of the setup process" - }, - "steps": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Step" - }, - "description": "List of setup steps" - }, - "currentStepIndex": { - "type": "number", - "description": "Index of the current step being executed" - } - }, - "required": ["state", "steps", "currentStepIndex"] - } - } - } -} diff --git a/openapi-sandbox-shell.json b/openapi-sandbox-shell.json deleted file mode 100644 index e362489..0000000 --- a/openapi-sandbox-shell.json +++ /dev/null @@ -1,916 +0,0 @@ -{ - "openapi": "3.0.0", - "info": { - "title": "Sandbox Shell API", - "description": "API for managing terminal and command shells in the sandbox", - "version": "1.0.0" - }, - "paths": { - "/shell/create": { - "post": { - "summary": "Create a new shell", - "description": "Creates a new terminal or command shell", - "operationId": "shellCreate", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "command": { - "type": "string", - "description": "Command to execute in the shell" - }, - "cwd": { - "type": "string", - "description": "Working directory for the shell" - }, - "size": { - "$ref": "#/components/schemas/ShellSize", - "description": "Terminal size dimensions" - }, - "type": { - "$ref": "#/components/schemas/ShellProcessType", - "description": "Type of shell to create" - }, - "isSystemShell": { - "type": "boolean", - "description": "Whether this shell is started by the editor itself to run a specific process" - } - } - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "$ref": "#/components/schemas/OpenShellDTO" - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error creating shell", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "$ref": "#/components/schemas/CommonError" - } - } - } - ] - } - } - } - } - } - } - }, - "/shell/in": { - "post": { - "summary": "Send input to shell", - "description": "Sends user input to an active shell", - "operationId": "shellIn", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "shellId": { - "$ref": "#/components/schemas/ShellId", - "description": "ID of the target shell" - }, - "input": { - "type": "string", - "description": "Input to send to the shell" - }, - "size": { - "$ref": "#/components/schemas/ShellSize", - "description": "Current terminal dimensions" - } - }, - "required": ["shellId", "input", "size"] - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "type": "null" - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error sending input to shell", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "$ref": "#/components/schemas/CommonError" - } - } - } - ] - } - } - } - } - } - } - }, - "/shell/list": { - "post": { - "summary": "List all shells", - "description": "Retrieves a list of all available shells", - "operationId": "shellList", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": {} - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "type": "object", - "properties": { - "shells": { - "type": "array", - "items": { - "$ref": "#/components/schemas/ShellDTO" - } - } - }, - "required": ["shells"] - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error listing shells", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "$ref": "#/components/schemas/CommonError" - } - } - } - ] - } - } - } - } - } - } - }, - "/shell/open": { - "post": { - "summary": "Open an existing shell", - "description": "Opens an existing shell and retrieves its buffer", - "operationId": "shellOpen", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "shellId": { - "$ref": "#/components/schemas/ShellId", - "description": "ID of the shell to open" - }, - "size": { - "$ref": "#/components/schemas/ShellSize", - "description": "Terminal dimensions" - } - }, - "required": ["shellId", "size"] - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "$ref": "#/components/schemas/OpenShellDTO" - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error opening shell", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "$ref": "#/components/schemas/CommonError" - } - } - } - ] - } - } - } - } - } - } - }, - "/shell/close": { - "post": { - "summary": "Close a shell", - "description": "Closes a shell without terminating the underlying process", - "operationId": "shellClose", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "shellId": { - "$ref": "#/components/schemas/ShellId", - "description": "ID of the shell to close" - } - }, - "required": ["shellId"] - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "type": "null" - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error closing shell", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "$ref": "#/components/schemas/CommonError" - } - } - } - ] - } - } - } - } - } - } - }, - "/shell/restart": { - "post": { - "summary": "Restart a shell", - "description": "Restarts an existing shell process", - "operationId": "shellRestart", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "shellId": { - "$ref": "#/components/schemas/ShellId", - "description": "ID of the shell to restart" - } - }, - "required": ["shellId"] - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "type": "null" - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error restarting shell", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "$ref": "#/components/schemas/CommonError" - } - } - } - ] - } - } - } - } - } - } - }, - "/shell/terminate": { - "post": { - "summary": "Terminate a shell", - "description": "Terminates a shell and its underlying process", - "operationId": "shellTerminate", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "shellId": { - "$ref": "#/components/schemas/ShellId", - "description": "ID of the shell to terminate" - } - }, - "required": ["shellId"] - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "$ref": "#/components/schemas/ShellDTO" - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error terminating shell", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "$ref": "#/components/schemas/CommonError" - } - } - } - ] - } - } - } - } - } - } - }, - "/shell/resize": { - "post": { - "summary": "Resize a shell", - "description": "Updates the dimensions of a shell", - "operationId": "shellResize", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "shellId": { - "$ref": "#/components/schemas/ShellId", - "description": "ID of the shell to resize" - }, - "size": { - "$ref": "#/components/schemas/ShellSize", - "description": "New terminal dimensions" - } - }, - "required": ["shellId", "size"] - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "type": "null" - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error resizing shell", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "$ref": "#/components/schemas/CommonError" - } - } - } - ] - } - } - } - } - } - } - }, - "/shell/rename": { - "post": { - "summary": "Rename a shell", - "description": "Updates the name of a shell", - "operationId": "shellRename", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "shellId": { - "$ref": "#/components/schemas/ShellId", - "description": "ID of the shell to rename" - }, - "name": { - "type": "string", - "description": "New name for the shell" - } - }, - "required": ["shellId", "name"] - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "type": "null" - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error renaming shell", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "$ref": "#/components/schemas/CommonError" - } - } - } - ] - } - } - } - } - } - } - } - }, - "components": { - "schemas": { - "SuccessResponse": { - "type": "object", - "properties": { - "status": { - "type": "number", - "enum": [0], - "description": "Status code for successful operations" - }, - "result": { - "type": "object", - "description": "Result payload for the operation" - } - }, - "required": ["status", "result"] - }, - "ErrorResponse": { - "type": "object", - "properties": { - "status": { - "type": "number", - "enum": [1], - "description": "Status code for error operations" - }, - "error": { - "type": "object", - "description": "Error details" - } - }, - "required": ["status", "error"] - }, - "ShellId": { - "type": "string", - "description": "Unique identifier for a shell" - }, - "ShellSize": { - "type": "object", - "properties": { - "cols": { - "type": "number", - "description": "Number of columns in the terminal" - }, - "rows": { - "type": "number", - "description": "Number of rows in the terminal" - } - }, - "required": ["cols", "rows"] - }, - "ShellProcessType": { - "type": "string", - "enum": ["TERMINAL", "COMMAND"], - "description": "Type of shell process" - }, - "ShellProcessStatus": { - "type": "string", - "enum": ["RUNNING", "FINISHED", "ERROR", "KILLED", "RESTARTING"], - "description": "Current status of the shell process" - }, - "BaseShellDTO": { - "type": "object", - "properties": { - "shellId": { - "$ref": "#/components/schemas/ShellId" - }, - "name": { - "type": "string", - "description": "Display name of the shell" - }, - "status": { - "$ref": "#/components/schemas/ShellProcessStatus" - }, - "exitCode": { - "type": "number", - "description": "Exit code of the process if it has finished", - "nullable": true - } - }, - "required": ["shellId", "name", "status"] - }, - "CommandShellDTO": { - "allOf": [ - { - "$ref": "#/components/schemas/BaseShellDTO" - }, - { - "type": "object", - "properties": { - "shellType": { - "type": "string", - "enum": ["COMMAND"], - "description": "Indicates this is a command shell" - }, - "startCommand": { - "type": "string", - "description": "The command that was executed to start this shell" - } - }, - "required": ["shellType", "startCommand"] - } - ] - }, - "TerminalShellDTO": { - "allOf": [ - { - "$ref": "#/components/schemas/BaseShellDTO" - }, - { - "type": "object", - "properties": { - "shellType": { - "type": "string", - "enum": ["TERMINAL"], - "description": "Indicates this is a terminal shell" - }, - "ownerUsername": { - "type": "string", - "description": "Username of the shell owner" - }, - "isSystemShell": { - "type": "boolean", - "description": "Whether this is a system shell" - } - }, - "required": ["shellType", "ownerUsername", "isSystemShell"] - } - ] - }, - "ShellDTO": { - "oneOf": [ - { - "$ref": "#/components/schemas/CommandShellDTO" - }, - { - "$ref": "#/components/schemas/TerminalShellDTO" - } - ], - "discriminator": { - "propertyName": "shellType", - "mapping": { - "COMMAND": "#/components/schemas/CommandShellDTO", - "TERMINAL": "#/components/schemas/TerminalShellDTO" - } - } - }, - "OpenCommandShellDTO": { - "allOf": [ - { - "$ref": "#/components/schemas/CommandShellDTO" - }, - { - "type": "object", - "properties": { - "buffer": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Content buffer of the shell" - } - }, - "required": ["buffer"] - } - ] - }, - "OpenTerminalShellDTO": { - "allOf": [ - { - "$ref": "#/components/schemas/TerminalShellDTO" - }, - { - "type": "object", - "properties": { - "buffer": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Content buffer of the shell" - } - }, - "required": ["buffer"] - } - ] - }, - "OpenShellDTO": { - "oneOf": [ - { - "$ref": "#/components/schemas/OpenCommandShellDTO" - }, - { - "$ref": "#/components/schemas/OpenTerminalShellDTO" - } - ], - "discriminator": { - "propertyName": "shellType", - "mapping": { - "COMMAND": "#/components/schemas/OpenCommandShellDTO", - "TERMINAL": "#/components/schemas/OpenTerminalShellDTO" - } - } - }, - "CommonError": { - "oneOf": [ - { - "type": "object", - "properties": { - "code": { - "type": "string", - "enum": ["SHELL_NOT_ACCESSIBLE"], - "description": "Error code indicating the shell is not accessible" - }, - "message": { - "type": "string", - "description": "Error message" - } - }, - "required": ["code", "message"] - }, - { - "type": "object", - "properties": { - "code": { - "type": "string", - "description": "Protocol error code" - }, - "message": { - "type": "string", - "description": "Error message" - } - }, - "required": ["code", "message"] - } - ] - } - } - } -} diff --git a/openapi-sandbox-system.json b/openapi-sandbox-system.json deleted file mode 100644 index 501f2f5..0000000 --- a/openapi-sandbox-system.json +++ /dev/null @@ -1,348 +0,0 @@ -{ - "openapi": "3.0.0", - "info": { - "title": "Sandbox System API", - "description": "API for managing sandbox system operations", - "version": "1.0.0" - }, - "paths": { - "/system/update": { - "post": { - "summary": "Update system", - "description": "Update the sandbox system", - "operationId": "systemUpdate", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": {} - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "type": "object", - "properties": {} - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error updating system", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "$ref": "#/components/schemas/SystemError" - } - } - } - ] - } - } - } - } - } - } - }, - "/system/hibernate": { - "post": { - "summary": "Hibernate system", - "description": "Put the sandbox system into hibernation mode", - "operationId": "systemHibernate", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": {} - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "type": "null" - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error hibernating system", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "$ref": "#/components/schemas/SystemError" - } - } - } - ] - } - } - } - } - } - } - }, - "/system/metrics": { - "post": { - "summary": "Get system metrics", - "description": "Retrieve current system metrics including CPU, memory and storage usage", - "operationId": "systemMetrics", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": {} - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "$ref": "#/components/schemas/SystemMetricsStatus" - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error retrieving system metrics", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "$ref": "#/components/schemas/SystemError" - } - } - } - ] - } - } - } - } - } - } - } - }, - "components": { - "schemas": { - "SuccessResponse": { - "type": "object", - "properties": { - "status": { - "type": "number", - "enum": [0], - "description": "Status code for successful operations" - }, - "result": { - "type": "object", - "description": "Result payload for the operation" - } - }, - "required": ["status", "result"] - }, - "ErrorResponse": { - "type": "object", - "properties": { - "status": { - "type": "number", - "enum": [1], - "description": "Status code for error operations" - }, - "error": { - "type": "object", - "description": "Error details" - } - }, - "required": ["status", "error"] - }, - "SystemError": { - "type": "object", - "properties": { - "code": { - "type": "number", - "description": "Error code" - }, - "message": { - "type": "string", - "description": "Error message" - }, - "data": { - "type": "object", - "description": "Additional error data", - "nullable": true - } - }, - "required": ["code", "message"] - }, - "SystemMetricsStatus": { - "type": "object", - "properties": { - "cpu": { - "type": "object", - "properties": { - "cores": { - "type": "number", - "description": "Number of CPU cores" - }, - "used": { - "type": "number", - "description": "Used CPU resources" - }, - "configured": { - "type": "number", - "description": "Configured CPU resources" - } - }, - "required": ["cores", "used", "configured"] - }, - "memory": { - "type": "object", - "properties": { - "used": { - "type": "number", - "description": "Used memory in bytes" - }, - "total": { - "type": "number", - "description": "Total available memory in bytes" - }, - "configured": { - "type": "number", - "description": "Configured memory limit in bytes" - } - }, - "required": ["used", "total", "configured"] - }, - "storage": { - "type": "object", - "properties": { - "used": { - "type": "number", - "description": "Used storage in bytes" - }, - "total": { - "type": "number", - "description": "Total available storage in bytes" - }, - "configured": { - "type": "number", - "description": "Configured storage limit in bytes" - } - }, - "required": ["used", "total", "configured"] - } - }, - "required": ["cpu", "memory", "storage"] - }, - "InitStatus": { - "type": "object", - "properties": { - "message": { - "type": "string", - "description": "Status message" - }, - "isError": { - "type": "boolean", - "description": "Whether the status represents an error", - "nullable": true - }, - "progress": { - "type": "number", - "description": "Current progress (0-100)", - "minimum": 0, - "maximum": 100 - }, - "nextProgress": { - "type": "number", - "description": "Next progress target (0-100)", - "minimum": 0, - "maximum": 100 - }, - "stdout": { - "type": "string", - "description": "Standard output from the initialization process", - "nullable": true - } - }, - "required": ["message", "progress", "nextProgress"] - } - } - } -} diff --git a/openapi-sandbox-task.json b/openapi-sandbox-task.json deleted file mode 100644 index 6b5e320..0000000 --- a/openapi-sandbox-task.json +++ /dev/null @@ -1,947 +0,0 @@ -{ - "openapi": "3.0.0", - "info": { - "title": "Sandbox Task API", - "description": "API for managing tasks in sandbox", - "version": "1.0.0" - }, - "paths": { - "/task/list": { - "post": { - "summary": "List tasks", - "description": "Retrieve a list of all configured tasks", - "operationId": "taskList", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": {} - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "$ref": "#/components/schemas/TaskListDTO" - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error retrieving task list", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "$ref": "#/components/schemas/CommonError" - } - } - } - ] - } - } - } - } - } - } - }, - "/task/run": { - "post": { - "summary": "Run task", - "description": "Start execution of a task by ID", - "operationId": "taskRun", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "taskId": { - "type": "string", - "description": "ID of the task to run" - } - }, - "required": ["taskId"] - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "$ref": "#/components/schemas/TaskDTO" - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error running task", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "$ref": "#/components/schemas/TaskError" - } - } - } - ] - } - } - } - } - } - } - }, - "/task/runCommand": { - "post": { - "summary": "Run command", - "description": "Run a shell command directly, optionally saving it as a task", - "operationId": "taskRunCommand", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "command": { - "type": "string", - "description": "Command to run" - }, - "name": { - "type": "string", - "description": "Optional name for the task", - "nullable": true - }, - "saveToConfig": { - "type": "boolean", - "description": "Whether to save this command as a task in the config", - "nullable": true - } - }, - "required": ["command"] - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "$ref": "#/components/schemas/TaskDTO" - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error running command", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "$ref": "#/components/schemas/TaskError" - } - } - } - ] - } - } - } - } - } - } - }, - "/task/stop": { - "post": { - "summary": "Stop task", - "description": "Stop execution of a running task", - "operationId": "taskStop", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "taskId": { - "type": "string", - "description": "ID of the task to stop" - } - }, - "required": ["taskId"] - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "oneOf": [ - { - "$ref": "#/components/schemas/TaskDTO" - }, - { - "type": "null", - "description": "Null when stopping an unconfigured task" - } - ] - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error stopping task", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "$ref": "#/components/schemas/TaskError" - } - } - } - ] - } - } - } - } - } - } - }, - "/task/create": { - "post": { - "summary": "Create task", - "description": "Create a new task configuration", - "operationId": "taskCreate", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "taskFields": { - "$ref": "#/components/schemas/TaskDefinitionDTO" - }, - "startTask": { - "type": "boolean", - "description": "Whether to start the task immediately after creation", - "nullable": true - } - }, - "required": ["taskFields"] - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "$ref": "#/components/schemas/TaskListDTO" - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error creating task", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "$ref": "#/components/schemas/TaskError" - } - } - } - ] - } - } - } - } - } - } - }, - "/task/update": { - "post": { - "summary": "Update task", - "description": "Update an existing task configuration", - "operationId": "taskUpdate", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "taskId": { - "type": "string", - "description": "ID of the task to update" - }, - "taskFields": { - "type": "object", - "description": "Fields to update in the task", - "properties": { - "name": { - "type": "string", - "description": "Name of the task", - "nullable": true - }, - "command": { - "type": "string", - "description": "Command to run", - "nullable": true - }, - "runAtStart": { - "type": "boolean", - "description": "Whether to run the task at sandbox start", - "nullable": true - }, - "preview": { - "type": "object", - "properties": { - "port": { - "type": "number", - "description": "Port to use for previewing the task", - "nullable": true - }, - "pr-link": { - "type": "string", - "enum": ["direct", "redirect", "devtool"], - "description": "Type of PR link to use", - "nullable": true - } - }, - "nullable": true - } - } - } - }, - "required": ["taskId", "taskFields"] - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "$ref": "#/components/schemas/TaskDTO" - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error updating task", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "$ref": "#/components/schemas/TaskError" - } - } - } - ] - } - } - } - } - } - } - }, - "/task/saveToConfig": { - "post": { - "summary": "Save task to config", - "description": "Save a runtime task to the configuration file", - "operationId": "taskSaveToConfig", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "taskId": { - "type": "string", - "description": "ID of the task to save to config" - } - }, - "required": ["taskId"] - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "$ref": "#/components/schemas/TaskDTO" - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error saving task to config", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "$ref": "#/components/schemas/TaskError" - } - } - } - ] - } - } - } - } - } - } - }, - "/task/generateConfig": { - "post": { - "summary": "Generate task config", - "description": "Generate a configuration file from current tasks", - "operationId": "taskGenerateConfig", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": {} - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "type": "null" - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error generating config", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "$ref": "#/components/schemas/TaskError" - } - } - } - ] - } - } - } - } - } - } - }, - "/task/createSetupTasks": { - "post": { - "summary": "Create setup tasks", - "description": "Create tasks that run during sandbox setup", - "operationId": "taskCreateSetupTasks", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "tasks": { - "type": "array", - "items": { - "$ref": "#/components/schemas/TaskDefinitionDTO" - }, - "description": "Setup tasks to create" - } - }, - "required": ["tasks"] - } - } - } - }, - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/SuccessResponse" - }, - { - "type": "object", - "properties": { - "result": { - "type": "null" - } - } - } - ] - } - } - } - }, - "400": { - "description": "Error creating setup tasks", - "content": { - "application/json": { - "schema": { - "allOf": [ - { - "$ref": "#/components/schemas/ErrorResponse" - }, - { - "type": "object", - "properties": { - "error": { - "$ref": "#/components/schemas/TaskError" - } - } - } - ] - } - } - } - } - } - } - } - }, - "components": { - "schemas": { - "SuccessResponse": { - "type": "object", - "properties": { - "status": { - "type": "number", - "enum": [0], - "description": "Status code for successful operations" - }, - "result": { - "type": "object", - "description": "Result payload for the operation" - } - }, - "required": ["status", "result"] - }, - "ErrorResponse": { - "type": "object", - "properties": { - "status": { - "type": "number", - "enum": [1], - "description": "Status code for error operations" - }, - "error": { - "type": "object", - "description": "Error details" - } - }, - "required": ["status", "error"] - }, - "CommonError": { - "type": "object", - "properties": { - "code": { - "type": "number", - "description": "Error code" - }, - "message": { - "type": "string", - "description": "Error message" - }, - "data": { - "type": "object", - "description": "Additional error data", - "nullable": true - } - }, - "required": ["code"] - }, - "TaskError": { - "oneOf": [ - { - "type": "object", - "properties": { - "code": { - "type": "number", - "enum": [600], - "description": "CONFIG_FILE_ALREADY_EXISTS error code" - }, - "message": { - "type": "string", - "description": "Error message" - } - }, - "required": ["code", "message"] - }, - { - "type": "object", - "properties": { - "code": { - "type": "number", - "enum": [601], - "description": "TASK_NOT_FOUND error code" - }, - "message": { - "type": "string", - "description": "Error message" - } - }, - "required": ["code", "message"] - }, - { - "type": "object", - "properties": { - "code": { - "type": "number", - "enum": [602], - "description": "COMMAND_ALREADY_CONFIGURED error code" - }, - "message": { - "type": "string", - "description": "Error message" - } - }, - "required": ["code", "message"] - }, - { - "$ref": "#/components/schemas/CommonError" - } - ], - "discriminator": { - "propertyName": "code" - } - }, - "TaskDefinitionDTO": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Name of the task" - }, - "command": { - "type": "string", - "description": "Command to run for the task" - }, - "runAtStart": { - "type": "boolean", - "description": "Whether the task should run when the sandbox starts", - "nullable": true - }, - "preview": { - "type": "object", - "properties": { - "port": { - "type": "number", - "description": "Port to preview from this task", - "nullable": true - }, - "pr-link": { - "type": "string", - "enum": ["direct", "redirect", "devtool"], - "description": "Type of PR link to use", - "nullable": true - } - }, - "nullable": true - } - }, - "required": ["name", "command"] - }, - "CommandShellDTO": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "ID of the shell command" - }, - "command": { - "type": "string", - "description": "Command being executed" - }, - "status": { - "type": "string", - "enum": ["initializing", "running", "stopped", "error"], - "description": "Current status of the shell command" - }, - "output": { - "type": "string", - "description": "Current output of the command" - } - }, - "required": ["id", "command", "status", "output"] - }, - "Port": { - "type": "object", - "properties": { - "port": { - "type": "number", - "description": "Port number" - }, - "hostname": { - "type": "string", - "description": "Hostname the port is bound to" - }, - "status": { - "type": "string", - "enum": ["open", "closed"], - "description": "Current status of the port" - }, - "taskId": { - "type": "string", - "description": "ID of the task that opened this port", - "nullable": true - } - }, - "required": ["port", "hostname", "status"] - }, - "TaskDTO": { - "allOf": [ - { - "$ref": "#/components/schemas/TaskDefinitionDTO" - }, - { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "Unique ID of the task" - }, - "unconfigured": { - "type": "boolean", - "description": "Whether this task is unconfigured (not saved in config)", - "nullable": true - }, - "shell": { - "type": "object", - "nullable": true, - "allOf": [ - { - "$ref": "#/components/schemas/CommandShellDTO" - } - ] - }, - "ports": { - "type": "array", - "items": { - "$ref": "#/components/schemas/Port" - }, - "description": "Ports opened by this task" - } - }, - "required": ["id", "shell", "ports"] - } - ] - }, - "TaskListDTO": { - "type": "object", - "properties": { - "tasks": { - "type": "object", - "additionalProperties": { - "$ref": "#/components/schemas/TaskDTO" - }, - "description": "Map of task IDs to task objects" - }, - "setupTasks": { - "type": "array", - "items": { - "$ref": "#/components/schemas/TaskDefinitionDTO" - }, - "description": "Tasks that run during sandbox setup" - }, - "validationErrors": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Validation errors in the task configuration" - } - }, - "required": ["tasks", "setupTasks", "validationErrors"] - } - } - } -} diff --git a/openapi.json b/openapi.json index 5890be8..07c5176 100644 --- a/openapi.json +++ b/openapi.json @@ -219,6 +219,10 @@ "id": { "type": "string" }, "is_frozen": { "type": "boolean" }, "privacy": { "type": "integer" }, + "settings": { + "properties": { "use_pint": { "type": "boolean" } }, + "type": "object" + }, "tags": { "items": { "type": "string" }, "type": "array" }, "title": { "nullable": true, "type": "string" }, "updated_at": { "format": "date-time", "type": "string" } @@ -229,7 +233,8 @@ "is_frozen", "created_at", "updated_at", - "tags" + "tags", + "settings" ], "title": "Sandbox", "type": "object" @@ -932,6 +937,7 @@ "pitcher_url": { "type": "string" }, "pitcher_version": { "type": "string" }, "reconnect_token": { "type": "string" }, + "use_pint": { "type": "boolean" }, "user_workspace_path": { "type": "string" }, "workspace_path": { "type": "string" } }, @@ -945,6 +951,7 @@ "pitcher_url", "pitcher_version", "reconnect_token", + "use_pint", "user_workspace_path", "workspace_path" ], @@ -1542,6 +1549,7 @@ "pitcher_url": { "type": "string" }, "pitcher_version": { "type": "string" }, "reconnect_token": { "type": "string" }, + "use_pint": { "type": "boolean" }, "user_workspace_path": { "type": "string" }, "workspace_path": { "type": "string" } }, @@ -1555,6 +1563,7 @@ "pitcher_url", "pitcher_version", "reconnect_token", + "use_pint", "user_workspace_path", "workspace_path" ], @@ -2671,6 +2680,6 @@ } }, "security": [], - "servers": [{ "url": "https://api.codesandbox.stream", "variables": {} }], + "servers": [{ "url": "https://api.codesandbox.io", "variables": {} }], "tags": [] } diff --git a/package-lock.json b/package-lock.json index e87d29d..05cc710 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "2.3.0", "license": "MIT", "dependencies": { - "@hey-api/client-fetch": "^0.7.3", + "@hey-api/client-fetch": "^0.13.1", "@inkjs/ui": "^2.0.0", "@opentelemetry/api": "^1.9.0", "@xterm/addon-serialize": "^0.13.0", @@ -31,7 +31,7 @@ "csb": "dist/bin/codesandbox.mjs" }, "devDependencies": { - "@hey-api/openapi-ts": "^0.63.2", + "@hey-api/openapi-ts": "^0.84.4", "@msgpack/msgpack": "^2.7.1", "@parcel/watcher": "^2.5.1", "@tanstack/react-query": "^5.76.1", @@ -537,24 +537,43 @@ } }, "node_modules/@hey-api/client-fetch": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/@hey-api/client-fetch/-/client-fetch-0.7.3.tgz", - "integrity": "sha512-nysIXMag9nr5ENy+47G0AYsegdT7vT6S4KLfY7NVgM6HsyZ0DrhCZvz5nP70M16x9i860SrnXhjpcuHx0g5sDQ==", + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/@hey-api/client-fetch/-/client-fetch-0.13.1.tgz", + "integrity": "sha512-29jBRYNdxVGlx5oewFgOrkulZckpIpBIRHth3uHFn1PrL2ucMy52FvWOY3U3dVx2go1Z3kUmMi6lr07iOpUqqA==", + "deprecated": "Starting with v0.73.0, this package is bundled directly inside @hey-api/openapi-ts.", "license": "MIT", "funding": { "url": "https://github.com/sponsors/hey-api" + }, + "peerDependencies": { + "@hey-api/openapi-ts": "< 2" + } + }, + "node_modules/@hey-api/codegen-core": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@hey-api/codegen-core/-/codegen-core-0.2.0.tgz", + "integrity": "sha512-c7VjBy/8ed0EVLNgaeS9Xxams1Tuv/WK/b4xXH3Qr4wjzYeJUtxOcoP8YdwNLavqKP8pGiuctjX2Z1Pwc4jMgQ==", + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=22.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/hey-api" + }, + "peerDependencies": { + "typescript": ">=5.5.3" } }, "node_modules/@hey-api/json-schema-ref-parser": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@hey-api/json-schema-ref-parser/-/json-schema-ref-parser-1.0.1.tgz", - "integrity": "sha512-dBt0A7op9kf4BcK++x6HBYDmvCvnJUZEGe5QytghPFHnMXPyKwDKomwL/v5e9ERk6E0e1GzL/e/y6pWUso9zrQ==", - "dev": true, + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@hey-api/json-schema-ref-parser/-/json-schema-ref-parser-1.1.0.tgz", + "integrity": "sha512-+5eg9pgAAM9oSqJQuUtfTKbLz8yieFKN91myyXiLnprqFj8ROfxUKJLr9DKq/hGKyybKT1WxFSetDqCFm80pCA==", "license": "MIT", "dependencies": { "@jsdevtools/ono": "^7.1.3", "@types/json-schema": "^7.0.15", - "js-yaml": "^4.1.0" + "js-yaml": "^4.1.0", + "lodash": "^4.17.21" }, "engines": { "node": ">= 16" @@ -564,28 +583,44 @@ } }, "node_modules/@hey-api/openapi-ts": { - "version": "0.63.2", - "resolved": "https://registry.npmjs.org/@hey-api/openapi-ts/-/openapi-ts-0.63.2.tgz", - "integrity": "sha512-HC5fR3+07P1AvDYrcZv0kbnhWogvMFot848PfpS3Gbncm47mDdO/uhNGIr2RF9CaRIvJNSJvAf1jL3XAQZ18RA==", - "dev": true, + "version": "0.84.4", + "resolved": "https://registry.npmjs.org/@hey-api/openapi-ts/-/openapi-ts-0.84.4.tgz", + "integrity": "sha512-yKR/KFQVCoAbvkpRM13uYxMUyZl3whwEY8Nj4X9Yra18dWpfzmdTyTxP8KtYzdfNMC9xIw4TAgK5+1FJctENBw==", "license": "MIT", "dependencies": { - "@hey-api/json-schema-ref-parser": "1.0.1", + "@hey-api/codegen-core": "^0.2.0", + "@hey-api/json-schema-ref-parser": "1.1.0", + "ansi-colors": "4.1.3", "c12": "2.0.1", + "color-support": "1.1.3", "commander": "13.0.0", - "handlebars": "4.7.8" + "handlebars": "4.7.8", + "open": "10.1.2", + "semver": "7.7.2" }, "bin": { "openapi-ts": "bin/index.cjs" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=22.11.0" + "node": "^18.18.0 || ^20.9.0 || >=22.10.0" }, "funding": { "url": "https://github.com/sponsors/hey-api" }, "peerDependencies": { - "typescript": "^5.5.3" + "typescript": ">=5.5.3" + } + }, + "node_modules/@hey-api/openapi-ts/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, "node_modules/@inkjs/ui": { @@ -672,7 +707,6 @@ "version": "7.1.3", "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz", "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==", - "dev": true, "license": "MIT" }, "node_modules/@msgpack/msgpack": { @@ -2083,7 +2117,6 @@ "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, "license": "MIT" }, "node_modules/@types/mysql": { @@ -2343,7 +2376,6 @@ "version": "8.14.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", - "devOptional": true, "license": "MIT", "bin": { "acorn": "bin/acorn" @@ -2362,6 +2394,15 @@ "acorn": "^8" } }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/ansi-escapes": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-6.2.1.tgz", @@ -2411,7 +2452,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, "license": "Python-2.0" }, "node_modules/asn1.js": { @@ -2709,11 +2749,25 @@ "node": ">=6.14.2" } }, + "node_modules/bundle-name": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", + "license": "MIT", + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/c12": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/c12/-/c12-2.0.1.tgz", "integrity": "sha512-Z4JgsKXHG37C6PYUtIxCfLJZvo6FyhHJoClwwb9ftUkLpPSkuYqn6Tr+vnaN8hymm0kIbcg6Ey3kv/Q71k5w/A==", - "dev": true, "license": "MIT", "dependencies": { "chokidar": "^4.0.1", @@ -2857,7 +2911,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "dev": true, "license": "MIT", "dependencies": { "readdirp": "^4.0.1" @@ -2873,7 +2926,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "dev": true, "license": "ISC", "engines": { "node": ">=10" @@ -2897,7 +2949,6 @@ "version": "0.1.6", "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz", "integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==", - "dev": true, "license": "MIT", "dependencies": { "consola": "^3.2.3" @@ -3140,11 +3191,19 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "license": "MIT" }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "license": "ISC", + "bin": { + "color-support": "bin.js" + } + }, "node_modules/commander": { "version": "13.0.0", "resolved": "https://registry.npmjs.org/commander/-/commander-13.0.0.tgz", "integrity": "sha512-oPYleIY8wmTVzkvQq10AEok6YcTC4sRUBl8F9gVuwchGVUCTbl/vhLTaQqutuuySYOsu8YTgV+OxKc/8Yvx+mQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=18" @@ -3154,14 +3213,12 @@ "version": "0.1.8", "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", - "dev": true, "license": "MIT" }, "node_modules/consola": { "version": "3.4.2", "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", - "dev": true, "license": "MIT", "engines": { "node": "^14.18.0 || >=16.10.0" @@ -3324,6 +3381,34 @@ "node": ">=0.10.0" } }, + "node_modules/default-browser": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz", + "integrity": "sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==", + "license": "MIT", + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.0.tgz", + "integrity": "sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", @@ -3341,11 +3426,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/defu": { "version": "6.1.4", "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz", "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", - "dev": true, "license": "MIT" }, "node_modules/des.js": { @@ -3363,7 +3459,6 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz", "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==", - "dev": true, "license": "MIT" }, "node_modules/detect-libc": { @@ -3401,7 +3496,6 @@ "version": "16.5.0", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz", "integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==", - "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=12" @@ -3750,7 +3844,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "dev": true, "license": "ISC", "dependencies": { "minipass": "^3.0.0" @@ -3763,7 +3856,6 @@ "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, "license": "ISC", "dependencies": { "yallist": "^4.0.0" @@ -3858,7 +3950,6 @@ "version": "1.2.5", "resolved": "https://registry.npmjs.org/giget/-/giget-1.2.5.tgz", "integrity": "sha512-r1ekGw/Bgpi3HLV3h1MRBIlSAdHoIMklpaQ3OQLFcRw9PwAj2rqigvIbg+dBUI51OxVI2jsEtDywDBjSiuf7Ug==", - "dev": true, "license": "MIT", "dependencies": { "citty": "^0.1.6", @@ -3877,7 +3968,6 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "dev": true, "license": "MIT" }, "node_modules/gl-matrix": { @@ -3926,7 +4016,6 @@ "version": "4.7.8", "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", - "dev": true, "license": "MIT", "dependencies": { "minimist": "^1.2.5", @@ -4323,6 +4412,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -4386,6 +4490,24 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "license": "MIT", + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-interactive": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", @@ -4460,6 +4582,21 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-wsl": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", + "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", @@ -4516,7 +4653,6 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", - "dev": true, "license": "MIT", "bin": { "jiti": "lib/jiti-cli.mjs" @@ -4532,7 +4668,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, "license": "MIT", "dependencies": { "argparse": "^2.0.1" @@ -4815,7 +4950,6 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -4835,7 +4969,6 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "dev": true, "license": "MIT", "dependencies": { "minipass": "^3.0.0", @@ -4849,7 +4982,6 @@ "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, "license": "ISC", "dependencies": { "yallist": "^4.0.0" @@ -4862,7 +4994,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, "license": "MIT", "bin": { "mkdirp": "bin/cmd.js" @@ -4875,7 +5006,6 @@ "version": "1.7.4", "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.4.tgz", "integrity": "sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==", - "dev": true, "license": "MIT", "dependencies": { "acorn": "^8.14.0", @@ -4888,7 +5018,6 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "dev": true, "license": "MIT" }, "node_modules/module-details-from-path": { @@ -4928,7 +5057,6 @@ "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true, "license": "MIT" }, "node_modules/nock": { @@ -4965,7 +5093,6 @@ "version": "1.6.6", "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.6.tgz", "integrity": "sha512-8Mc2HhqPdlIfedsuZoc3yioPuzp6b+L5jRCRY1QzuWZh2EGJVQrGppC6V6cF0bLdbW0+O2YpqCA25aF/1lvipQ==", - "dev": true, "license": "MIT" }, "node_modules/node-gyp-build": { @@ -4996,7 +5123,6 @@ "version": "0.5.4", "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.5.4.tgz", "integrity": "sha512-X0SNNrZiGU8/e/zAB7sCTtdxWTMSIO73q+xuKgglm2Yvzwlo8UoC5FNySQFCvl84uPaeADkqHUZUkWy4aH4xOA==", - "dev": true, "license": "MIT", "dependencies": { "citty": "^0.1.6", @@ -5017,14 +5143,12 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "dev": true, "license": "MIT" }, "node_modules/ohash": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/ohash/-/ohash-1.1.6.tgz", "integrity": "sha512-TBu7PtV8YkAZn0tSxobKY2n2aAQva936lhRrj6957aDaCf9IEtqsKbgMzXE/F/sjqYOwmrukeORHNLe5glk7Cg==", - "dev": true, "license": "MIT" }, "node_modules/onetime": { @@ -5042,6 +5166,24 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/open": { + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/open/-/open-10.1.2.tgz", + "integrity": "sha512-cxN6aIDPz6rm8hbebcP7vrQNhvRcveZoJU72Y7vskh4oIm+BZwBECnx5nTmrlres1Qapvx27Qo1Auukpf8PKXw==", + "license": "MIT", + "dependencies": { + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "is-wsl": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/optimist": { "version": "0.3.7", "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.3.7.tgz", @@ -5224,7 +5366,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", - "dev": true, "license": "MIT" }, "node_modules/pathval": { @@ -5258,7 +5399,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", - "dev": true, "license": "MIT" }, "node_modules/pg-int8": { @@ -5339,7 +5479,6 @@ "version": "1.3.1", "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", - "dev": true, "license": "MIT", "dependencies": { "confbox": "^0.1.8", @@ -5351,7 +5490,6 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "dev": true, "license": "MIT" }, "node_modules/png-js": { @@ -5529,7 +5667,6 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/rc9/-/rc9-2.1.2.tgz", "integrity": "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==", - "dev": true, "license": "MIT", "dependencies": { "defu": "^6.1.4", @@ -5593,7 +5730,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 14.18.0" @@ -5750,6 +5886,18 @@ "fsevents": "~2.3.2" } }, + "node_modules/run-applescript": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz", + "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -5940,7 +6088,6 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" @@ -6238,7 +6385,6 @@ "version": "6.2.1", "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", - "dev": true, "license": "ISC", "dependencies": { "chownr": "^2.0.0", @@ -6256,7 +6402,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "dev": true, "license": "ISC", "engines": { "node": ">=8" @@ -6278,7 +6423,6 @@ "version": "0.3.2", "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", - "dev": true, "license": "MIT" }, "node_modules/tinyglobby": { @@ -6383,7 +6527,6 @@ "version": "5.8.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", - "dev": true, "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", @@ -6397,14 +6540,12 @@ "version": "1.6.1", "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.1.tgz", "integrity": "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==", - "dev": true, "license": "MIT" }, "node_modules/uglify-js": { "version": "3.19.3", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", - "dev": true, "license": "BSD-2-Clause", "optional": true, "bin": { @@ -6780,7 +6921,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", - "dev": true, "license": "MIT" }, "node_modules/wrap-ansi": { @@ -6972,7 +7112,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true, "license": "ISC" }, "node_modules/yargs": { diff --git a/package.json b/package.json index 4f9ea34..617338b 100644 --- a/package.json +++ b/package.json @@ -42,16 +42,9 @@ "build:esbuild": "node esbuild.cjs", "build:cjs:types": "tsc -p ./tsconfig.build-cjs.json --emitDeclarationOnly", "build:esm:types": "tsc -p ./tsconfig.build-esm.json --emitDeclarationOnly", - "build-openapi": "rimraf src/api-clients && curl -o openapi.json https://api.codesandbox.io/meta/openapi && npx prettier --write ./openapi.json && node_modules/.bin/openapi-ts -i ./openapi.json -o src/api-clients/client -c @hey-api/client-fetch && npm run build-openapi-rest", + "build-openapi": "rimraf src/api-clients && curl -o openapi.json https://api.codesandbox.io/meta/openapi && npx prettier --write ./openapi.json && node_modules/.bin/openapi-ts -i ./openapi.json -o src/api-clients/client -c @hey-api/client-fetch && npm run build-openapi-pint", "build-openapi:staging": "rimraf src/api-clients && curl -o openapi.json https://api.codesandbox.stream/meta/openapi && npx prettier --write ./openapi.json && node_modules/.bin/openapi-ts -i ./openapi.json -o src/api-clients/client -c @hey-api/client-fetch && npm run build-openapi-rest", - "build-openapi-rest": "npm run build-openapi-rest-fs && npm run build-openapi-rest-task && npm run build-openapi-rest-container && npm run build-openapi-rest-git && npm run build-openapi-rest-setup && npm run build-openapi-rest-shell && npm run build-openapi-rest-system", - "build-openapi-rest-container": "node_modules/.bin/openapi-ts -i ./openapi-sandbox-container.json -o src/api-clients/client-rest-container -c @hey-api/client-fetch", - "build-openapi-rest-fs": "node_modules/.bin/openapi-ts -i ./openapi-sandbox-fs.json -o src/api-clients/client-rest-fs -c @hey-api/client-fetch", - "build-openapi-rest-git": "node_modules/.bin/openapi-ts -i ./openapi-sandbox-git.json -o src/api-clients/client-rest-git -c @hey-api/client-fetch", - "build-openapi-rest-setup": "node_modules/.bin/openapi-ts -i ./openapi-sandbox-setup.json -o src/api-clients/client-rest-setup -c @hey-api/client-fetch", - "build-openapi-rest-shell": "node_modules/.bin/openapi-ts -i ./openapi-sandbox-shell.json -o src/api-clients/client-rest-shell -c @hey-api/client-fetch", - "build-openapi-rest-system": "node_modules/.bin/openapi-ts -i ./openapi-sandbox-system.json -o src/api-clients/client-rest-system -c @hey-api/client-fetch", - "build-openapi-rest-task": "node_modules/.bin/openapi-ts -i ./openapi-sandbox-task.json -o src/api-clients/client-rest-task -c @hey-api/client-fetch", + "build-openapi-pint": "node_modules/.bin/openapi-ts -i ./pint-openapi-bundled.json -o src/api-clients/pint -c @hey-api/client-fetch", "clean": "rimraf ./dist", "test": "vitest", "typecheck": "tsc --noEmit", @@ -70,7 +63,7 @@ "README.md" ], "devDependencies": { - "@hey-api/openapi-ts": "^0.63.2", + "@hey-api/openapi-ts": "^0.84.4", "@msgpack/msgpack": "^2.7.1", "@parcel/watcher": "^2.5.1", "@tanstack/react-query": "^5.76.1", @@ -98,7 +91,7 @@ "why-is-node-running": "^2.3.0" }, "dependencies": { - "@hey-api/client-fetch": "^0.7.3", + "@hey-api/client-fetch": "^0.13.1", "@inkjs/ui": "^2.0.0", "@opentelemetry/api": "^1.9.0", "@xterm/addon-serialize": "^0.13.0", diff --git a/pint-openapi-bundled.json b/pint-openapi-bundled.json index 0f7faaf..6d8a5ce 100644 --- a/pint-openapi-bundled.json +++ b/pint-openapi-bundled.json @@ -37,9 +37,7 @@ "/api/v1/files/{path}": { "post": { "summary": "Create a file", - "tags": [ - "files" - ], + "tags": ["files"], "description": "Creates a new file at the specified path with optional content.", "operationId": "createFile", "security": [ @@ -124,9 +122,7 @@ }, "get": { "summary": "Read file content", - "tags": [ - "files" - ], + "tags": ["files"], "description": "Reads the content of a file at the specified path.", "operationId": "readFile", "security": [ @@ -201,9 +197,7 @@ }, "patch": { "summary": "Perform file actions", - "tags": [ - "files" - ], + "tags": ["files"], "description": "Performs actions on files (e.g., move operations).", "operationId": "performFileAction", "security": [ @@ -289,9 +283,7 @@ }, "delete": { "summary": "Delete a file", - "tags": [ - "files" - ], + "tags": ["files"], "description": "Deletes a file at the specified path.", "operationId": "deleteFile", "security": [ @@ -378,9 +370,7 @@ "/api/v1/directories/{path}": { "post": { "summary": "Create a directory", - "tags": [ - "directories" - ], + "tags": ["directories"], "description": "Creates a new directory at the specified path.", "operationId": "createDirectory", "security": [ @@ -455,9 +445,7 @@ }, "get": { "summary": "List directory contents", - "tags": [ - "directories" - ], + "tags": ["directories"], "description": "Lists the contents of a directory at the specified path.", "operationId": "listDirectory", "security": [ @@ -532,9 +520,7 @@ }, "delete": { "summary": "Delete a directory", - "tags": [ - "directories" - ], + "tags": ["directories"], "description": "Deletes a directory at the specified path.", "operationId": "deleteDirectory", "security": [ @@ -621,9 +607,7 @@ "/api/v1/execs": { "post": { "summary": "Create a new exec", - "tags": [ - "execs" - ], + "tags": ["execs"], "description": "Creates a new exec with specified command and arguments.", "operationId": "createExec", "security": [ @@ -697,9 +681,7 @@ }, "get": { "summary": "List all execs", - "tags": [ - "execs" - ], + "tags": ["execs"], "description": "Returns a list of all active execs.", "operationId": "listExecs", "security": [ @@ -750,9 +732,7 @@ "/api/v1/execs/{id}": { "get": { "summary": "Get exec by ID", - "tags": [ - "execs" - ], + "tags": ["execs"], "description": "Retrieves a specific exec by its ID.", "operationId": "getExec", "security": [ @@ -832,9 +812,7 @@ }, "put": { "summary": "Update exec", - "tags": [ - "execs" - ], + "tags": ["execs"], "description": "Updates exec status (e.g., start a stopped exec).", "operationId": "updateExec", "security": [ @@ -939,9 +917,7 @@ }, "delete": { "summary": "Delete Exec", - "tags": [ - "execs" - ], + "tags": ["execs"], "description": "Deletes a exec and execs its process.", "operationId": "deleteExec", "security": [ @@ -1017,9 +993,7 @@ "/api/v1/execs/{id}/io": { "get": { "summary": "Get Exec output", - "tags": [ - "execs" - ], + "tags": ["execs"], "description": "Retrieves the plain text output from a exec's buffer.", "operationId": "getExecOutput", "security": [ @@ -1101,9 +1075,7 @@ }, "post": { "summary": "exec exec stdin", - "tags": [ - "execs" - ], + "tags": ["execs"], "description": "exec exec command (e.g., npm install).", "operationId": "execExecStdin", "security": [ @@ -1200,9 +1172,7 @@ "/ws/v1/execs/{id}": { "get": { "summary": "Connect to exec via WebSocket", - "tags": [ - "execs" - ], + "tags": ["execs"], "description": "Establishes a WebSocket connection for real-time exec interaction.\n\nAuthentication can be provided via:\n- Authorization header: `Authorization: Bearer `\n- Query parameter: `?token=`\n\nPermissions:\n- Admin users: Can send input and receive output\n- Readonly users: Can only receive output\n", "operationId": "connectToExecWebSocket", "security": [ @@ -1284,9 +1254,7 @@ "/api/v1/tasks": { "get": { "summary": "List all tasks", - "tags": [ - "tasks" - ], + "tags": ["tasks"], "description": "Lists all configured tasks from .codesandbox/tasks.json with their current status.", "operationId": "listTasks", "security": [ @@ -1331,9 +1299,7 @@ "/api/v1/tasks/{id}": { "get": { "summary": "Get task by ID", - "tags": [ - "tasks" - ], + "tags": ["tasks"], "description": "Retrieves a specific task by its ID with current status and configuration.", "operationId": "getTask", "security": [ @@ -1409,9 +1375,7 @@ "/api/v1/tasks/{id}/actions": { "patch": { "summary": "Execute task action", - "tags": [ - "tasks" - ], + "tags": ["tasks"], "description": "Executes an action on a specific task (start, stop, or restart).", "operationId": "executeTaskAction", "security": [ @@ -1516,9 +1480,7 @@ "/api/v1/setup-tasks": { "get": { "summary": "List setup tasks", - "tags": [ - "tasks" - ], + "tags": ["tasks"], "description": "Lists all setup tasks with their execution status. Setup tasks are auto-executed during server start.", "operationId": "listSetupTasks", "security": [ @@ -1563,9 +1525,7 @@ "/api/v1/ports": { "get": { "summary": "List open ports", - "tags": [ - "ports" - ], + "tags": ["ports"], "description": "Lists all open TCP ports on the system, excluding ignored ports configured in the server.", "operationId": "listPorts", "security": [ @@ -1641,9 +1601,7 @@ "/api/v1/ports/stream": { "get": { "summary": "List open ports using Server-Sent Events (SSE)", - "tags": [ - "ports" - ], + "tags": ["ports"], "description": "Lists all open TCP ports on the system AND LISTEN TO THE CHANGES, excluding ignored ports configured in the server.", "operationId": "ListPortsSSE", "security": [ @@ -1655,32 +1613,6 @@ "200": { "description": "Open ports retrieved successfully", "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PortsListResponse" - }, - "examples": { - "default": { - "summary": "Example ports response", - "value": { - "ports": [ - { - "port": 8080, - "address": "0.0.0.0" - }, - { - "port": 3000, - "address": "127.0.0.1" - }, - { - "port": 5432, - "address": "::1" - } - ] - } - } - } - }, "text/event-stream": { "schema": { "type": "string", @@ -1747,10 +1679,7 @@ "type": "string" } }, - "required": [ - "code", - "message" - ] + "required": ["code", "message"] }, "FileReadResponse": { "type": "object", @@ -1764,10 +1693,7 @@ "description": "File content" } }, - "required": [ - "path", - "content" - ] + "required": ["path", "content"] }, "FileCreateRequest": { "type": "object", @@ -1790,19 +1716,14 @@ "description": "File or directory path" } }, - "required": [ - "message", - "path" - ] + "required": ["message", "path"] }, "FileActionRequest": { "type": "object", "properties": { "action": { "type": "string", - "enum": [ - "move" - ], + "enum": ["move"], "description": "Type of action to perform on the file" }, "destination": { @@ -1810,10 +1731,7 @@ "description": "Destination path for move operation" } }, - "required": [ - "action", - "destination" - ] + "required": ["action", "destination"] }, "FileActionResponse": { "type": "object", @@ -1831,11 +1749,7 @@ "description": "Destination path" } }, - "required": [ - "message", - "from", - "to" - ] + "required": ["message", "from", "to"] }, "FileInfo": { "type": "object", @@ -1862,13 +1776,7 @@ "description": "Last modification time" } }, - "required": [ - "name", - "path", - "isDir", - "size", - "modTime" - ] + "required": ["name", "path", "isDir", "size", "modTime"] }, "DirectoryListResponse": { "type": "object", @@ -1885,10 +1793,7 @@ "description": "List of files and directories" } }, - "required": [ - "path", - "files" - ] + "required": ["path", "files"] }, "ExecItem": { "type": "object", @@ -1917,13 +1822,7 @@ "description": "Process ID of the exec" } }, - "required": [ - "id", - "command", - "args", - "status", - "pid" - ] + "required": ["id", "command", "args", "status", "pid"] }, "ExecListResponse": { "type": "object", @@ -1936,9 +1835,7 @@ "description": "List of execs" } }, - "required": [ - "execs" - ] + "required": ["execs"] }, "CreateExecRequest": { "type": "object", @@ -1963,25 +1860,18 @@ "description": "Whether to start interactive shell session or not (defaults to false)" } }, - "required": [ - "command", - "args" - ] + "required": ["command", "args"] }, "UpdateExecRequest": { "type": "object", "properties": { "status": { "type": "string", - "enum": [ - "running" - ], + "enum": ["running"], "description": "Status to set for the exec (currently only 'running' is supported)" } }, - "required": [ - "status" - ] + "required": ["status"] }, "ExecDeleteResponse": { "type": "object", @@ -1991,9 +1881,7 @@ "description": "Deletion confirmation message" } }, - "required": [ - "message" - ] + "required": ["message"] }, "ExecStdout": { "type": "object", @@ -2001,10 +1889,7 @@ "type": { "type": "string", "description": "Type of the exec output", - "enum": [ - "stdout", - "stderr" - ] + "enum": ["stdout", "stderr"] }, "output": { "type": "string", @@ -2021,11 +1906,7 @@ "description": "Timestamp of when the output was generated" } }, - "required": [ - "type", - "output", - "sequence" - ] + "required": ["type", "output", "sequence"] }, "ExecStdin": { "type": "object", @@ -2033,31 +1914,18 @@ "type": { "type": "string", "description": "Type of the exec input", - "enum": [ - "stdin", - "resize" - ] + "enum": ["stdin", "resize"] }, "input": { "type": "string", "description": "Data associated with the exec input" } }, - "required": [ - "type", - "input" - ] + "required": ["type", "input"] }, "TaskStatus": { "type": "string", - "enum": [ - "RUNNING", - "FINISHED", - "ERROR", - "KILLED", - "RESTARTING", - "IDLE" - ] + "enum": ["RUNNING", "FINISHED", "ERROR", "KILLED", "RESTARTING", "IDLE"] }, "TaskBase": { "type": "object", @@ -2080,12 +1948,7 @@ "example": "2017-08-21T17:32:28Z" } }, - "required": [ - "status", - "execId", - "startTime", - "endTime" - ], + "required": ["status", "execId", "startTime", "endTime"], "description": "Base schema for a task item, containing common fields shared across different task types.", "title": "TaskBase" }, @@ -2108,12 +1971,7 @@ "type": "boolean" } }, - "required": [ - "files", - "clone", - "resume", - "branch" - ] + "required": ["files", "clone", "resume", "branch"] }, "TaskPreview": { "type": "object", @@ -2122,9 +1980,7 @@ "type": "integer" } }, - "required": [ - "port" - ] + "required": ["port"] }, "TaskConfig": { "type": "object", @@ -2145,13 +2001,7 @@ "$ref": "#/components/schemas/TaskPreview" } }, - "required": [ - "name", - "command", - "runAtStart", - "restartOn", - "preview" - ] + "required": ["name", "command", "runAtStart", "restartOn", "preview"] }, "TaskItem": { "allOf": [ @@ -2169,10 +2019,7 @@ "$ref": "#/components/schemas/TaskConfig" } }, - "required": [ - "id", - "config" - ] + "required": ["id", "config"] } ] }, @@ -2186,9 +2033,7 @@ } } }, - "required": [ - "tasks" - ] + "required": ["tasks"] }, "GetTaskResponse": { "type": "object", @@ -2197,17 +2042,11 @@ "$ref": "#/components/schemas/TaskItem" } }, - "required": [ - "task" - ] + "required": ["task"] }, "TaskActionType": { "type": "string", - "enum": [ - "start", - "stop", - "restart" - ], + "enum": ["start", "stop", "restart"], "description": "Type of action to execute on a task" }, "TaskActionResponse": { @@ -2235,12 +2074,7 @@ "description": "Action result message" } }, - "required": [ - "id", - "name", - "message", - "command" - ] + "required": ["id", "name", "message", "command"] } ], "description": "Schema for task action responses, containing details about the task and the action performed.", @@ -2263,11 +2097,7 @@ "description": "Setup task command" } }, - "required": [ - "name", - "command", - "execId" - ] + "required": ["name", "command", "execId"] } ] }, @@ -2281,9 +2111,7 @@ } } }, - "required": [ - "setupTasks" - ] + "required": ["setupTasks"] }, "PortInfo": { "type": "object", @@ -2302,10 +2130,7 @@ "example": "0.0.0.0" } }, - "required": [ - "port", - "address" - ] + "required": ["port", "address"] }, "PortsListResponse": { "type": "object", @@ -2318,13 +2143,11 @@ "description": "List of open ports" } }, - "required": [ - "ports" - ] + "required": ["ports"] }, "Task": { "$ref": "#/components/schemas/TaskItem" } } } -} \ No newline at end of file +} diff --git a/src/API.ts b/src/API.ts index 42898a9..34360b6 100644 --- a/src/API.ts +++ b/src/API.ts @@ -1,6 +1,4 @@ -import type { Client, Config } from "@hey-api/client-fetch"; import { handleResponse, retryWithDelay, createApiClient } from "./utils/api"; -import { getInferredBaseUrl } from "./utils/constants"; import { metaInfo, workspaceCreate, @@ -56,7 +54,8 @@ import type { PreviewHostUpdateData, } from "./api-clients/client"; import { PitcherManagerResponse } from "./types"; - +import { Config } from "./api-clients/client/client"; +import { Client } from "./api-clients/pint/client"; export interface APIOptions { apiKey: string; diff --git a/src/AgentClient/AgentConnection.ts b/src/AgentClient/AgentConnection.ts index 8627ee5..c20dfb3 100644 --- a/src/AgentClient/AgentConnection.ts +++ b/src/AgentClient/AgentConnection.ts @@ -14,7 +14,7 @@ import type { import { PendingPitcherMessage } from "./PendingPitcherMessage"; import { createWebSocketClient, WebSocketClient } from "./WebSocketClient"; -import { IAgentClientState } from "./agent-client-interface"; +import { IAgentClientState } from "../agent-client-interface"; import { DEFAULT_SUBSCRIPTIONS } from "../types"; import { Emitter } from "../utils/event"; import { SliceList } from "../utils/sliceList"; diff --git a/src/AgentClient/index.ts b/src/AgentClient/index.ts index c6915b6..20dbadb 100644 --- a/src/AgentClient/index.ts +++ b/src/AgentClient/index.ts @@ -17,7 +17,7 @@ import { IAgentClientSystem, IAgentClientTasks, PickRawFsResult, -} from "./agent-client-interface"; +} from "../agent-client-interface"; import { AgentConnection } from "./AgentConnection"; import { Emitter, Event } from "../utils/event"; import { DEFAULT_SUBSCRIPTIONS, SandboxSession } from "../types"; diff --git a/src/PintClient/index.ts b/src/PintClient/index.ts new file mode 100644 index 0000000..cae0548 --- /dev/null +++ b/src/PintClient/index.ts @@ -0,0 +1,95 @@ +import { + IAgentClient, + IAgentClientPorts, + IAgentClientState, +} from "../agent-client-interface"; +import { Port } from "../pitcher-protocol/messages/port"; +import { listPorts, listPortsSse } from "../api-clients/pint"; +import { SandboxSession } from "../types"; +import { Emitter, EmitterSubscription, Event } from "../utils/event"; +import { Disposable } from "../utils/disposable"; +import { Client, createClient, createConfig } from "../api-clients/pint/client"; + +class PintPortsClient implements IAgentClientPorts { + private onPortsUpdatedEmitter = new EmitterSubscription((fire) => { + const abortController = new AbortController(); + + listPortsSse({ + signal: abortController.signal, + headers: { + headers: { Accept: "text/event-stream" }, + }, + }).then(async ({ stream }) => { + for await (const evt of stream) { + const evtWithoutDataPrefix = evt.substring(5); + + fire(JSON.parse(evtWithoutDataPrefix)); + } + }); + + return Disposable.create(() => { + abortController.abort(); + }); + }); + onPortsUpdated = this.onPortsUpdatedEmitter.event; + + constructor(private apiClient: Client, private sandboxId: string) {} + + async getPorts(): Promise { + const ports = await listPorts({ + client: this.apiClient, + }); + + return ( + ports.data?.ports.map((port) => ({ + port: port.port, + url: `https://${this.sandboxId}-${port.port}.csb.app`, + })) ?? [] + ); + } +} + +export class PintClient implements IAgentClient { + static async create(session: SandboxSession) { + return new PintClient(session); + } + + // Since there is no websocket connection or internal hibernation, the state + // will always be CONNECTED. No state change events will be triggered + readonly state = "CONNECTED"; + private onStateChangeEmitter = new Emitter(); + onStateChange = this.onStateChangeEmitter.event; + + sandboxId: string; + workspacePath: string; + isUpToDate: boolean; + + ports: IAgentClientPorts; + shells: any = null; // TODO: Implement + fs: any = null; // TODO: Implement + setup: any = null; // TODO: Implement + tasks: any = null; // TODO: Implement + system: any = null; // TODO: Implement + + constructor(session: SandboxSession) { + this.sandboxId = session.sandboxId; + this.workspacePath = session.workspacePath; + this.isUpToDate = true; + + const apiClient = createClient( + createConfig({ + baseUrl: session.pitcherURL, + headers: { + Authorization: `Bearer ${session.pitcherToken}`, + }, + }) + ); + + this.ports = new PintPortsClient(apiClient, this.sandboxId); + } + + ping(): void {} + async reconnect(): Promise {} + async disconnect(): Promise {} + dispose(): void {} +} diff --git a/src/SandboxClient/commands.ts b/src/SandboxClient/commands.ts index c2a0c9b..2b52abb 100644 --- a/src/SandboxClient/commands.ts +++ b/src/SandboxClient/commands.ts @@ -1,6 +1,6 @@ import { Disposable, DisposableStore } from "../utils/disposable"; import { Emitter } from "../utils/event"; -import { IAgentClient } from "../AgentClient/agent-client-interface"; +import { IAgentClient } from "../agent-client-interface"; import * as protocol from "../pitcher-protocol"; import { Barrier } from "../utils/barrier"; import { Tracer, SpanStatusCode } from "@opentelemetry/api"; @@ -186,7 +186,8 @@ export class SandboxCommands { return shells .filter( - (shell) => shell.shellType === "TERMINAL" && isCommandShell(shell) + (shell): shell is protocol.shell.CommandShellDTO => + shell.shellType === "TERMINAL" && isCommandShell(shell) ) .map( (shell) => diff --git a/src/SandboxClient/filesystem.ts b/src/SandboxClient/filesystem.ts index 3e1ac32..68440a5 100644 --- a/src/SandboxClient/filesystem.ts +++ b/src/SandboxClient/filesystem.ts @@ -1,4 +1,4 @@ -import { type IAgentClient } from "../AgentClient/agent-client-interface"; +import { type IAgentClient } from "../agent-client-interface"; import { Disposable } from "../utils/disposable"; import { Emitter, type Event } from "../utils/event"; diff --git a/src/SandboxClient/index.ts b/src/SandboxClient/index.ts index a8b0fc6..ca83b2f 100644 --- a/src/SandboxClient/index.ts +++ b/src/SandboxClient/index.ts @@ -10,7 +10,7 @@ import { Terminals } from "./terminals"; import { SandboxCommands } from "./commands"; import { HostToken } from "../HostTokens"; import { Hosts } from "./hosts"; -import { IAgentClient } from "../AgentClient/agent-client-interface"; +import { IAgentClient } from "../agent-client-interface"; import { setup, system } from "../pitcher-protocol"; import { Barrier } from "../utils/barrier"; import { AgentClient } from "../AgentClient"; diff --git a/src/SandboxClient/ports.ts b/src/SandboxClient/ports.ts index 9666646..f0ccf1d 100644 --- a/src/SandboxClient/ports.ts +++ b/src/SandboxClient/ports.ts @@ -1,6 +1,6 @@ import { Disposable } from "../utils/disposable"; import { Emitter } from "../utils/event"; -import { IAgentClient } from "../AgentClient/agent-client-interface"; +import { IAgentClient } from "../agent-client-interface"; import { Tracer, SpanStatusCode } from "@opentelemetry/api"; export type Port = { diff --git a/src/SandboxClient/setup.ts b/src/SandboxClient/setup.ts index d13a3bb..e322cd2 100644 --- a/src/SandboxClient/setup.ts +++ b/src/SandboxClient/setup.ts @@ -2,7 +2,7 @@ import * as protocol from "../pitcher-protocol"; import { Disposable } from "../utils/disposable"; import { Emitter } from "../utils/event"; import { DEFAULT_SHELL_SIZE } from "./terminals"; -import { IAgentClient } from "../AgentClient/agent-client-interface"; +import { IAgentClient } from "../agent-client-interface"; import { Tracer, SpanStatusCode } from "@opentelemetry/api"; export class Setup { diff --git a/src/SandboxClient/tasks.ts b/src/SandboxClient/tasks.ts index 8ca7aa3..e8fce74 100644 --- a/src/SandboxClient/tasks.ts +++ b/src/SandboxClient/tasks.ts @@ -1,7 +1,7 @@ import * as protocol from "../pitcher-protocol"; import { Disposable, IDisposable } from "../utils/disposable"; import { DEFAULT_SHELL_SIZE } from "./terminals"; -import { IAgentClient } from "../AgentClient/agent-client-interface"; +import { IAgentClient } from "../agent-client-interface"; import { Emitter } from "../utils/event"; import { Tracer, SpanStatusCode } from "@opentelemetry/api"; diff --git a/src/SandboxClient/terminals.ts b/src/SandboxClient/terminals.ts index ae544a8..408e5b6 100644 --- a/src/SandboxClient/terminals.ts +++ b/src/SandboxClient/terminals.ts @@ -2,7 +2,7 @@ import * as protocol from "../pitcher-protocol"; import { Disposable } from "../utils/disposable"; import { Emitter } from "../utils/event"; import { isCommandShell, ShellRunOpts } from "./commands"; -import { IAgentClient } from "../AgentClient/agent-client-interface"; +import { IAgentClient } from "../agent-client-interface"; import { Tracer, SpanStatusCode } from "@opentelemetry/api"; export type ShellSize = { cols: number; rows: number }; diff --git a/src/AgentClient/agent-client-interface.ts b/src/agent-client-interface.ts similarity index 98% rename from src/AgentClient/agent-client-interface.ts rename to src/agent-client-interface.ts index c84dca5..a5f9a55 100644 --- a/src/AgentClient/agent-client-interface.ts +++ b/src/agent-client-interface.ts @@ -8,8 +8,8 @@ import { setup, task, system, -} from "../pitcher-protocol"; -import { Event } from "../utils/event"; +} from "./pitcher-protocol"; +import { Event } from "./utils/event"; export interface IAgentClientShells { onShellExited: Event<{ diff --git a/src/api-clients/client-rest-container/client.gen.ts b/src/api-clients/client-rest-container/client.gen.ts deleted file mode 100644 index 1822a95..0000000 --- a/src/api-clients/client-rest-container/client.gen.ts +++ /dev/null @@ -1,5 +0,0 @@ -// This file is auto-generated by @hey-api/openapi-ts - -import { createClient, createConfig } from '@hey-api/client-fetch'; - -export const client = createClient(createConfig()); \ No newline at end of file diff --git a/src/api-clients/client-rest-container/index.ts b/src/api-clients/client-rest-container/index.ts deleted file mode 100644 index e64537d..0000000 --- a/src/api-clients/client-rest-container/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -// This file is auto-generated by @hey-api/openapi-ts -export * from './types.gen'; -export * from './sdk.gen'; \ No newline at end of file diff --git a/src/api-clients/client-rest-container/sdk.gen.ts b/src/api-clients/client-rest-container/sdk.gen.ts deleted file mode 100644 index bfa7256..0000000 --- a/src/api-clients/client-rest-container/sdk.gen.ts +++ /dev/null @@ -1,29 +0,0 @@ -// This file is auto-generated by @hey-api/openapi-ts - -import type { Options as ClientOptions, TDataShape, Client } from '@hey-api/client-fetch'; -import type { ContainerSetupData, ContainerSetupResponse, ContainerSetupError } from './types.gen'; -import { client as _heyApiClient } from './client.gen'; - -export type Options = ClientOptions & { - /** - * You can provide a client instance returned by `createClient()` instead of - * individual options. This might be also useful if you want to implement a - * custom client. - */ - client?: Client; -}; - -/** - * Setup container - * Set up a new container based on a template - */ -export const containerSetup = (options: Options) => { - return (options.client ?? _heyApiClient).post({ - url: '/container/setup', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options?.headers - } - }); -}; \ No newline at end of file diff --git a/src/api-clients/client-rest-container/types.gen.ts b/src/api-clients/client-rest-container/types.gen.ts deleted file mode 100644 index b2fa4a4..0000000 --- a/src/api-clients/client-rest-container/types.gen.ts +++ /dev/null @@ -1,111 +0,0 @@ -// This file is auto-generated by @hey-api/openapi-ts - -export type SuccessResponse = { - /** - * Status code for successful operations - */ - status: 0; - /** - * Result payload for the operation - */ - result: { - [key: string]: unknown; - }; -}; - -export type ErrorResponse = { - /** - * Status code for error operations - */ - status: 1; - /** - * Error details - */ - error: { - [key: string]: unknown; - }; -}; - -export type ProtocolError = { - /** - * Error code - */ - code: string; - /** - * Error message - */ - message: string; - /** - * Additional error data - */ - data?: { - [key: string]: unknown; - } | null; -}; - -export type TaskDto = { - /** - * Task identifier - */ - id: string; - /** - * Task status - */ - status: string; - /** - * Task progress (0-100) - */ - progress: number; -}; - -export type ContainerSetupData = { - body: { - /** - * Identifier of the template to use - */ - templateId: string; - /** - * Arguments for the template - */ - templateArgs: { - [key: string]: string; - }; - features?: Array<{ - /** - * Feature identifier - */ - id: string; - /** - * Options for the feature - */ - options: { - [key: string]: string; - }; - }> | null; - }; - path?: never; - query?: never; - url: '/container/setup'; -}; - -export type ContainerSetupErrors = { - /** - * Error setting up container - */ - 400: ErrorResponse & { - error?: ProtocolError; - }; -}; - -export type ContainerSetupError = ContainerSetupErrors[keyof ContainerSetupErrors]; - -export type ContainerSetupResponses = { - /** - * Successful operation - */ - 200: SuccessResponse & { - result?: TaskDto; - }; -}; - -export type ContainerSetupResponse = ContainerSetupResponses[keyof ContainerSetupResponses]; \ No newline at end of file diff --git a/src/api-clients/client-rest-fs/client.gen.ts b/src/api-clients/client-rest-fs/client.gen.ts deleted file mode 100644 index 1822a95..0000000 --- a/src/api-clients/client-rest-fs/client.gen.ts +++ /dev/null @@ -1,5 +0,0 @@ -// This file is auto-generated by @hey-api/openapi-ts - -import { createClient, createConfig } from '@hey-api/client-fetch'; - -export const client = createClient(createConfig()); \ No newline at end of file diff --git a/src/api-clients/client-rest-fs/index.ts b/src/api-clients/client-rest-fs/index.ts deleted file mode 100644 index e64537d..0000000 --- a/src/api-clients/client-rest-fs/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -// This file is auto-generated by @hey-api/openapi-ts -export * from './types.gen'; -export * from './sdk.gen'; \ No newline at end of file diff --git a/src/api-clients/client-rest-fs/sdk.gen.ts b/src/api-clients/client-rest-fs/sdk.gen.ts deleted file mode 100644 index 83a4cab..0000000 --- a/src/api-clients/client-rest-fs/sdk.gen.ts +++ /dev/null @@ -1,280 +0,0 @@ -// This file is auto-generated by @hey-api/openapi-ts - -import type { Options as ClientOptions, TDataShape, Client } from '@hey-api/client-fetch'; -import type { WriteFileData, WriteFileResponse, WriteFileError, FsReadData, FsReadResponse, FsReadError, FsOperationData, FsOperationResponse, FsOperationError, FsSearchData, FsSearchResponse, FsSearchError, FsStreamingSearchData, FsStreamingSearchResponse, FsStreamingSearchError, FsCancelStreamingSearchData, FsCancelStreamingSearchResponse, FsCancelStreamingSearchError, FsPathSearchData, FsPathSearchResponse, FsPathSearchError, FsUploadData, FsUploadResponse, FsUploadError, FsDownloadData, FsDownloadResponse, FsDownloadError, FsReadFileData, FsReadFileResponse, FsReadFileError, FsReadDirData, FsReadDirResponse, FsReadDirError, FsStatData, FsStatResponse, FsStatError, FsCopyData, FsCopyResponse, FsCopyError, FsRenameData, FsRenameResponse, FsRenameError, FsRemoveData, FsRemoveResponse, FsRemoveError, FsMkdirData, FsMkdirResponse, FsMkdirError, FsWatchData, FsWatchResponse, FsWatchError, FsUnwatchData, FsUnwatchResponse, FsUnwatchError } from './types.gen'; -import { client as _heyApiClient } from './client.gen'; - -export type Options = ClientOptions & { - /** - * You can provide a client instance returned by `createClient()` instead of - * individual options. This might be also useful if you want to implement a - * custom client. - */ - client?: Client; -}; - -/** - * Write to a file - * Write content to a file at the specified path - */ -export const writeFile = (options: Options) => { - return (options.client ?? _heyApiClient).post({ - url: '/fs/writeFile', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options?.headers - } - }); -}; - -/** - * Read file system - * Retrieve the latest snapshot of the server's MemoryFS file and children list - */ -export const fsRead = (options?: Options) => { - return (options?.client ?? _heyApiClient).post({ - url: '/fs/read', - ...options - }); -}; - -/** - * Perform file system operation - * Send a tree operation reflecting filesystem operations - */ -export const fsOperation = (options: Options) => { - return (options.client ?? _heyApiClient).post({ - url: '/fs/operation', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options?.headers - } - }); -}; - -/** - * Search files - * Search for content in files - */ -export const fsSearch = (options: Options) => { - return (options.client ?? _heyApiClient).post({ - url: '/fs/search', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options?.headers - } - }); -}; - -/** - * Start streaming search - * Start a streaming search for content in files - */ -export const fsStreamingSearch = (options: Options) => { - return (options.client ?? _heyApiClient).post({ - url: '/fs/streamingSearch', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options?.headers - } - }); -}; - -/** - * Cancel streaming search - * Cancel an ongoing streaming search - */ -export const fsCancelStreamingSearch = (options: Options) => { - return (options.client ?? _heyApiClient).post({ - url: '/fs/cancelStreamingSearch', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options?.headers - } - }); -}; - -/** - * Search file paths - * Search for file paths matching a pattern - */ -export const fsPathSearch = (options: Options) => { - return (options.client ?? _heyApiClient).post({ - url: '/fs/pathSearch', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options?.headers - } - }); -}; - -/** - * Upload file - * Upload a file to the specified parent directory - */ -export const fsUpload = (options: Options) => { - return (options.client ?? _heyApiClient).post({ - url: '/fs/upload', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options?.headers - } - }); -}; - -/** - * Download files - * Download files at a specified path as a zip - */ -export const fsDownload = (options: Options) => { - return (options.client ?? _heyApiClient).post({ - url: '/fs/download', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options?.headers - } - }); -}; - -/** - * Read file content - * Read the content of a file at the specified path - */ -export const fsReadFile = (options: Options) => { - return (options.client ?? _heyApiClient).post({ - url: '/fs/readFile', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options?.headers - } - }); -}; - -/** - * Read directory contents - * List the contents of a directory at the specified path - */ -export const fsReadDir = (options: Options) => { - return (options.client ?? _heyApiClient).post({ - url: '/fs/readdir', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options?.headers - } - }); -}; - -/** - * Get file/directory stats - * Get stats for a file or directory at the specified path - */ -export const fsStat = (options: Options) => { - return (options.client ?? _heyApiClient).post({ - url: '/fs/stat', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options?.headers - } - }); -}; - -/** - * Copy file/directory - * Copy a file or directory from one location to another - */ -export const fsCopy = (options: Options) => { - return (options.client ?? _heyApiClient).post({ - url: '/fs/copy', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options?.headers - } - }); -}; - -/** - * Rename file/directory - * Rename a file or directory (move from one location to another) - */ -export const fsRename = (options: Options) => { - return (options.client ?? _heyApiClient).post({ - url: '/fs/rename', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options?.headers - } - }); -}; - -/** - * Remove file/directory - * Delete a file or directory at the specified path - */ -export const fsRemove = (options: Options) => { - return (options.client ?? _heyApiClient).post({ - url: '/fs/remove', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options?.headers - } - }); -}; - -/** - * Create directory - * Create a new directory at the specified path - */ -export const fsMkdir = (options: Options) => { - return (options.client ?? _heyApiClient).post({ - url: '/fs/mkdir', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options?.headers - } - }); -}; - -/** - * Watch file/directory - * Watch a file or directory for changes - */ -export const fsWatch = (options: Options) => { - return (options.client ?? _heyApiClient).post({ - url: '/fs/watch', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options?.headers - } - }); -}; - -/** - * Stop watching file/directory - * Stop watching a file or directory for changes - */ -export const fsUnwatch = (options: Options) => { - return (options.client ?? _heyApiClient).post({ - url: '/fs/unwatch', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options?.headers - } - }); -}; \ No newline at end of file diff --git a/src/api-clients/client-rest-fs/types.gen.ts b/src/api-clients/client-rest-fs/types.gen.ts deleted file mode 100644 index 624cd45..0000000 --- a/src/api-clients/client-rest-fs/types.gen.ts +++ /dev/null @@ -1,1058 +0,0 @@ -// This file is auto-generated by @hey-api/openapi-ts - -export type SuccessResponse = { - /** - * Status code for successful operations - */ - status: 0; - /** - * Result payload for the operation - */ - result: { - [key: string]: unknown; - }; -}; - -export type ErrorResponse = { - /** - * Status code for error operations - */ - status: 1; - error: ({ - code?: 'DefaultError'; - } & DefaultError) | ({ - code?: 'RawFsError'; - } & RawFsError); -}; - -export type DefaultError = { - code: PitcherErrorCode; - /** - * Additional error details - */ - data?: { - [key: string]: unknown; - } | null; - /** - * Human-readable error message that can be displayed to users - */ - publicMessage?: string | null; -}; - -export type RawFsError = { - /** - * RAWFS_ERROR code - */ - code: 102; - data: { - /** - * File system error number, or null if not available - */ - errno: unknown; - }; - /** - * Human-readable error message that can be displayed to users - */ - publicMessage?: string | null; -}; - -/** - * Enumeration of error codes - */ -export type PitcherErrorCode = 0 | 1 | 2 | 3 | 100 | 101 | 102 | 200 | 201 | 204 | 300 | 400 | 404 | 410 | 420 | 430 | 440 | 450 | 460 | 470 | 500 | 600 | 601 | 602 | 704 | 800 | 801 | 802 | 803 | 814; - -export type WriteFileRequest = { - /** - * File path to write to - */ - path: string; - /** - * File content as binary data (Uint8Array) - */ - content: Blob | File; - /** - * Whether to create the file if it doesn't exist - */ - create?: boolean; - /** - * Whether to overwrite the file if it exists - */ - overwrite?: boolean; -}; - -export type FsReadResult = { - treeNodes: Array<{ - [key: string]: unknown; - }>; - /** - * Current clock value for the file system - */ - clock: number; -}; - -export type FsOperationRequest = { - operation: FsOperation; -}; - -export type FsOperation = ({ - type?: 'FSCreateOperation'; -} & FsCreateOperation) | ({ - type?: 'FSDeleteOperation'; -} & FsDeleteOperation) | ({ - type?: 'FSMoveOperation'; -} & FsMoveOperation); - -export type FsCreateOperation = { - type: 'create'; - /** - * ID of the parent directory - */ - parentId: string; - newEntry: { - /** - * ID of the new entry - */ - id: string; - /** - * Type of the node - */ - type: 'directory' | 'file'; - /** - * Name of the new entry - */ - name: string; - }; -}; - -export type FsDeleteOperation = { - type: 'delete'; - /** - * ID of the entry to delete - */ - id: string; -}; - -export type FsMoveOperation = { - type: 'move'; - /** - * ID of the entry to move - */ - id: string; - /** - * ID of the new parent directory - */ - parentId?: string | null; - /** - * New name for the entry - */ - name?: string | null; -}; - -export type FsOperationResult = { - /** - * Success code - */ - code: 0; - /** - * Current clock value - */ - clock: number; -} | { - /** - * Ignored code - */ - code: 1; -}; - -export type FsSearchParams = { - /** - * Text to search for - */ - text: string; - /** - * Glob pattern to filter files - */ - glob?: string | null; - /** - * Whether to treat the search text as a regular expression - */ - isRegex?: boolean | null; - /** - * Case sensitivity setting for the search - */ - caseSensitivity?: 'smart' | 'enabled' | 'disabled'; -}; - -export type SearchResult = { - /** - * ID of the file containing the match - */ - fileId: string; - lines: { - /** - * Text of the line containing the match - */ - text: string; - }; - /** - * Line number of the match - */ - lineNumber: number; - /** - * Absolute offset of the match in the file - */ - absoluteOffset: number; - submatches: Array; -}; - -export type SearchSubMatch = { - match: { - /** - * Matched text - */ - text: string; - }; - /** - * Start position of the match - */ - start: number; - /** - * End position of the match - */ - end: number; -}; - -export type FsStreamingSearchParams = { - /** - * ID for the search operation - */ - searchId: string; - /** - * Text to search for - */ - text: string; - /** - * Glob pattern to filter files - */ - glob?: string | null; - /** - * Whether to treat the search text as a regular expression - */ - isRegex?: boolean | null; - /** - * Case sensitivity setting for the search - */ - caseSensitivity?: 'smart' | 'enabled' | 'disabled'; - /** - * Maximum number of results to return (default: 10,000) - */ - maxResults?: number | null; -}; - -export type PathSearchParams = { - /** - * Text to search for in file paths - */ - text: string; -}; - -export type PathSearchResult = { - matches: Array; -}; - -export type PathSearchMatch = { - /** - * Path that matched the search - */ - path: string; - submatches: Array; -}; - -export type InvalidIdError = { - /** - * INVALID_ID error code - */ - code: 100; -}; - -export type FsReadFileParams = { - /** - * Path to the file to read - */ - path: string; -}; - -export type FsReadFileResult = { - /** - * File content as binary data - */ - content: Blob | File; -}; - -export type FsReadDirParams = { - /** - * Path to the directory to read - */ - path: string; -}; - -export type FsReadDirResult = { - entries: Array<{ - /** - * Name of the entry - */ - name: string; - /** - * Type of the entry - */ - type: 'directory' | 'file'; - /** - * Whether the entry is a symlink - */ - isSymlink: boolean; - }>; -}; - -export type FsStatParams = { - /** - * Path to the file or directory to stat - */ - path: string; -}; - -export type FsStatResult = { - /** - * Type of the entry - */ - type: 'directory' | 'file'; - /** - * Whether the entry is a symlink - */ - isSymlink: boolean; - /** - * Size of the file in bytes - */ - size: number; - /** - * Last modified time - */ - mtime: number; - /** - * Creation time - */ - ctime: number; - /** - * Last accessed time - */ - atime: number; -}; - -export type FsCopyParams = { - /** - * Path to copy from - */ - from: string; - /** - * Path to copy to - */ - to: string; - /** - * Whether to copy directories recursively - */ - recursive?: boolean | null; - /** - * Whether to overwrite existing files - */ - overwrite?: boolean | null; -}; - -export type FsRenameParams = { - /** - * Path to rename from - */ - from: string; - /** - * Path to rename to - */ - to: string; - /** - * Whether to overwrite existing files - */ - overwrite?: boolean | null; -}; - -export type FsRemoveParams = { - /** - * Path to remove - */ - path: string; - /** - * Whether to remove directories recursively - */ - recursive?: boolean | null; -}; - -export type FsMkdirParams = { - /** - * Path to create directory at - */ - path: string; - /** - * Whether to create parent directories if they don't exist - */ - recursive?: boolean | null; -}; - -export type FsWatchParams = { - /** - * Path to watch - */ - path: string; - /** - * Whether to watch directories recursively - */ - recursive?: boolean | null; - /** - * Glob patterns to exclude from watching - */ - excludes?: Array | null; -}; - -export type FsWatchResult = { - /** - * ID of the watch - */ - watchId: string; -}; - -export type FsUnwatchParams = { - /** - * ID of the watch to stop - */ - watchId: string; -}; - -export type WriteFileData = { - body: WriteFileRequest; - path?: never; - query?: never; - url: '/fs/writeFile'; -}; - -export type WriteFileErrors = { - /** - * Error writing file - */ - 400: ErrorResponse & { - error?: ({ - code?: 'DefaultError'; - } & DefaultError) | ({ - code?: 'RawFsError'; - } & RawFsError); - }; -}; - -export type WriteFileError = WriteFileErrors[keyof WriteFileErrors]; - -export type WriteFileResponses = { - /** - * Successful operation - */ - 200: SuccessResponse & { - result?: { - [key: string]: unknown; - }; - }; -}; - -export type WriteFileResponse = WriteFileResponses[keyof WriteFileResponses]; - -export type FsReadData = { - body?: never; - path?: never; - query?: never; - url: '/fs/read'; -}; - -export type FsReadErrors = { - /** - * Error reading file system - */ - 400: ErrorResponse & { - error?: DefaultError; - }; -}; - -export type FsReadError = FsReadErrors[keyof FsReadErrors]; - -export type FsReadResponses = { - /** - * Successful operation - */ - 200: SuccessResponse & { - result?: FsReadResult; - }; -}; - -export type FsReadResponse = FsReadResponses[keyof FsReadResponses]; - -export type FsOperationData = { - body: FsOperationRequest; - path?: never; - query?: never; - url: '/fs/operation'; -}; - -export type FsOperationErrors = { - /** - * Error performing operation - */ - 400: ErrorResponse & { - error?: DefaultError; - }; -}; - -export type FsOperationError = FsOperationErrors[keyof FsOperationErrors]; - -export type FsOperationResponses = { - /** - * Successful operation - */ - 200: SuccessResponse & { - result?: FsOperationResult; - }; -}; - -export type FsOperationResponse = FsOperationResponses[keyof FsOperationResponses]; - -export type FsSearchData = { - body: FsSearchParams; - path?: never; - query?: never; - url: '/fs/search'; -}; - -export type FsSearchErrors = { - /** - * Error searching files - */ - 400: ErrorResponse & { - error?: DefaultError; - }; -}; - -export type FsSearchError = FsSearchErrors[keyof FsSearchErrors]; - -export type FsSearchResponses = { - /** - * Successful operation - */ - 200: SuccessResponse & { - result?: Array; - }; -}; - -export type FsSearchResponse = FsSearchResponses[keyof FsSearchResponses]; - -export type FsStreamingSearchData = { - body: FsStreamingSearchParams; - path?: never; - query?: never; - url: '/fs/streamingSearch'; -}; - -export type FsStreamingSearchErrors = { - /** - * Error starting streaming search - */ - 400: ErrorResponse & { - error?: DefaultError; - }; -}; - -export type FsStreamingSearchError = FsStreamingSearchErrors[keyof FsStreamingSearchErrors]; - -export type FsStreamingSearchResponses = { - /** - * Successful operation - */ - 200: SuccessResponse & { - result?: { - /** - * ID of the search operation - */ - searchId?: string; - }; - }; -}; - -export type FsStreamingSearchResponse = FsStreamingSearchResponses[keyof FsStreamingSearchResponses]; - -export type FsCancelStreamingSearchData = { - body: { - /** - * ID of the search to cancel - */ - searchId: string; - }; - path?: never; - query?: never; - url: '/fs/cancelStreamingSearch'; -}; - -export type FsCancelStreamingSearchErrors = { - /** - * Error cancelling search - */ - 400: ErrorResponse & { - error?: DefaultError; - }; -}; - -export type FsCancelStreamingSearchError = FsCancelStreamingSearchErrors[keyof FsCancelStreamingSearchErrors]; - -export type FsCancelStreamingSearchResponses = { - /** - * Successful operation - */ - 200: SuccessResponse & { - result?: { - /** - * ID of the cancelled search - */ - searchId?: string; - }; - }; -}; - -export type FsCancelStreamingSearchResponse = FsCancelStreamingSearchResponses[keyof FsCancelStreamingSearchResponses]; - -export type FsPathSearchData = { - body: PathSearchParams; - path?: never; - query?: never; - url: '/fs/pathSearch'; -}; - -export type FsPathSearchErrors = { - /** - * Error searching paths - */ - 400: ErrorResponse & { - error?: DefaultError; - }; -}; - -export type FsPathSearchError = FsPathSearchErrors[keyof FsPathSearchErrors]; - -export type FsPathSearchResponses = { - /** - * Successful operation - */ - 200: SuccessResponse & { - result?: PathSearchResult; - }; -}; - -export type FsPathSearchResponse = FsPathSearchResponses[keyof FsPathSearchResponses]; - -export type FsUploadData = { - body: { - /** - * ID of the parent directory - */ - parentId: string; - /** - * Name of the file to create - */ - filename: string; - /** - * File content as binary data - */ - content: Blob | File; - }; - path?: never; - query?: never; - url: '/fs/upload'; -}; - -export type FsUploadErrors = { - /** - * Error uploading file - */ - 400: ErrorResponse & { - error?: ({ - code?: 'DefaultError'; - } & DefaultError) | ({ - code?: 'InvalidIdError'; - } & InvalidIdError); - }; -}; - -export type FsUploadError = FsUploadErrors[keyof FsUploadErrors]; - -export type FsUploadResponses = { - /** - * Successful operation - */ - 200: SuccessResponse & { - result?: { - /** - * ID of the created file - */ - fileId?: string; - }; - }; -}; - -export type FsUploadResponse = FsUploadResponses[keyof FsUploadResponses]; - -export type FsDownloadData = { - body: { - /** - * Path to download - */ - path: string; - /** - * Glob patterns of files/folders to exclude from the download - */ - excludes?: Array; - }; - path?: never; - query?: never; - url: '/fs/download'; -}; - -export type FsDownloadErrors = { - /** - * Error creating download - */ - 400: ErrorResponse & { - error?: DefaultError; - }; -}; - -export type FsDownloadError = FsDownloadErrors[keyof FsDownloadErrors]; - -export type FsDownloadResponses = { - /** - * Successful operation - */ - 200: SuccessResponse & { - result?: { - /** - * URL to download the files from - */ - downloadUrl?: string; - }; - }; -}; - -export type FsDownloadResponse = FsDownloadResponses[keyof FsDownloadResponses]; - -export type FsReadFileData = { - body: FsReadFileParams; - path?: never; - query?: never; - url: '/fs/readFile'; -}; - -export type FsReadFileErrors = { - /** - * Error reading file - */ - 400: ErrorResponse & { - error?: ({ - code?: 'DefaultError'; - } & DefaultError) | ({ - code?: 'RawFsError'; - } & RawFsError); - }; -}; - -export type FsReadFileError = FsReadFileErrors[keyof FsReadFileErrors]; - -export type FsReadFileResponses = { - /** - * Successful operation - */ - 200: SuccessResponse & { - result?: FsReadFileResult; - }; -}; - -export type FsReadFileResponse = FsReadFileResponses[keyof FsReadFileResponses]; - -export type FsReadDirData = { - body: FsReadDirParams; - path?: never; - query?: never; - url: '/fs/readdir'; -}; - -export type FsReadDirErrors = { - /** - * Error reading directory - */ - 400: ErrorResponse & { - error?: ({ - code?: 'DefaultError'; - } & DefaultError) | ({ - code?: 'RawFsError'; - } & RawFsError); - }; -}; - -export type FsReadDirError = FsReadDirErrors[keyof FsReadDirErrors]; - -export type FsReadDirResponses = { - /** - * Successful operation - */ - 200: SuccessResponse & { - result?: FsReadDirResult; - }; -}; - -export type FsReadDirResponse = FsReadDirResponses[keyof FsReadDirResponses]; - -export type FsStatData = { - body: FsStatParams; - path?: never; - query?: never; - url: '/fs/stat'; -}; - -export type FsStatErrors = { - /** - * Error getting stats - */ - 400: ErrorResponse & { - error?: ({ - code?: 'DefaultError'; - } & DefaultError) | ({ - code?: 'RawFsError'; - } & RawFsError); - }; -}; - -export type FsStatError = FsStatErrors[keyof FsStatErrors]; - -export type FsStatResponses = { - /** - * Successful operation - */ - 200: SuccessResponse & { - result?: FsStatResult; - }; -}; - -export type FsStatResponse = FsStatResponses[keyof FsStatResponses]; - -export type FsCopyData = { - body: FsCopyParams; - path?: never; - query?: never; - url: '/fs/copy'; -}; - -export type FsCopyErrors = { - /** - * Error copying file/directory - */ - 400: ErrorResponse & { - error?: ({ - code?: 'DefaultError'; - } & DefaultError) | ({ - code?: 'RawFsError'; - } & RawFsError); - }; -}; - -export type FsCopyError = FsCopyErrors[keyof FsCopyErrors]; - -export type FsCopyResponses = { - /** - * Successful operation - */ - 200: SuccessResponse & { - result?: { - [key: string]: unknown; - }; - }; -}; - -export type FsCopyResponse = FsCopyResponses[keyof FsCopyResponses]; - -export type FsRenameData = { - body: FsRenameParams; - path?: never; - query?: never; - url: '/fs/rename'; -}; - -export type FsRenameErrors = { - /** - * Error renaming file/directory - */ - 400: ErrorResponse & { - error?: ({ - code?: 'DefaultError'; - } & DefaultError) | ({ - code?: 'RawFsError'; - } & RawFsError); - }; -}; - -export type FsRenameError = FsRenameErrors[keyof FsRenameErrors]; - -export type FsRenameResponses = { - /** - * Successful operation - */ - 200: SuccessResponse & { - result?: { - [key: string]: unknown; - }; - }; -}; - -export type FsRenameResponse = FsRenameResponses[keyof FsRenameResponses]; - -export type FsRemoveData = { - body: FsRemoveParams; - path?: never; - query?: never; - url: '/fs/remove'; -}; - -export type FsRemoveErrors = { - /** - * Error removing file/directory - */ - 400: ErrorResponse & { - error?: ({ - code?: 'DefaultError'; - } & DefaultError) | ({ - code?: 'RawFsError'; - } & RawFsError); - }; -}; - -export type FsRemoveError = FsRemoveErrors[keyof FsRemoveErrors]; - -export type FsRemoveResponses = { - /** - * Successful operation - */ - 200: SuccessResponse & { - result?: { - [key: string]: unknown; - }; - }; -}; - -export type FsRemoveResponse = FsRemoveResponses[keyof FsRemoveResponses]; - -export type FsMkdirData = { - body: FsMkdirParams; - path?: never; - query?: never; - url: '/fs/mkdir'; -}; - -export type FsMkdirErrors = { - /** - * Error creating directory - */ - 400: ErrorResponse & { - error?: ({ - code?: 'DefaultError'; - } & DefaultError) | ({ - code?: 'RawFsError'; - } & RawFsError); - }; -}; - -export type FsMkdirError = FsMkdirErrors[keyof FsMkdirErrors]; - -export type FsMkdirResponses = { - /** - * Successful operation - */ - 200: SuccessResponse & { - result?: { - [key: string]: unknown; - }; - }; -}; - -export type FsMkdirResponse = FsMkdirResponses[keyof FsMkdirResponses]; - -export type FsWatchData = { - body: FsWatchParams; - path?: never; - query?: never; - url: '/fs/watch'; -}; - -export type FsWatchErrors = { - /** - * Error watching file/directory - */ - 400: ErrorResponse & { - error?: ({ - code?: 'DefaultError'; - } & DefaultError) | ({ - code?: 'RawFsError'; - } & RawFsError); - }; -}; - -export type FsWatchError = FsWatchErrors[keyof FsWatchErrors]; - -export type FsWatchResponses = { - /** - * Successful operation - */ - 200: SuccessResponse & { - result?: FsWatchResult; - }; -}; - -export type FsWatchResponse = FsWatchResponses[keyof FsWatchResponses]; - -export type FsUnwatchData = { - body: FsUnwatchParams; - path?: never; - query?: never; - url: '/fs/unwatch'; -}; - -export type FsUnwatchErrors = { - /** - * Error unwatching file/directory - */ - 400: ErrorResponse & { - error?: ({ - code?: 'DefaultError'; - } & DefaultError) | ({ - code?: 'RawFsError'; - } & RawFsError); - }; -}; - -export type FsUnwatchError = FsUnwatchErrors[keyof FsUnwatchErrors]; - -export type FsUnwatchResponses = { - /** - * Successful operation - */ - 200: SuccessResponse & { - result?: { - [key: string]: unknown; - }; - }; -}; - -export type FsUnwatchResponse = FsUnwatchResponses[keyof FsUnwatchResponses]; \ No newline at end of file diff --git a/src/api-clients/client-rest-git/client.gen.ts b/src/api-clients/client-rest-git/client.gen.ts deleted file mode 100644 index 1822a95..0000000 --- a/src/api-clients/client-rest-git/client.gen.ts +++ /dev/null @@ -1,5 +0,0 @@ -// This file is auto-generated by @hey-api/openapi-ts - -import { createClient, createConfig } from '@hey-api/client-fetch'; - -export const client = createClient(createConfig()); \ No newline at end of file diff --git a/src/api-clients/client-rest-git/index.ts b/src/api-clients/client-rest-git/index.ts deleted file mode 100644 index e64537d..0000000 --- a/src/api-clients/client-rest-git/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -// This file is auto-generated by @hey-api/openapi-ts -export * from './types.gen'; -export * from './sdk.gen'; \ No newline at end of file diff --git a/src/api-clients/client-rest-git/sdk.gen.ts b/src/api-clients/client-rest-git/sdk.gen.ts deleted file mode 100644 index 0e3b272..0000000 --- a/src/api-clients/client-rest-git/sdk.gen.ts +++ /dev/null @@ -1,224 +0,0 @@ -// This file is auto-generated by @hey-api/openapi-ts - -import type { Options as ClientOptions, TDataShape, Client } from '@hey-api/client-fetch'; -import type { GitStatusData, GitStatusResponse, GitStatusError, GitRemotesData, GitRemotesResponse, GitRemotesError, GitTargetDiffData, GitTargetDiffResponse, GitTargetDiffError, GitPullData, GitPullResponse, GitPullError, GitDiscardData, GitDiscardResponse, GitDiscardError, GitCommitData, GitCommitResponse, GitCommitError, GitPushData, GitPushResponse, GitPushError, GitPushToRemoteData, GitPushToRemoteResponse, GitPushToRemoteError, GitRenameBranchData, GitRenameBranchResponse, GitRenameBranchError, GitRemoteContentData, GitRemoteContentResponse, GitRemoteContentError, GitDiffStatusData, GitDiffStatusResponse, GitDiffStatusError, GitResetLocalWithRemoteData, GitResetLocalWithRemoteResponse, GitResetLocalWithRemoteError, GitCheckoutInitialBranchData, GitCheckoutInitialBranchResponse, GitCheckoutInitialBranchError, GitTransposeLinesData, GitTransposeLinesResponse, GitTransposeLinesError } from './types.gen'; -import { client as _heyApiClient } from './client.gen'; - -export type Options = ClientOptions & { - /** - * You can provide a client instance returned by `createClient()` instead of - * individual options. This might be also useful if you want to implement a - * custom client. - */ - client?: Client; -}; - -/** - * Get git status - * Retrieve current git status including changed files, branch information, and commits - */ -export const gitStatus = (options: Options) => { - return (options.client ?? _heyApiClient).post({ - url: '/git/status', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options?.headers - } - }); -}; - -/** - * Get git remotes - * Retrieve git remote information - */ -export const gitRemotes = (options: Options) => { - return (options.client ?? _heyApiClient).post({ - url: '/git/remotes', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options?.headers - } - }); -}; - -/** - * Get git target diff - * Retrieve diff between current branch and target branch - */ -export const gitTargetDiff = (options: Options) => { - return (options.client ?? _heyApiClient).post({ - url: '/git/targetDiff', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options?.headers - } - }); -}; - -/** - * Pull from remote - * Pull changes from remote repository - */ -export const gitPull = (options: Options) => { - return (options.client ?? _heyApiClient).post({ - url: '/git/pull', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options?.headers - } - }); -}; - -/** - * Discard changes - * Discard local changes for specified paths - */ -export const gitDiscard = (options: Options) => { - return (options.client ?? _heyApiClient).post({ - url: '/git/discard', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options?.headers - } - }); -}; - -/** - * Commit changes - * Commit changes to the repository - */ -export const gitCommit = (options: Options) => { - return (options.client ?? _heyApiClient).post({ - url: '/git/commit', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options?.headers - } - }); -}; - -/** - * Push changes - * Push local commits to remote repository - */ -export const gitPush = (options: Options) => { - return (options.client ?? _heyApiClient).post({ - url: '/git/push', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options?.headers - } - }); -}; - -/** - * Push to remote - * Push to a specific remote repository - */ -export const gitPushToRemote = (options: Options) => { - return (options.client ?? _heyApiClient).post({ - url: '/git/pushToRemote', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options?.headers - } - }); -}; - -/** - * Rename branch - * Rename a git branch - */ -export const gitRenameBranch = (options: Options) => { - return (options.client ?? _heyApiClient).post({ - url: '/git/renameBranch', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options?.headers - } - }); -}; - -/** - * Get remote content - * Retrieve content from a remote repository - */ -export const gitRemoteContent = (options: Options) => { - return (options.client ?? _heyApiClient).post({ - url: '/git/remoteContent', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options?.headers - } - }); -}; - -/** - * Get diff status - * Retrieve diff status between two git references - */ -export const gitDiffStatus = (options: Options) => { - return (options.client ?? _heyApiClient).post({ - url: '/git/diffStatus', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options?.headers - } - }); -}; - -/** - * Reset local with remote - * Reset local repository to match the remote state - */ -export const gitResetLocalWithRemote = (options: Options) => { - return (options.client ?? _heyApiClient).post({ - url: '/git/resetLocalWithRemote', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options?.headers - } - }); -}; - -/** - * Checkout initial branch - * Checkout the initial branch of the repository - */ -export const gitCheckoutInitialBranch = (options: Options) => { - return (options.client ?? _heyApiClient).post({ - url: '/git/checkoutInitialBranch', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options?.headers - } - }); -}; - -/** - * Transpose lines - * Transpose line numbers from one git reference to another - */ -export const gitTransposeLines = (options: Options) => { - return (options.client ?? _heyApiClient).post({ - url: '/git/transposeLines', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options?.headers - } - }); -}; \ No newline at end of file diff --git a/src/api-clients/client-rest-git/types.gen.ts b/src/api-clients/client-rest-git/types.gen.ts deleted file mode 100644 index dce87ba..0000000 --- a/src/api-clients/client-rest-git/types.gen.ts +++ /dev/null @@ -1,725 +0,0 @@ -// This file is auto-generated by @hey-api/openapi-ts - -export type SuccessResponse = { - /** - * Status code for successful operations - */ - status: 0; - /** - * Result payload for the operation - */ - result: { - [key: string]: unknown; - }; -}; - -export type ErrorResponse = { - /** - * Status code for error operations - */ - status: 1; - /** - * Error details - */ - error: { - [key: string]: unknown; - }; -}; - -export type CommonError = { - /** - * Error code - */ - code: 'GIT_OPERATION_IN_PROGRESS' | 'GIT_REMOTE_FILE_NOT_FOUND'; - /** - * Error message - */ - message: string; -} | { - /** - * Protocol error code - */ - code: string; - /** - * Error message - */ - message: string; - /** - * Additional error data - */ - data?: { - [key: string]: unknown; - }; -}; - -/** - * Git status short format codes - */ -export type GitStatusShortFormat = '' | 'M' | 'A' | 'D' | 'R' | 'C' | 'U' | '?'; - -export type GitItem = { - /** - * File path - */ - path: string; - index: GitStatusShortFormat; - workingTree: GitStatusShortFormat; - /** - * Whether the file is staged - */ - isStaged: boolean; - /** - * Whether the file has conflicts - */ - isConflicted: boolean; - /** - * Unique identifier for the file - */ - fileId?: string; -}; - -/** - * Map of file IDs to Git items - */ -export type GitChangedFiles = { - [key: string]: GitItem; -}; - -export type GitBranchProperties = { - /** - * Current HEAD reference - */ - head?: unknown; - /** - * Current branch name - */ - branch?: unknown; - /** - * Number of commits ahead of the remote - */ - ahead: number; - /** - * Number of commits behind the remote - */ - behind: number; - /** - * Whether the branch is safe to operate on - */ - safe: boolean; -}; - -export type GitCommit = { - /** - * Commit hash - */ - hash: string; - /** - * Commit date - */ - date: string; - /** - * Commit message - */ - message: string; - /** - * Commit author - */ - author: string; -}; - -export type GitStatus = { - changedFiles: GitChangedFiles; - deletedFiles: Array; - /** - * Whether there are remote conflicts - */ - conflicts: boolean; - /** - * Whether there are local changes - */ - localChanges: boolean; - remote: GitBranchProperties; - target: GitBranchProperties; - /** - * Current HEAD reference - */ - head?: string; - commits: Array; - /** - * Current branch name - */ - branch: unknown; - /** - * Whether a merge is in progress - */ - isMerging: boolean; -}; - -export type GitTargetDiff = { - /** - * Number of commits ahead of the target - */ - ahead: number; - /** - * Number of commits behind the target - */ - behind: number; - commits: Array; -}; - -export type GitRemotes = { - /** - * Origin remote URL - */ - origin: string; - /** - * Upstream remote URL - */ - upstream: string; -}; - -export type GitRemoteParams = { - /** - * Branch or commit hash - */ - reference: string; - /** - * Path to the file - */ - path: string; -}; - -export type GitDiffStatusParams = { - /** - * Base reference used for diffing - */ - base: string; - /** - * Head reference used for diffing - */ - head: string; -}; - -export type GitDiffStatusItem = { - status: GitStatusShortFormat; - /** - * Path to the file - */ - path: string; - /** - * Original path for renamed files - */ - oldPath?: string; - hunks: Array<{ - original: { - start: number; - end: number; - }; - modified: { - start: number; - end: number; - }; - }>; -}; - -export type GitDiffStatusResult = { - files: Array; -}; - -export type GitStatusData = { - body: { - [key: string]: unknown; - }; - path?: never; - query?: never; - url: '/git/status'; -}; - -export type GitStatusErrors = { - /** - * Error retrieving git status - */ - 400: ErrorResponse & { - error?: CommonError; - }; -}; - -export type GitStatusError = GitStatusErrors[keyof GitStatusErrors]; - -export type GitStatusResponses = { - /** - * Successful operation - */ - 200: SuccessResponse & { - result?: GitStatus; - }; -}; - -export type GitStatusResponse = GitStatusResponses[keyof GitStatusResponses]; - -export type GitRemotesData = { - body: { - [key: string]: unknown; - }; - path?: never; - query?: never; - url: '/git/remotes'; -}; - -export type GitRemotesErrors = { - /** - * Error retrieving git remotes - */ - 400: ErrorResponse & { - error?: CommonError; - }; -}; - -export type GitRemotesError = GitRemotesErrors[keyof GitRemotesErrors]; - -export type GitRemotesResponses = { - /** - * Successful operation - */ - 200: SuccessResponse & { - result?: GitRemotes; - }; -}; - -export type GitRemotesResponse = GitRemotesResponses[keyof GitRemotesResponses]; - -export type GitTargetDiffData = { - body: { - /** - * Branch to compare against - */ - branch: string; - }; - path?: never; - query?: never; - url: '/git/targetDiff'; -}; - -export type GitTargetDiffErrors = { - /** - * Error retrieving git target diff - */ - 400: ErrorResponse & { - error?: CommonError; - }; -}; - -export type GitTargetDiffError = GitTargetDiffErrors[keyof GitTargetDiffErrors]; - -export type GitTargetDiffResponses = { - /** - * Successful operation - */ - 200: SuccessResponse & { - result?: GitTargetDiff; - }; -}; - -export type GitTargetDiffResponse = GitTargetDiffResponses[keyof GitTargetDiffResponses]; - -export type GitPullData = { - body: { - /** - * Branch to pull from - */ - branch?: string; - /** - * Force pull even if there are conflicts - */ - force?: boolean; - }; - path?: never; - query?: never; - url: '/git/pull'; -}; - -export type GitPullErrors = { - /** - * Error pulling from remote - */ - 400: ErrorResponse & { - error?: CommonError; - }; -}; - -export type GitPullError = GitPullErrors[keyof GitPullErrors]; - -export type GitPullResponses = { - /** - * Successful operation - */ - 200: SuccessResponse & { - result?: unknown; - }; -}; - -export type GitPullResponse = GitPullResponses[keyof GitPullResponses]; - -export type GitDiscardData = { - body: { - /** - * Paths of files to discard changes - */ - paths?: Array; - }; - path?: never; - query?: never; - url: '/git/discard'; -}; - -export type GitDiscardErrors = { - /** - * Error discarding changes - */ - 400: ErrorResponse & { - error?: CommonError; - }; -}; - -export type GitDiscardError = GitDiscardErrors[keyof GitDiscardErrors]; - -export type GitDiscardResponses = { - /** - * Successful operation - */ - 200: SuccessResponse & { - result?: { - paths?: Array; - }; - }; -}; - -export type GitDiscardResponse = GitDiscardResponses[keyof GitDiscardResponses]; - -export type GitCommitData = { - body: { - /** - * Paths of files to commit - */ - paths?: Array; - /** - * Commit message - */ - message: string; - /** - * Whether to push the commit immediately - */ - push?: boolean; - }; - path?: never; - query?: never; - url: '/git/commit'; -}; - -export type GitCommitErrors = { - /** - * Error committing changes - */ - 400: ErrorResponse & { - error?: CommonError; - }; -}; - -export type GitCommitError = GitCommitErrors[keyof GitCommitErrors]; - -export type GitCommitResponses = { - /** - * Successful operation - */ - 200: SuccessResponse & { - result?: { - /** - * ID of the shell process - */ - shellId: string; - }; - }; -}; - -export type GitCommitResponse = GitCommitResponses[keyof GitCommitResponses]; - -export type GitPushData = { - body: { - [key: string]: unknown; - }; - path?: never; - query?: never; - url: '/git/push'; -}; - -export type GitPushErrors = { - /** - * Error pushing changes - */ - 400: ErrorResponse & { - error?: CommonError; - }; -}; - -export type GitPushError = GitPushErrors[keyof GitPushErrors]; - -export type GitPushResponses = { - /** - * Successful operation - */ - 200: SuccessResponse & { - result?: unknown; - }; -}; - -export type GitPushResponse = GitPushResponses[keyof GitPushResponses]; - -export type GitPushToRemoteData = { - body: { - /** - * URL of the remote repository - */ - url: string; - /** - * Branch to push to - */ - branch: string; - /** - * Whether to squash all commits into one - */ - squashAllCommits?: boolean; - }; - path?: never; - query?: never; - url: '/git/pushToRemote'; -}; - -export type GitPushToRemoteErrors = { - /** - * Error pushing to remote - */ - 400: ErrorResponse & { - error?: CommonError; - }; -}; - -export type GitPushToRemoteError = GitPushToRemoteErrors[keyof GitPushToRemoteErrors]; - -export type GitPushToRemoteResponses = { - /** - * Successful operation - */ - 200: SuccessResponse & { - result?: unknown; - }; -}; - -export type GitPushToRemoteResponse = GitPushToRemoteResponses[keyof GitPushToRemoteResponses]; - -export type GitRenameBranchData = { - body: { - /** - * Current branch name - */ - oldBranch: string; - /** - * New branch name - */ - newBranch: string; - }; - path?: never; - query?: never; - url: '/git/renameBranch'; -}; - -export type GitRenameBranchErrors = { - /** - * Error renaming branch - */ - 400: ErrorResponse & { - error?: CommonError; - }; -}; - -export type GitRenameBranchError = GitRenameBranchErrors[keyof GitRenameBranchErrors]; - -export type GitRenameBranchResponses = { - /** - * Successful operation - */ - 200: SuccessResponse & { - result?: unknown; - }; -}; - -export type GitRenameBranchResponse = GitRenameBranchResponses[keyof GitRenameBranchResponses]; - -export type GitRemoteContentData = { - body: GitRemoteParams; - path?: never; - query?: never; - url: '/git/remoteContent'; -}; - -export type GitRemoteContentErrors = { - /** - * Error retrieving remote content - */ - 400: ErrorResponse & { - error?: CommonError; - }; -}; - -export type GitRemoteContentError = GitRemoteContentErrors[keyof GitRemoteContentErrors]; - -export type GitRemoteContentResponses = { - /** - * Successful operation - */ - 200: SuccessResponse & { - result?: { - /** - * Content of the file - */ - content: string; - }; - }; -}; - -export type GitRemoteContentResponse = GitRemoteContentResponses[keyof GitRemoteContentResponses]; - -export type GitDiffStatusData = { - body: GitDiffStatusParams; - path?: never; - query?: never; - url: '/git/diffStatus'; -}; - -export type GitDiffStatusErrors = { - /** - * Error retrieving diff status - */ - 400: ErrorResponse & { - error?: CommonError; - }; -}; - -export type GitDiffStatusError = GitDiffStatusErrors[keyof GitDiffStatusErrors]; - -export type GitDiffStatusResponses = { - /** - * Successful operation - */ - 200: SuccessResponse & { - result?: GitDiffStatusResult; - }; -}; - -export type GitDiffStatusResponse = GitDiffStatusResponses[keyof GitDiffStatusResponses]; - -export type GitResetLocalWithRemoteData = { - body: { - [key: string]: unknown; - }; - path?: never; - query?: never; - url: '/git/resetLocalWithRemote'; -}; - -export type GitResetLocalWithRemoteErrors = { - /** - * Error resetting local with remote - */ - 400: ErrorResponse & { - error?: CommonError; - }; -}; - -export type GitResetLocalWithRemoteError = GitResetLocalWithRemoteErrors[keyof GitResetLocalWithRemoteErrors]; - -export type GitResetLocalWithRemoteResponses = { - /** - * Successful operation - */ - 200: SuccessResponse & { - result?: unknown; - }; -}; - -export type GitResetLocalWithRemoteResponse = GitResetLocalWithRemoteResponses[keyof GitResetLocalWithRemoteResponses]; - -export type GitCheckoutInitialBranchData = { - body: { - [key: string]: unknown; - }; - path?: never; - query?: never; - url: '/git/checkoutInitialBranch'; -}; - -export type GitCheckoutInitialBranchErrors = { - /** - * Error checking out initial branch - */ - 400: ErrorResponse & { - error?: CommonError; - }; -}; - -export type GitCheckoutInitialBranchError = GitCheckoutInitialBranchErrors[keyof GitCheckoutInitialBranchErrors]; - -export type GitCheckoutInitialBranchResponses = { - /** - * Successful operation - */ - 200: SuccessResponse & { - result?: unknown; - }; -}; - -export type GitCheckoutInitialBranchResponse = GitCheckoutInitialBranchResponses[keyof GitCheckoutInitialBranchResponses]; - -export type GitTransposeLinesData = { - body: Array<{ - /** - * Git commit SHA - */ - sha: string; - /** - * Path to the file - */ - path: string; - /** - * Line number to transpose - */ - line: number; - }>; - path?: never; - query?: never; - url: '/git/transposeLines'; -}; - -export type GitTransposeLinesErrors = { - /** - * Error transposing lines - */ - 400: ErrorResponse & { - error?: CommonError; - }; -}; - -export type GitTransposeLinesError = GitTransposeLinesErrors[keyof GitTransposeLinesErrors]; - -export type GitTransposeLinesResponses = { - /** - * Successful operation - */ - 200: SuccessResponse & { - result?: Array<{ - path: string; - line: number; - } | unknown>; - }; -}; - -export type GitTransposeLinesResponse = GitTransposeLinesResponses[keyof GitTransposeLinesResponses]; \ No newline at end of file diff --git a/src/api-clients/client-rest-setup/client.gen.ts b/src/api-clients/client-rest-setup/client.gen.ts deleted file mode 100644 index 1822a95..0000000 --- a/src/api-clients/client-rest-setup/client.gen.ts +++ /dev/null @@ -1,5 +0,0 @@ -// This file is auto-generated by @hey-api/openapi-ts - -import { createClient, createConfig } from '@hey-api/client-fetch'; - -export const client = createClient(createConfig()); \ No newline at end of file diff --git a/src/api-clients/client-rest-setup/index.ts b/src/api-clients/client-rest-setup/index.ts deleted file mode 100644 index e64537d..0000000 --- a/src/api-clients/client-rest-setup/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -// This file is auto-generated by @hey-api/openapi-ts -export * from './types.gen'; -export * from './sdk.gen'; \ No newline at end of file diff --git a/src/api-clients/client-rest-setup/sdk.gen.ts b/src/api-clients/client-rest-setup/sdk.gen.ts deleted file mode 100644 index 1a3ae61..0000000 --- a/src/api-clients/client-rest-setup/sdk.gen.ts +++ /dev/null @@ -1,119 +0,0 @@ -// This file is auto-generated by @hey-api/openapi-ts - -import type { Options as ClientOptions, TDataShape, Client } from '@hey-api/client-fetch'; -import type { SetupGetData, SetupGetResponse, SetupGetError, SetupSkipStepData, SetupSkipStepResponse, SetupSkipStepError, SetupSkipAllData, SetupSkipAllResponse, SetupSkipAllError, SetupDisableData, SetupDisableResponse, SetupDisableError, SetupEnableData, SetupEnableResponse, SetupEnableError, SetupInitData, SetupInitResponse, SetupInitError, SetupSetStepData, SetupSetStepResponse, SetupSetStepError } from './types.gen'; -import { client as _heyApiClient } from './client.gen'; - -export type Options = ClientOptions & { - /** - * You can provide a client instance returned by `createClient()` instead of - * individual options. This might be also useful if you want to implement a - * custom client. - */ - client?: Client; -}; - -/** - * Get setup progress - * Retrieve the current setup progress status - */ -export const setupGet = (options: Options) => { - return (options.client ?? _heyApiClient).post({ - url: '/setup/get', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options?.headers - } - }); -}; - -/** - * Skip setup step - * Skip a specific step in the setup process - */ -export const setupSkipStep = (options: Options) => { - return (options.client ?? _heyApiClient).post({ - url: '/setup/skip', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options?.headers - } - }); -}; - -/** - * Skip all setup steps - * Skip all remaining steps in the setup process - */ -export const setupSkipAll = (options: Options) => { - return (options.client ?? _heyApiClient).post({ - url: '/setup/skipAll', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options?.headers - } - }); -}; - -/** - * Disable setup - * Disable the setup process - */ -export const setupDisable = (options: Options) => { - return (options.client ?? _heyApiClient).post({ - url: '/setup/disable', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options?.headers - } - }); -}; - -/** - * Enable setup - * Enable the setup process - */ -export const setupEnable = (options: Options) => { - return (options.client ?? _heyApiClient).post({ - url: '/setup/enable', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options?.headers - } - }); -}; - -/** - * Initialize setup - * Initialize the setup process - */ -export const setupInit = (options: Options) => { - return (options.client ?? _heyApiClient).post({ - url: '/setup/init', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options?.headers - } - }); -}; - -/** - * Set current setup step - * Set the current step in the setup process (used for restarting) - */ -export const setupSetStep = (options: Options) => { - return (options.client ?? _heyApiClient).post({ - url: '/setup/setStep', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options?.headers - } - }); -}; \ No newline at end of file diff --git a/src/api-clients/client-rest-setup/types.gen.ts b/src/api-clients/client-rest-setup/types.gen.ts deleted file mode 100644 index 43425e7..0000000 --- a/src/api-clients/client-rest-setup/types.gen.ts +++ /dev/null @@ -1,295 +0,0 @@ -// This file is auto-generated by @hey-api/openapi-ts - -export type SuccessResponse = { - /** - * Status code for successful operations - */ - status: 0; - /** - * Result payload for the operation - */ - result: { - [key: string]: unknown; - }; -}; - -export type ErrorResponse = { - /** - * Status code for error operations - */ - status: 1; - /** - * Error details - */ - error: { - [key: string]: unknown; - }; -}; - -export type ProtocolError = { - /** - * Error code - */ - code: number; - /** - * Error message - */ - message: string; - /** - * Additional error data - */ - data?: { - [key: string]: unknown; - } | null; -}; - -/** - * Status of a setup shell step - */ -export type SetupShellStatus = 'SUCCEEDED' | 'FAILED' | 'SKIPPED'; - -export type Step = { - /** - * Name of the setup step - */ - name: string; - /** - * Command to execute for this step - */ - command: string; - /** - * ID of the shell executing the command - */ - shellId: string | null; - finishStatus: SetupShellStatus; -}; - -export type SetupProgress = { - /** - * Current state of the setup process - */ - state: 'IDLE' | 'IN_PROGRESS' | 'FINISHED' | 'STOPPED'; - /** - * List of setup steps - */ - steps: Array; - /** - * Index of the current step being executed - */ - currentStepIndex: number; -}; - -export type SetupGetData = { - body: { - [key: string]: unknown; - }; - path?: never; - query?: never; - url: '/setup/get'; -}; - -export type SetupGetErrors = { - /** - * Error retrieving setup progress - */ - 400: ErrorResponse & { - error?: ProtocolError; - }; -}; - -export type SetupGetError = SetupGetErrors[keyof SetupGetErrors]; - -export type SetupGetResponses = { - /** - * Successful operation - */ - 200: SuccessResponse & { - result?: SetupProgress; - }; -}; - -export type SetupGetResponse = SetupGetResponses[keyof SetupGetResponses]; - -export type SetupSkipStepData = { - body: { - /** - * Index of the step to skip - */ - stepIndexToSkip: number; - }; - path?: never; - query?: never; - url: '/setup/skip'; -}; - -export type SetupSkipStepErrors = { - /** - * Error skipping step - */ - 400: ErrorResponse & { - error?: ProtocolError; - }; -}; - -export type SetupSkipStepError = SetupSkipStepErrors[keyof SetupSkipStepErrors]; - -export type SetupSkipStepResponses = { - /** - * Successful operation - */ - 200: SuccessResponse & { - result?: SetupProgress; - }; -}; - -export type SetupSkipStepResponse = SetupSkipStepResponses[keyof SetupSkipStepResponses]; - -export type SetupSkipAllData = { - body: unknown; - path?: never; - query?: never; - url: '/setup/skipAll'; -}; - -export type SetupSkipAllErrors = { - /** - * Error skipping all steps - */ - 400: ErrorResponse & { - error?: ProtocolError; - }; -}; - -export type SetupSkipAllError = SetupSkipAllErrors[keyof SetupSkipAllErrors]; - -export type SetupSkipAllResponses = { - /** - * Successful operation - */ - 200: SuccessResponse & { - result?: SetupProgress; - }; -}; - -export type SetupSkipAllResponse = SetupSkipAllResponses[keyof SetupSkipAllResponses]; - -export type SetupDisableData = { - body: unknown; - path?: never; - query?: never; - url: '/setup/disable'; -}; - -export type SetupDisableErrors = { - /** - * Error disabling setup - */ - 400: ErrorResponse & { - error?: ProtocolError; - }; -}; - -export type SetupDisableError = SetupDisableErrors[keyof SetupDisableErrors]; - -export type SetupDisableResponses = { - /** - * Successful operation - */ - 200: SuccessResponse & { - result?: SetupProgress; - }; -}; - -export type SetupDisableResponse = SetupDisableResponses[keyof SetupDisableResponses]; - -export type SetupEnableData = { - body: unknown; - path?: never; - query?: never; - url: '/setup/enable'; -}; - -export type SetupEnableErrors = { - /** - * Error enabling setup - */ - 400: ErrorResponse & { - error?: ProtocolError; - }; -}; - -export type SetupEnableError = SetupEnableErrors[keyof SetupEnableErrors]; - -export type SetupEnableResponses = { - /** - * Successful operation - */ - 200: SuccessResponse & { - result?: SetupProgress; - }; -}; - -export type SetupEnableResponse = SetupEnableResponses[keyof SetupEnableResponses]; - -export type SetupInitData = { - body: unknown; - path?: never; - query?: never; - url: '/setup/init'; -}; - -export type SetupInitErrors = { - /** - * Error initializing setup - */ - 400: ErrorResponse & { - error?: ProtocolError; - }; -}; - -export type SetupInitError = SetupInitErrors[keyof SetupInitErrors]; - -export type SetupInitResponses = { - /** - * Successful operation - */ - 200: SuccessResponse & { - result?: SetupProgress; - }; -}; - -export type SetupInitResponse = SetupInitResponses[keyof SetupInitResponses]; - -export type SetupSetStepData = { - body: { - /** - * Index of the step to set as current - */ - stepIndex: number; - }; - path?: never; - query?: never; - url: '/setup/setStep'; -}; - -export type SetupSetStepErrors = { - /** - * Error setting current step - */ - 400: ErrorResponse & { - error?: ProtocolError; - }; -}; - -export type SetupSetStepError = SetupSetStepErrors[keyof SetupSetStepErrors]; - -export type SetupSetStepResponses = { - /** - * Successful operation - */ - 200: SuccessResponse & { - result?: SetupProgress; - }; -}; - -export type SetupSetStepResponse = SetupSetStepResponses[keyof SetupSetStepResponses]; \ No newline at end of file diff --git a/src/api-clients/client-rest-shell/client.gen.ts b/src/api-clients/client-rest-shell/client.gen.ts deleted file mode 100644 index 1822a95..0000000 --- a/src/api-clients/client-rest-shell/client.gen.ts +++ /dev/null @@ -1,5 +0,0 @@ -// This file is auto-generated by @hey-api/openapi-ts - -import { createClient, createConfig } from '@hey-api/client-fetch'; - -export const client = createClient(createConfig()); \ No newline at end of file diff --git a/src/api-clients/client-rest-shell/index.ts b/src/api-clients/client-rest-shell/index.ts deleted file mode 100644 index e64537d..0000000 --- a/src/api-clients/client-rest-shell/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -// This file is auto-generated by @hey-api/openapi-ts -export * from './types.gen'; -export * from './sdk.gen'; \ No newline at end of file diff --git a/src/api-clients/client-rest-shell/sdk.gen.ts b/src/api-clients/client-rest-shell/sdk.gen.ts deleted file mode 100644 index 319634a..0000000 --- a/src/api-clients/client-rest-shell/sdk.gen.ts +++ /dev/null @@ -1,149 +0,0 @@ -// This file is auto-generated by @hey-api/openapi-ts - -import type { Options as ClientOptions, TDataShape, Client } from '@hey-api/client-fetch'; -import type { ShellCreateData, ShellCreateResponse, ShellCreateError, ShellInData, ShellInResponse, ShellInError, ShellListData, ShellListResponse, ShellListError, ShellOpenData, ShellOpenResponse, ShellOpenError, ShellCloseData, ShellCloseResponse, ShellCloseError, ShellRestartData, ShellRestartResponse, ShellRestartError, ShellTerminateData, ShellTerminateResponse, ShellTerminateError, ShellResizeData, ShellResizeResponse, ShellResizeError, ShellRenameData, ShellRenameResponse, ShellRenameError } from './types.gen'; -import { client as _heyApiClient } from './client.gen'; - -export type Options = ClientOptions & { - /** - * You can provide a client instance returned by `createClient()` instead of - * individual options. This might be also useful if you want to implement a - * custom client. - */ - client?: Client; -}; - -/** - * Create a new shell - * Creates a new terminal or command shell - */ -export const shellCreate = (options: Options) => { - return (options.client ?? _heyApiClient).post({ - url: '/shell/create', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options?.headers - } - }); -}; - -/** - * Send input to shell - * Sends user input to an active shell - */ -export const shellIn = (options: Options) => { - return (options.client ?? _heyApiClient).post({ - url: '/shell/in', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options?.headers - } - }); -}; - -/** - * List all shells - * Retrieves a list of all available shells - */ -export const shellList = (options: Options) => { - return (options.client ?? _heyApiClient).post({ - url: '/shell/list', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options?.headers - } - }); -}; - -/** - * Open an existing shell - * Opens an existing shell and retrieves its buffer - */ -export const shellOpen = (options: Options) => { - return (options.client ?? _heyApiClient).post({ - url: '/shell/open', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options?.headers - } - }); -}; - -/** - * Close a shell - * Closes a shell without terminating the underlying process - */ -export const shellClose = (options: Options) => { - return (options.client ?? _heyApiClient).post({ - url: '/shell/close', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options?.headers - } - }); -}; - -/** - * Restart a shell - * Restarts an existing shell process - */ -export const shellRestart = (options: Options) => { - return (options.client ?? _heyApiClient).post({ - url: '/shell/restart', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options?.headers - } - }); -}; - -/** - * Terminate a shell - * Terminates a shell and its underlying process - */ -export const shellTerminate = (options: Options) => { - return (options.client ?? _heyApiClient).post({ - url: '/shell/terminate', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options?.headers - } - }); -}; - -/** - * Resize a shell - * Updates the dimensions of a shell - */ -export const shellResize = (options: Options) => { - return (options.client ?? _heyApiClient).post({ - url: '/shell/resize', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options?.headers - } - }); -}; - -/** - * Rename a shell - * Updates the name of a shell - */ -export const shellRename = (options: Options) => { - return (options.client ?? _heyApiClient).post({ - url: '/shell/rename', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options?.headers - } - }); -}; \ No newline at end of file diff --git a/src/api-clients/client-rest-shell/types.gen.ts b/src/api-clients/client-rest-shell/types.gen.ts deleted file mode 100644 index 45aa947..0000000 --- a/src/api-clients/client-rest-shell/types.gen.ts +++ /dev/null @@ -1,443 +0,0 @@ -// This file is auto-generated by @hey-api/openapi-ts - -export type SuccessResponse = { - /** - * Status code for successful operations - */ - status: 0; - /** - * Result payload for the operation - */ - result: { - [key: string]: unknown; - }; -}; - -export type ErrorResponse = { - /** - * Status code for error operations - */ - status: 1; - /** - * Error details - */ - error: { - [key: string]: unknown; - }; -}; - -/** - * Unique identifier for a shell - */ -export type ShellId = string; - -export type ShellSize = { - /** - * Number of columns in the terminal - */ - cols: number; - /** - * Number of rows in the terminal - */ - rows: number; -}; - -/** - * Type of shell process - */ -export type ShellProcessType = 'TERMINAL' | 'COMMAND'; - -/** - * Current status of the shell process - */ -export type ShellProcessStatus = 'RUNNING' | 'FINISHED' | 'ERROR' | 'KILLED' | 'RESTARTING'; - -export type BaseShellDto = { - shellId: ShellId; - /** - * Display name of the shell - */ - name: string; - status: ShellProcessStatus; - /** - * Exit code of the process if it has finished - */ - exitCode?: number | null; -}; - -export type CommandShellDto = BaseShellDto & { - /** - * Indicates this is a command shell - */ - shellType: 'COMMAND'; - /** - * The command that was executed to start this shell - */ - startCommand: string; -}; - -export type TerminalShellDto = BaseShellDto & { - /** - * Indicates this is a terminal shell - */ - shellType: 'TERMINAL'; - /** - * Username of the shell owner - */ - ownerUsername: string; - /** - * Whether this is a system shell - */ - isSystemShell: boolean; -}; - -export type ShellDto = ({ - shellType?: 'COMMAND'; -} & CommandShellDto) | ({ - shellType?: 'TERMINAL'; -} & TerminalShellDto); - -export type OpenCommandShellDto = CommandShellDto & { - /** - * Content buffer of the shell - */ - buffer: Array; -}; - -export type OpenTerminalShellDto = TerminalShellDto & { - /** - * Content buffer of the shell - */ - buffer: Array; -}; - -export type OpenShellDto = ({ - shellType?: 'COMMAND'; -} & OpenCommandShellDto) | ({ - shellType?: 'TERMINAL'; -} & OpenTerminalShellDto); - -export type CommonError = { - /** - * Error code indicating the shell is not accessible - */ - code: 'SHELL_NOT_ACCESSIBLE'; - /** - * Error message - */ - message: string; -} | { - /** - * Protocol error code - */ - code: string; - /** - * Error message - */ - message: string; -}; - -export type ShellCreateData = { - body: { - /** - * Command to execute in the shell - */ - command?: string; - /** - * Working directory for the shell - */ - cwd?: string; - size?: ShellSize; - type?: ShellProcessType; - /** - * Whether this shell is started by the editor itself to run a specific process - */ - isSystemShell?: boolean; - }; - path?: never; - query?: never; - url: '/shell/create'; -}; - -export type ShellCreateErrors = { - /** - * Error creating shell - */ - 400: ErrorResponse & { - error?: CommonError; - }; -}; - -export type ShellCreateError = ShellCreateErrors[keyof ShellCreateErrors]; - -export type ShellCreateResponses = { - /** - * Successful operation - */ - 200: SuccessResponse & { - result?: OpenShellDto; - }; -}; - -export type ShellCreateResponse = ShellCreateResponses[keyof ShellCreateResponses]; - -export type ShellInData = { - body: { - shellId: ShellId; - /** - * Input to send to the shell - */ - input: string; - size: ShellSize; - }; - path?: never; - query?: never; - url: '/shell/in'; -}; - -export type ShellInErrors = { - /** - * Error sending input to shell - */ - 400: ErrorResponse & { - error?: CommonError; - }; -}; - -export type ShellInError = ShellInErrors[keyof ShellInErrors]; - -export type ShellInResponses = { - /** - * Successful operation - */ - 200: SuccessResponse & { - result?: unknown; - }; -}; - -export type ShellInResponse = ShellInResponses[keyof ShellInResponses]; - -export type ShellListData = { - body: { - [key: string]: unknown; - }; - path?: never; - query?: never; - url: '/shell/list'; -}; - -export type ShellListErrors = { - /** - * Error listing shells - */ - 400: ErrorResponse & { - error?: CommonError; - }; -}; - -export type ShellListError = ShellListErrors[keyof ShellListErrors]; - -export type ShellListResponses = { - /** - * Successful operation - */ - 200: SuccessResponse & { - result?: { - shells: Array; - }; - }; -}; - -export type ShellListResponse = ShellListResponses[keyof ShellListResponses]; - -export type ShellOpenData = { - body: { - shellId: ShellId; - size: ShellSize; - }; - path?: never; - query?: never; - url: '/shell/open'; -}; - -export type ShellOpenErrors = { - /** - * Error opening shell - */ - 400: ErrorResponse & { - error?: CommonError; - }; -}; - -export type ShellOpenError = ShellOpenErrors[keyof ShellOpenErrors]; - -export type ShellOpenResponses = { - /** - * Successful operation - */ - 200: SuccessResponse & { - result?: OpenShellDto; - }; -}; - -export type ShellOpenResponse = ShellOpenResponses[keyof ShellOpenResponses]; - -export type ShellCloseData = { - body: { - shellId: ShellId; - }; - path?: never; - query?: never; - url: '/shell/close'; -}; - -export type ShellCloseErrors = { - /** - * Error closing shell - */ - 400: ErrorResponse & { - error?: CommonError; - }; -}; - -export type ShellCloseError = ShellCloseErrors[keyof ShellCloseErrors]; - -export type ShellCloseResponses = { - /** - * Successful operation - */ - 200: SuccessResponse & { - result?: unknown; - }; -}; - -export type ShellCloseResponse = ShellCloseResponses[keyof ShellCloseResponses]; - -export type ShellRestartData = { - body: { - shellId: ShellId; - }; - path?: never; - query?: never; - url: '/shell/restart'; -}; - -export type ShellRestartErrors = { - /** - * Error restarting shell - */ - 400: ErrorResponse & { - error?: CommonError; - }; -}; - -export type ShellRestartError = ShellRestartErrors[keyof ShellRestartErrors]; - -export type ShellRestartResponses = { - /** - * Successful operation - */ - 200: SuccessResponse & { - result?: unknown; - }; -}; - -export type ShellRestartResponse = ShellRestartResponses[keyof ShellRestartResponses]; - -export type ShellTerminateData = { - body: { - shellId: ShellId; - }; - path?: never; - query?: never; - url: '/shell/terminate'; -}; - -export type ShellTerminateErrors = { - /** - * Error terminating shell - */ - 400: ErrorResponse & { - error?: CommonError; - }; -}; - -export type ShellTerminateError = ShellTerminateErrors[keyof ShellTerminateErrors]; - -export type ShellTerminateResponses = { - /** - * Successful operation - */ - 200: SuccessResponse & { - result?: ShellDto; - }; -}; - -export type ShellTerminateResponse = ShellTerminateResponses[keyof ShellTerminateResponses]; - -export type ShellResizeData = { - body: { - shellId: ShellId; - size: ShellSize; - }; - path?: never; - query?: never; - url: '/shell/resize'; -}; - -export type ShellResizeErrors = { - /** - * Error resizing shell - */ - 400: ErrorResponse & { - error?: CommonError; - }; -}; - -export type ShellResizeError = ShellResizeErrors[keyof ShellResizeErrors]; - -export type ShellResizeResponses = { - /** - * Successful operation - */ - 200: SuccessResponse & { - result?: unknown; - }; -}; - -export type ShellResizeResponse = ShellResizeResponses[keyof ShellResizeResponses]; - -export type ShellRenameData = { - body: { - shellId: ShellId; - /** - * New name for the shell - */ - name: string; - }; - path?: never; - query?: never; - url: '/shell/rename'; -}; - -export type ShellRenameErrors = { - /** - * Error renaming shell - */ - 400: ErrorResponse & { - error?: CommonError; - }; -}; - -export type ShellRenameError = ShellRenameErrors[keyof ShellRenameErrors]; - -export type ShellRenameResponses = { - /** - * Successful operation - */ - 200: SuccessResponse & { - result?: unknown; - }; -}; - -export type ShellRenameResponse = ShellRenameResponses[keyof ShellRenameResponses]; \ No newline at end of file diff --git a/src/api-clients/client-rest-system/client.gen.ts b/src/api-clients/client-rest-system/client.gen.ts deleted file mode 100644 index 1822a95..0000000 --- a/src/api-clients/client-rest-system/client.gen.ts +++ /dev/null @@ -1,5 +0,0 @@ -// This file is auto-generated by @hey-api/openapi-ts - -import { createClient, createConfig } from '@hey-api/client-fetch'; - -export const client = createClient(createConfig()); \ No newline at end of file diff --git a/src/api-clients/client-rest-system/index.ts b/src/api-clients/client-rest-system/index.ts deleted file mode 100644 index e64537d..0000000 --- a/src/api-clients/client-rest-system/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -// This file is auto-generated by @hey-api/openapi-ts -export * from './types.gen'; -export * from './sdk.gen'; \ No newline at end of file diff --git a/src/api-clients/client-rest-system/sdk.gen.ts b/src/api-clients/client-rest-system/sdk.gen.ts deleted file mode 100644 index d1f450e..0000000 --- a/src/api-clients/client-rest-system/sdk.gen.ts +++ /dev/null @@ -1,59 +0,0 @@ -// This file is auto-generated by @hey-api/openapi-ts - -import type { Options as ClientOptions, TDataShape, Client } from '@hey-api/client-fetch'; -import type { SystemUpdateData, SystemUpdateResponse, SystemUpdateError, SystemHibernateData, SystemHibernateResponse, SystemHibernateError, SystemMetricsData, SystemMetricsResponse, SystemMetricsError } from './types.gen'; -import { client as _heyApiClient } from './client.gen'; - -export type Options = ClientOptions & { - /** - * You can provide a client instance returned by `createClient()` instead of - * individual options. This might be also useful if you want to implement a - * custom client. - */ - client?: Client; -}; - -/** - * Update system - * Update the sandbox system - */ -export const systemUpdate = (options: Options) => { - return (options.client ?? _heyApiClient).post({ - url: '/system/update', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options?.headers - } - }); -}; - -/** - * Hibernate system - * Put the sandbox system into hibernation mode - */ -export const systemHibernate = (options: Options) => { - return (options.client ?? _heyApiClient).post({ - url: '/system/hibernate', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options?.headers - } - }); -}; - -/** - * Get system metrics - * Retrieve current system metrics including CPU, memory and storage usage - */ -export const systemMetrics = (options: Options) => { - return (options.client ?? _heyApiClient).post({ - url: '/system/metrics', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options?.headers - } - }); -}; \ No newline at end of file diff --git a/src/api-clients/client-rest-system/types.gen.ts b/src/api-clients/client-rest-system/types.gen.ts deleted file mode 100644 index f23b7f1..0000000 --- a/src/api-clients/client-rest-system/types.gen.ts +++ /dev/null @@ -1,207 +0,0 @@ -// This file is auto-generated by @hey-api/openapi-ts - -export type SuccessResponse = { - /** - * Status code for successful operations - */ - status: 0; - /** - * Result payload for the operation - */ - result: { - [key: string]: unknown; - }; -}; - -export type ErrorResponse = { - /** - * Status code for error operations - */ - status: 1; - /** - * Error details - */ - error: { - [key: string]: unknown; - }; -}; - -export type SystemError = { - /** - * Error code - */ - code: number; - /** - * Error message - */ - message: string; - /** - * Additional error data - */ - data?: { - [key: string]: unknown; - } | null; -}; - -export type SystemMetricsStatus = { - cpu: { - /** - * Number of CPU cores - */ - cores: number; - /** - * Used CPU resources - */ - used: number; - /** - * Configured CPU resources - */ - configured: number; - }; - memory: { - /** - * Used memory in bytes - */ - used: number; - /** - * Total available memory in bytes - */ - total: number; - /** - * Configured memory limit in bytes - */ - configured: number; - }; - storage: { - /** - * Used storage in bytes - */ - used: number; - /** - * Total available storage in bytes - */ - total: number; - /** - * Configured storage limit in bytes - */ - configured: number; - }; -}; - -export type InitStatus = { - /** - * Status message - */ - message: string; - /** - * Whether the status represents an error - */ - isError?: boolean | null; - /** - * Current progress (0-100) - */ - progress: number; - /** - * Next progress target (0-100) - */ - nextProgress: number; - /** - * Standard output from the initialization process - */ - stdout?: string | null; -}; - -export type SystemUpdateData = { - body: { - [key: string]: unknown; - }; - path?: never; - query?: never; - url: '/system/update'; -}; - -export type SystemUpdateErrors = { - /** - * Error updating system - */ - 400: ErrorResponse & { - error?: SystemError; - }; -}; - -export type SystemUpdateError = SystemUpdateErrors[keyof SystemUpdateErrors]; - -export type SystemUpdateResponses = { - /** - * Successful operation - */ - 200: SuccessResponse & { - result?: { - [key: string]: unknown; - }; - }; -}; - -export type SystemUpdateResponse = SystemUpdateResponses[keyof SystemUpdateResponses]; - -export type SystemHibernateData = { - body: { - [key: string]: unknown; - }; - path?: never; - query?: never; - url: '/system/hibernate'; -}; - -export type SystemHibernateErrors = { - /** - * Error hibernating system - */ - 400: ErrorResponse & { - error?: SystemError; - }; -}; - -export type SystemHibernateError = SystemHibernateErrors[keyof SystemHibernateErrors]; - -export type SystemHibernateResponses = { - /** - * Successful operation - */ - 200: SuccessResponse & { - result?: unknown; - }; -}; - -export type SystemHibernateResponse = SystemHibernateResponses[keyof SystemHibernateResponses]; - -export type SystemMetricsData = { - body: { - [key: string]: unknown; - }; - path?: never; - query?: never; - url: '/system/metrics'; -}; - -export type SystemMetricsErrors = { - /** - * Error retrieving system metrics - */ - 400: ErrorResponse & { - error?: SystemError; - }; -}; - -export type SystemMetricsError = SystemMetricsErrors[keyof SystemMetricsErrors]; - -export type SystemMetricsResponses = { - /** - * Successful operation - */ - 200: SuccessResponse & { - result?: SystemMetricsStatus; - }; -}; - -export type SystemMetricsResponse = SystemMetricsResponses[keyof SystemMetricsResponses]; \ No newline at end of file diff --git a/src/api-clients/client-rest-task/client.gen.ts b/src/api-clients/client-rest-task/client.gen.ts deleted file mode 100644 index 1822a95..0000000 --- a/src/api-clients/client-rest-task/client.gen.ts +++ /dev/null @@ -1,5 +0,0 @@ -// This file is auto-generated by @hey-api/openapi-ts - -import { createClient, createConfig } from '@hey-api/client-fetch'; - -export const client = createClient(createConfig()); \ No newline at end of file diff --git a/src/api-clients/client-rest-task/index.ts b/src/api-clients/client-rest-task/index.ts deleted file mode 100644 index e64537d..0000000 --- a/src/api-clients/client-rest-task/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -// This file is auto-generated by @hey-api/openapi-ts -export * from './types.gen'; -export * from './sdk.gen'; \ No newline at end of file diff --git a/src/api-clients/client-rest-task/sdk.gen.ts b/src/api-clients/client-rest-task/sdk.gen.ts deleted file mode 100644 index 133e8f8..0000000 --- a/src/api-clients/client-rest-task/sdk.gen.ts +++ /dev/null @@ -1,149 +0,0 @@ -// This file is auto-generated by @hey-api/openapi-ts - -import type { Options as ClientOptions, TDataShape, Client } from '@hey-api/client-fetch'; -import type { TaskListData, TaskListResponse, TaskListError, TaskRunData, TaskRunResponse, TaskRunError, TaskRunCommandData, TaskRunCommandResponse, TaskRunCommandError, TaskStopData, TaskStopResponse, TaskStopError, TaskCreateData, TaskCreateResponse, TaskCreateError, TaskUpdateData, TaskUpdateResponse, TaskUpdateError, TaskSaveToConfigData, TaskSaveToConfigResponse, TaskSaveToConfigError, TaskGenerateConfigData, TaskGenerateConfigResponse, TaskGenerateConfigError, TaskCreateSetupTasksData, TaskCreateSetupTasksResponse, TaskCreateSetupTasksError } from './types.gen'; -import { client as _heyApiClient } from './client.gen'; - -export type Options = ClientOptions & { - /** - * You can provide a client instance returned by `createClient()` instead of - * individual options. This might be also useful if you want to implement a - * custom client. - */ - client?: Client; -}; - -/** - * List tasks - * Retrieve a list of all configured tasks - */ -export const taskList = (options: Options) => { - return (options.client ?? _heyApiClient).post({ - url: '/task/list', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options?.headers - } - }); -}; - -/** - * Run task - * Start execution of a task by ID - */ -export const taskRun = (options: Options) => { - return (options.client ?? _heyApiClient).post({ - url: '/task/run', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options?.headers - } - }); -}; - -/** - * Run command - * Run a shell command directly, optionally saving it as a task - */ -export const taskRunCommand = (options: Options) => { - return (options.client ?? _heyApiClient).post({ - url: '/task/runCommand', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options?.headers - } - }); -}; - -/** - * Stop task - * Stop execution of a running task - */ -export const taskStop = (options: Options) => { - return (options.client ?? _heyApiClient).post({ - url: '/task/stop', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options?.headers - } - }); -}; - -/** - * Create task - * Create a new task configuration - */ -export const taskCreate = (options: Options) => { - return (options.client ?? _heyApiClient).post({ - url: '/task/create', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options?.headers - } - }); -}; - -/** - * Update task - * Update an existing task configuration - */ -export const taskUpdate = (options: Options) => { - return (options.client ?? _heyApiClient).post({ - url: '/task/update', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options?.headers - } - }); -}; - -/** - * Save task to config - * Save a runtime task to the configuration file - */ -export const taskSaveToConfig = (options: Options) => { - return (options.client ?? _heyApiClient).post({ - url: '/task/saveToConfig', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options?.headers - } - }); -}; - -/** - * Generate task config - * Generate a configuration file from current tasks - */ -export const taskGenerateConfig = (options: Options) => { - return (options.client ?? _heyApiClient).post({ - url: '/task/generateConfig', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options?.headers - } - }); -}; - -/** - * Create setup tasks - * Create tasks that run during sandbox setup - */ -export const taskCreateSetupTasks = (options: Options) => { - return (options.client ?? _heyApiClient).post({ - url: '/task/createSetupTasks', - ...options, - headers: { - 'Content-Type': 'application/json', - ...options?.headers - } - }); -}; \ No newline at end of file diff --git a/src/api-clients/client-rest-task/types.gen.ts b/src/api-clients/client-rest-task/types.gen.ts deleted file mode 100644 index 012bd28..0000000 --- a/src/api-clients/client-rest-task/types.gen.ts +++ /dev/null @@ -1,507 +0,0 @@ -// This file is auto-generated by @hey-api/openapi-ts - -export type SuccessResponse = { - /** - * Status code for successful operations - */ - status: 0; - /** - * Result payload for the operation - */ - result: { - [key: string]: unknown; - }; -}; - -export type ErrorResponse = { - /** - * Status code for error operations - */ - status: 1; - /** - * Error details - */ - error: { - [key: string]: unknown; - }; -}; - -export type CommonError = { - /** - * Error code - */ - code: number; - /** - * Error message - */ - message?: string; - /** - * Additional error data - */ - data?: { - [key: string]: unknown; - } | null; -}; - -export type TaskError = { - /** - * CONFIG_FILE_ALREADY_EXISTS error code - */ - code: 600; - /** - * Error message - */ - message: string; -} | { - /** - * TASK_NOT_FOUND error code - */ - code: 601; - /** - * Error message - */ - message: string; -} | { - /** - * COMMAND_ALREADY_CONFIGURED error code - */ - code: 602; - /** - * Error message - */ - message: string; -} | ({ - code?: 'CommonError'; -} & CommonError); - -export type TaskDefinitionDto = { - /** - * Name of the task - */ - name: string; - /** - * Command to run for the task - */ - command: string; - /** - * Whether the task should run when the sandbox starts - */ - runAtStart?: boolean | null; - preview?: { - /** - * Port to preview from this task - */ - port?: number | null; - /** - * Type of PR link to use - */ - 'pr-link'?: 'direct' | 'redirect' | 'devtool'; - } | null; -}; - -export type CommandShellDto = { - /** - * ID of the shell command - */ - id: string; - /** - * Command being executed - */ - command: string; - /** - * Current status of the shell command - */ - status: 'initializing' | 'running' | 'stopped' | 'error'; - /** - * Current output of the command - */ - output: string; -}; - -export type Port = { - /** - * Port number - */ - port: number; - /** - * Hostname the port is bound to - */ - hostname: string; - /** - * Current status of the port - */ - status: 'open' | 'closed'; - /** - * ID of the task that opened this port - */ - taskId?: string | null; -}; - -export type TaskDto = TaskDefinitionDto & { - /** - * Unique ID of the task - */ - id: string; - /** - * Whether this task is unconfigured (not saved in config) - */ - unconfigured?: boolean | null; - shell: CommandShellDto | null; - /** - * Ports opened by this task - */ - ports: Array; -}; - -export type TaskListDto = { - /** - * Map of task IDs to task objects - */ - tasks: { - [key: string]: TaskDto; - }; - /** - * Tasks that run during sandbox setup - */ - setupTasks: Array; - /** - * Validation errors in the task configuration - */ - validationErrors: Array; -}; - -export type TaskListData = { - body: { - [key: string]: unknown; - }; - path?: never; - query?: never; - url: '/task/list'; -}; - -export type TaskListErrors = { - /** - * Error retrieving task list - */ - 400: ErrorResponse & { - error?: CommonError; - }; -}; - -export type TaskListError = TaskListErrors[keyof TaskListErrors]; - -export type TaskListResponses = { - /** - * Successful operation - */ - 200: SuccessResponse & { - result?: TaskListDto; - }; -}; - -export type TaskListResponse = TaskListResponses[keyof TaskListResponses]; - -export type TaskRunData = { - body: { - /** - * ID of the task to run - */ - taskId: string; - }; - path?: never; - query?: never; - url: '/task/run'; -}; - -export type TaskRunErrors = { - /** - * Error running task - */ - 400: ErrorResponse & { - error?: TaskError; - }; -}; - -export type TaskRunError = TaskRunErrors[keyof TaskRunErrors]; - -export type TaskRunResponses = { - /** - * Successful operation - */ - 200: SuccessResponse & { - result?: TaskDto; - }; -}; - -export type TaskRunResponse = TaskRunResponses[keyof TaskRunResponses]; - -export type TaskRunCommandData = { - body: { - /** - * Command to run - */ - command: string; - /** - * Optional name for the task - */ - name?: string | null; - /** - * Whether to save this command as a task in the config - */ - saveToConfig?: boolean | null; - }; - path?: never; - query?: never; - url: '/task/runCommand'; -}; - -export type TaskRunCommandErrors = { - /** - * Error running command - */ - 400: ErrorResponse & { - error?: TaskError; - }; -}; - -export type TaskRunCommandError = TaskRunCommandErrors[keyof TaskRunCommandErrors]; - -export type TaskRunCommandResponses = { - /** - * Successful operation - */ - 200: SuccessResponse & { - result?: TaskDto; - }; -}; - -export type TaskRunCommandResponse = TaskRunCommandResponses[keyof TaskRunCommandResponses]; - -export type TaskStopData = { - body: { - /** - * ID of the task to stop - */ - taskId: string; - }; - path?: never; - query?: never; - url: '/task/stop'; -}; - -export type TaskStopErrors = { - /** - * Error stopping task - */ - 400: ErrorResponse & { - error?: TaskError; - }; -}; - -export type TaskStopError = TaskStopErrors[keyof TaskStopErrors]; - -export type TaskStopResponses = { - /** - * Successful operation - */ - 200: SuccessResponse & { - result?: TaskDto | unknown; - }; -}; - -export type TaskStopResponse = TaskStopResponses[keyof TaskStopResponses]; - -export type TaskCreateData = { - body: { - taskFields: TaskDefinitionDto; - /** - * Whether to start the task immediately after creation - */ - startTask?: boolean | null; - }; - path?: never; - query?: never; - url: '/task/create'; -}; - -export type TaskCreateErrors = { - /** - * Error creating task - */ - 400: ErrorResponse & { - error?: TaskError; - }; -}; - -export type TaskCreateError = TaskCreateErrors[keyof TaskCreateErrors]; - -export type TaskCreateResponses = { - /** - * Successful operation - */ - 200: SuccessResponse & { - result?: TaskListDto; - }; -}; - -export type TaskCreateResponse = TaskCreateResponses[keyof TaskCreateResponses]; - -export type TaskUpdateData = { - body: { - /** - * ID of the task to update - */ - taskId: string; - /** - * Fields to update in the task - */ - taskFields: { - /** - * Name of the task - */ - name?: string | null; - /** - * Command to run - */ - command?: string | null; - /** - * Whether to run the task at sandbox start - */ - runAtStart?: boolean | null; - preview?: { - /** - * Port to use for previewing the task - */ - port?: number | null; - /** - * Type of PR link to use - */ - 'pr-link'?: 'direct' | 'redirect' | 'devtool'; - } | null; - }; - }; - path?: never; - query?: never; - url: '/task/update'; -}; - -export type TaskUpdateErrors = { - /** - * Error updating task - */ - 400: ErrorResponse & { - error?: TaskError; - }; -}; - -export type TaskUpdateError = TaskUpdateErrors[keyof TaskUpdateErrors]; - -export type TaskUpdateResponses = { - /** - * Successful operation - */ - 200: SuccessResponse & { - result?: TaskDto; - }; -}; - -export type TaskUpdateResponse = TaskUpdateResponses[keyof TaskUpdateResponses]; - -export type TaskSaveToConfigData = { - body: { - /** - * ID of the task to save to config - */ - taskId: string; - }; - path?: never; - query?: never; - url: '/task/saveToConfig'; -}; - -export type TaskSaveToConfigErrors = { - /** - * Error saving task to config - */ - 400: ErrorResponse & { - error?: TaskError; - }; -}; - -export type TaskSaveToConfigError = TaskSaveToConfigErrors[keyof TaskSaveToConfigErrors]; - -export type TaskSaveToConfigResponses = { - /** - * Successful operation - */ - 200: SuccessResponse & { - result?: TaskDto; - }; -}; - -export type TaskSaveToConfigResponse = TaskSaveToConfigResponses[keyof TaskSaveToConfigResponses]; - -export type TaskGenerateConfigData = { - body: { - [key: string]: unknown; - }; - path?: never; - query?: never; - url: '/task/generateConfig'; -}; - -export type TaskGenerateConfigErrors = { - /** - * Error generating config - */ - 400: ErrorResponse & { - error?: TaskError; - }; -}; - -export type TaskGenerateConfigError = TaskGenerateConfigErrors[keyof TaskGenerateConfigErrors]; - -export type TaskGenerateConfigResponses = { - /** - * Successful operation - */ - 200: SuccessResponse & { - result?: unknown; - }; -}; - -export type TaskGenerateConfigResponse = TaskGenerateConfigResponses[keyof TaskGenerateConfigResponses]; - -export type TaskCreateSetupTasksData = { - body: { - /** - * Setup tasks to create - */ - tasks: Array; - }; - path?: never; - query?: never; - url: '/task/createSetupTasks'; -}; - -export type TaskCreateSetupTasksErrors = { - /** - * Error creating setup tasks - */ - 400: ErrorResponse & { - error?: TaskError; - }; -}; - -export type TaskCreateSetupTasksError = TaskCreateSetupTasksErrors[keyof TaskCreateSetupTasksErrors]; - -export type TaskCreateSetupTasksResponses = { - /** - * Successful operation - */ - 200: SuccessResponse & { - result?: unknown; - }; -}; - -export type TaskCreateSetupTasksResponse = TaskCreateSetupTasksResponses[keyof TaskCreateSetupTasksResponses]; \ No newline at end of file diff --git a/src/api-clients/client/client.gen.ts b/src/api-clients/client/client.gen.ts index 1822a95..eb153b1 100644 --- a/src/api-clients/client/client.gen.ts +++ b/src/api-clients/client/client.gen.ts @@ -1,5 +1,18 @@ // This file is auto-generated by @hey-api/openapi-ts -import { createClient, createConfig } from '@hey-api/client-fetch'; +import { type ClientOptions, type Config, createClient, createConfig } from './client'; +import type { ClientOptions as ClientOptions2 } from './types.gen'; -export const client = createClient(createConfig()); \ No newline at end of file +/** + * The `createClientConfig()` function will be called on client initialization + * and the returned object will become the client's initial configuration. + * + * You may want to initialize your client this way instead of calling + * `setConfig()`. This is useful for example if you're using Next.js + * to ensure your client always has the correct values. + */ +export type CreateClientConfig = (override?: Config) => Config & T>; + +export const client = createClient(createConfig({ + baseUrl: 'https://api.codesandbox.io' +})); diff --git a/src/api-clients/client/client/client.gen.ts b/src/api-clients/client/client/client.gen.ts new file mode 100644 index 0000000..a439d27 --- /dev/null +++ b/src/api-clients/client/client/client.gen.ts @@ -0,0 +1,268 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import { createSseClient } from '../core/serverSentEvents.gen'; +import type { HttpMethod } from '../core/types.gen'; +import { getValidRequestBody } from '../core/utils.gen'; +import type { + Client, + Config, + RequestOptions, + ResolvedRequestOptions, +} from './types.gen'; +import { + buildUrl, + createConfig, + createInterceptors, + getParseAs, + mergeConfigs, + mergeHeaders, + setAuthParams, +} from './utils.gen'; + +type ReqInit = Omit & { + body?: any; + headers: ReturnType; +}; + +export const createClient = (config: Config = {}): Client => { + let _config = mergeConfigs(createConfig(), config); + + const getConfig = (): Config => ({ ..._config }); + + const setConfig = (config: Config): Config => { + _config = mergeConfigs(_config, config); + return getConfig(); + }; + + const interceptors = createInterceptors< + Request, + Response, + unknown, + ResolvedRequestOptions + >(); + + const beforeRequest = async (options: RequestOptions) => { + const opts = { + ..._config, + ...options, + fetch: options.fetch ?? _config.fetch ?? globalThis.fetch, + headers: mergeHeaders(_config.headers, options.headers), + serializedBody: undefined, + }; + + if (opts.security) { + await setAuthParams({ + ...opts, + security: opts.security, + }); + } + + if (opts.requestValidator) { + await opts.requestValidator(opts); + } + + if (opts.body !== undefined && opts.bodySerializer) { + opts.serializedBody = opts.bodySerializer(opts.body); + } + + // remove Content-Type header if body is empty to avoid sending invalid requests + if (opts.body === undefined || opts.serializedBody === '') { + opts.headers.delete('Content-Type'); + } + + const url = buildUrl(opts); + + return { opts, url }; + }; + + const request: Client['request'] = async (options) => { + // @ts-expect-error + const { opts, url } = await beforeRequest(options); + const requestInit: ReqInit = { + redirect: 'follow', + ...opts, + body: getValidRequestBody(opts), + }; + + let request = new Request(url, requestInit); + + for (const fn of interceptors.request.fns) { + if (fn) { + request = await fn(request, opts); + } + } + + // fetch must be assigned here, otherwise it would throw the error: + // TypeError: Failed to execute 'fetch' on 'Window': Illegal invocation + const _fetch = opts.fetch!; + let response = await _fetch(request); + + for (const fn of interceptors.response.fns) { + if (fn) { + response = await fn(response, request, opts); + } + } + + const result = { + request, + response, + }; + + if (response.ok) { + const parseAs = + (opts.parseAs === 'auto' + ? getParseAs(response.headers.get('Content-Type')) + : opts.parseAs) ?? 'json'; + + if ( + response.status === 204 || + response.headers.get('Content-Length') === '0' + ) { + let emptyData: any; + switch (parseAs) { + case 'arrayBuffer': + case 'blob': + case 'text': + emptyData = await response[parseAs](); + break; + case 'formData': + emptyData = new FormData(); + break; + case 'stream': + emptyData = response.body; + break; + case 'json': + default: + emptyData = {}; + break; + } + return opts.responseStyle === 'data' + ? emptyData + : { + data: emptyData, + ...result, + }; + } + + let data: any; + switch (parseAs) { + case 'arrayBuffer': + case 'blob': + case 'formData': + case 'json': + case 'text': + data = await response[parseAs](); + break; + case 'stream': + return opts.responseStyle === 'data' + ? response.body + : { + data: response.body, + ...result, + }; + } + + if (parseAs === 'json') { + if (opts.responseValidator) { + await opts.responseValidator(data); + } + + if (opts.responseTransformer) { + data = await opts.responseTransformer(data); + } + } + + return opts.responseStyle === 'data' + ? data + : { + data, + ...result, + }; + } + + const textError = await response.text(); + let jsonError: unknown; + + try { + jsonError = JSON.parse(textError); + } catch { + // noop + } + + const error = jsonError ?? textError; + let finalError = error; + + for (const fn of interceptors.error.fns) { + if (fn) { + finalError = (await fn(error, response, request, opts)) as string; + } + } + + finalError = finalError || ({} as string); + + if (opts.throwOnError) { + throw finalError; + } + + // TODO: we probably want to return error and improve types + return opts.responseStyle === 'data' + ? undefined + : { + error: finalError, + ...result, + }; + }; + + const makeMethodFn = + (method: Uppercase) => (options: RequestOptions) => + request({ ...options, method }); + + const makeSseFn = + (method: Uppercase) => async (options: RequestOptions) => { + const { opts, url } = await beforeRequest(options); + return createSseClient({ + ...opts, + body: opts.body as BodyInit | null | undefined, + headers: opts.headers as unknown as Record, + method, + onRequest: async (url, init) => { + let request = new Request(url, init); + for (const fn of interceptors.request.fns) { + if (fn) { + request = await fn(request, opts); + } + } + return request; + }, + url, + }); + }; + + return { + buildUrl, + connect: makeMethodFn('CONNECT'), + delete: makeMethodFn('DELETE'), + get: makeMethodFn('GET'), + getConfig, + head: makeMethodFn('HEAD'), + interceptors, + options: makeMethodFn('OPTIONS'), + patch: makeMethodFn('PATCH'), + post: makeMethodFn('POST'), + put: makeMethodFn('PUT'), + request, + setConfig, + sse: { + connect: makeSseFn('CONNECT'), + delete: makeSseFn('DELETE'), + get: makeSseFn('GET'), + head: makeSseFn('HEAD'), + options: makeSseFn('OPTIONS'), + patch: makeSseFn('PATCH'), + post: makeSseFn('POST'), + put: makeSseFn('PUT'), + trace: makeSseFn('TRACE'), + }, + trace: makeMethodFn('TRACE'), + } as Client; +}; diff --git a/src/api-clients/client/client/index.ts b/src/api-clients/client/client/index.ts new file mode 100644 index 0000000..318a84b --- /dev/null +++ b/src/api-clients/client/client/index.ts @@ -0,0 +1,25 @@ +// This file is auto-generated by @hey-api/openapi-ts + +export type { Auth } from '../core/auth.gen'; +export type { QuerySerializerOptions } from '../core/bodySerializer.gen'; +export { + formDataBodySerializer, + jsonBodySerializer, + urlSearchParamsBodySerializer, +} from '../core/bodySerializer.gen'; +export { buildClientParams } from '../core/params.gen'; +export { createClient } from './client.gen'; +export type { + Client, + ClientOptions, + Config, + CreateClientConfig, + Options, + OptionsLegacyParser, + RequestOptions, + RequestResult, + ResolvedRequestOptions, + ResponseStyle, + TDataShape, +} from './types.gen'; +export { createConfig, mergeHeaders } from './utils.gen'; diff --git a/src/api-clients/client/client/types.gen.ts b/src/api-clients/client/client/types.gen.ts new file mode 100644 index 0000000..1a005b5 --- /dev/null +++ b/src/api-clients/client/client/types.gen.ts @@ -0,0 +1,268 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import type { Auth } from '../core/auth.gen'; +import type { + ServerSentEventsOptions, + ServerSentEventsResult, +} from '../core/serverSentEvents.gen'; +import type { + Client as CoreClient, + Config as CoreConfig, +} from '../core/types.gen'; +import type { Middleware } from './utils.gen'; + +export type ResponseStyle = 'data' | 'fields'; + +export interface Config + extends Omit, + CoreConfig { + /** + * Base URL for all requests made by this client. + */ + baseUrl?: T['baseUrl']; + /** + * Fetch API implementation. You can use this option to provide a custom + * fetch instance. + * + * @default globalThis.fetch + */ + fetch?: typeof fetch; + /** + * Please don't use the Fetch client for Next.js applications. The `next` + * options won't have any effect. + * + * Install {@link https://www.npmjs.com/package/@hey-api/client-next `@hey-api/client-next`} instead. + */ + next?: never; + /** + * Return the response data parsed in a specified format. By default, `auto` + * will infer the appropriate method from the `Content-Type` response header. + * You can override this behavior with any of the {@link Body} methods. + * Select `stream` if you don't want to parse response data at all. + * + * @default 'auto' + */ + parseAs?: + | 'arrayBuffer' + | 'auto' + | 'blob' + | 'formData' + | 'json' + | 'stream' + | 'text'; + /** + * Should we return only data or multiple fields (data, error, response, etc.)? + * + * @default 'fields' + */ + responseStyle?: ResponseStyle; + /** + * Throw an error instead of returning it in the response? + * + * @default false + */ + throwOnError?: T['throwOnError']; +} + +export interface RequestOptions< + TData = unknown, + TResponseStyle extends ResponseStyle = 'fields', + ThrowOnError extends boolean = boolean, + Url extends string = string, +> extends Config<{ + responseStyle: TResponseStyle; + throwOnError: ThrowOnError; + }>, + Pick< + ServerSentEventsOptions, + | 'onSseError' + | 'onSseEvent' + | 'sseDefaultRetryDelay' + | 'sseMaxRetryAttempts' + | 'sseMaxRetryDelay' + > { + /** + * Any body that you want to add to your request. + * + * {@link https://developer.mozilla.org/docs/Web/API/fetch#body} + */ + body?: unknown; + path?: Record; + query?: Record; + /** + * Security mechanism(s) to use for the request. + */ + security?: ReadonlyArray; + url: Url; +} + +export interface ResolvedRequestOptions< + TResponseStyle extends ResponseStyle = 'fields', + ThrowOnError extends boolean = boolean, + Url extends string = string, +> extends RequestOptions { + serializedBody?: string; +} + +export type RequestResult< + TData = unknown, + TError = unknown, + ThrowOnError extends boolean = boolean, + TResponseStyle extends ResponseStyle = 'fields', +> = ThrowOnError extends true + ? Promise< + TResponseStyle extends 'data' + ? TData extends Record + ? TData[keyof TData] + : TData + : { + data: TData extends Record + ? TData[keyof TData] + : TData; + request: Request; + response: Response; + } + > + : Promise< + TResponseStyle extends 'data' + ? + | (TData extends Record + ? TData[keyof TData] + : TData) + | undefined + : ( + | { + data: TData extends Record + ? TData[keyof TData] + : TData; + error: undefined; + } + | { + data: undefined; + error: TError extends Record + ? TError[keyof TError] + : TError; + } + ) & { + request: Request; + response: Response; + } + >; + +export interface ClientOptions { + baseUrl?: string; + responseStyle?: ResponseStyle; + throwOnError?: boolean; +} + +type MethodFn = < + TData = unknown, + TError = unknown, + ThrowOnError extends boolean = false, + TResponseStyle extends ResponseStyle = 'fields', +>( + options: Omit, 'method'>, +) => RequestResult; + +type SseFn = < + TData = unknown, + TError = unknown, + ThrowOnError extends boolean = false, + TResponseStyle extends ResponseStyle = 'fields', +>( + options: Omit, 'method'>, +) => Promise>; + +type RequestFn = < + TData = unknown, + TError = unknown, + ThrowOnError extends boolean = false, + TResponseStyle extends ResponseStyle = 'fields', +>( + options: Omit, 'method'> & + Pick< + Required>, + 'method' + >, +) => RequestResult; + +type BuildUrlFn = < + TData extends { + body?: unknown; + path?: Record; + query?: Record; + url: string; + }, +>( + options: Pick & Options, +) => string; + +export type Client = CoreClient< + RequestFn, + Config, + MethodFn, + BuildUrlFn, + SseFn +> & { + interceptors: Middleware; +}; + +/** + * The `createClientConfig()` function will be called on client initialization + * and the returned object will become the client's initial configuration. + * + * You may want to initialize your client this way instead of calling + * `setConfig()`. This is useful for example if you're using Next.js + * to ensure your client always has the correct values. + */ +export type CreateClientConfig = ( + override?: Config, +) => Config & T>; + +export interface TDataShape { + body?: unknown; + headers?: unknown; + path?: unknown; + query?: unknown; + url: string; +} + +type OmitKeys = Pick>; + +export type Options< + TData extends TDataShape = TDataShape, + ThrowOnError extends boolean = boolean, + TResponse = unknown, + TResponseStyle extends ResponseStyle = 'fields', +> = OmitKeys< + RequestOptions, + 'body' | 'path' | 'query' | 'url' +> & + Omit; + +export type OptionsLegacyParser< + TData = unknown, + ThrowOnError extends boolean = boolean, + TResponseStyle extends ResponseStyle = 'fields', +> = TData extends { body?: any } + ? TData extends { headers?: any } + ? OmitKeys< + RequestOptions, + 'body' | 'headers' | 'url' + > & + TData + : OmitKeys< + RequestOptions, + 'body' | 'url' + > & + TData & + Pick, 'headers'> + : TData extends { headers?: any } + ? OmitKeys< + RequestOptions, + 'headers' | 'url' + > & + TData & + Pick, 'body'> + : OmitKeys, 'url'> & + TData; diff --git a/src/api-clients/client/client/utils.gen.ts b/src/api-clients/client/client/utils.gen.ts new file mode 100644 index 0000000..b4bcc4d --- /dev/null +++ b/src/api-clients/client/client/utils.gen.ts @@ -0,0 +1,331 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import { getAuthToken } from '../core/auth.gen'; +import type { QuerySerializerOptions } from '../core/bodySerializer.gen'; +import { jsonBodySerializer } from '../core/bodySerializer.gen'; +import { + serializeArrayParam, + serializeObjectParam, + serializePrimitiveParam, +} from '../core/pathSerializer.gen'; +import { getUrl } from '../core/utils.gen'; +import type { Client, ClientOptions, Config, RequestOptions } from './types.gen'; + +export const createQuerySerializer = ({ + allowReserved, + array, + object, +}: QuerySerializerOptions = {}) => { + const querySerializer = (queryParams: T) => { + const search: string[] = []; + if (queryParams && typeof queryParams === 'object') { + for (const name in queryParams) { + const value = queryParams[name]; + + if (value === undefined || value === null) { + continue; + } + + if (Array.isArray(value)) { + const serializedArray = serializeArrayParam({ + allowReserved, + explode: true, + name, + style: 'form', + value, + ...array, + }); + if (serializedArray) search.push(serializedArray); + } else if (typeof value === 'object') { + const serializedObject = serializeObjectParam({ + allowReserved, + explode: true, + name, + style: 'deepObject', + value: value as Record, + ...object, + }); + if (serializedObject) search.push(serializedObject); + } else { + const serializedPrimitive = serializePrimitiveParam({ + allowReserved, + name, + value: value as string, + }); + if (serializedPrimitive) search.push(serializedPrimitive); + } + } + } + return search.join('&'); + }; + return querySerializer; +}; + +/** + * Infers parseAs value from provided Content-Type header. + */ +export const getParseAs = ( + contentType: string | null, +): Exclude => { + if (!contentType) { + // If no Content-Type header is provided, the best we can do is return the raw response body, + // which is effectively the same as the 'stream' option. + return 'stream'; + } + + const cleanContent = contentType.split(';')[0]?.trim(); + + if (!cleanContent) { + return; + } + + if ( + cleanContent.startsWith('application/json') || + cleanContent.endsWith('+json') + ) { + return 'json'; + } + + if (cleanContent === 'multipart/form-data') { + return 'formData'; + } + + if ( + ['application/', 'audio/', 'image/', 'video/'].some((type) => + cleanContent.startsWith(type), + ) + ) { + return 'blob'; + } + + if (cleanContent.startsWith('text/')) { + return 'text'; + } + + return; +}; + +const checkForExistence = ( + options: Pick & { + headers: Headers; + }, + name?: string, +): boolean => { + if (!name) { + return false; + } + if ( + options.headers.has(name) || + options.query?.[name] || + options.headers.get('Cookie')?.includes(`${name}=`) + ) { + return true; + } + return false; +}; + +export const setAuthParams = async ({ + security, + ...options +}: Pick, 'security'> & + Pick & { + headers: Headers; + }) => { + for (const auth of security) { + if (checkForExistence(options, auth.name)) { + continue; + } + + const token = await getAuthToken(auth, options.auth); + + if (!token) { + continue; + } + + const name = auth.name ?? 'Authorization'; + + switch (auth.in) { + case 'query': + if (!options.query) { + options.query = {}; + } + options.query[name] = token; + break; + case 'cookie': + options.headers.append('Cookie', `${name}=${token}`); + break; + case 'header': + default: + options.headers.set(name, token); + break; + } + } +}; + +export const buildUrl: Client['buildUrl'] = (options) => + getUrl({ + baseUrl: options.baseUrl as string, + path: options.path, + query: options.query, + querySerializer: + typeof options.querySerializer === 'function' + ? options.querySerializer + : createQuerySerializer(options.querySerializer), + url: options.url, + }); + +export const mergeConfigs = (a: Config, b: Config): Config => { + const config = { ...a, ...b }; + if (config.baseUrl?.endsWith('/')) { + config.baseUrl = config.baseUrl.substring(0, config.baseUrl.length - 1); + } + config.headers = mergeHeaders(a.headers, b.headers); + return config; +}; + +const headersEntries = (headers: Headers): Array<[string, string]> => { + const entries: Array<[string, string]> = []; + headers.forEach((value, key) => { + entries.push([key, value]); + }); + return entries; +}; + +export const mergeHeaders = ( + ...headers: Array['headers'] | undefined> +): Headers => { + const mergedHeaders = new Headers(); + for (const header of headers) { + if (!header) { + continue; + } + + const iterator = + header instanceof Headers + ? headersEntries(header) + : Object.entries(header); + + for (const [key, value] of iterator) { + if (value === null) { + mergedHeaders.delete(key); + } else if (Array.isArray(value)) { + for (const v of value) { + mergedHeaders.append(key, v as string); + } + } else if (value !== undefined) { + // assume object headers are meant to be JSON stringified, i.e. their + // content value in OpenAPI specification is 'application/json' + mergedHeaders.set( + key, + typeof value === 'object' ? JSON.stringify(value) : (value as string), + ); + } + } + } + return mergedHeaders; +}; + +type ErrInterceptor = ( + error: Err, + response: Res, + request: Req, + options: Options, +) => Err | Promise; + +type ReqInterceptor = ( + request: Req, + options: Options, +) => Req | Promise; + +type ResInterceptor = ( + response: Res, + request: Req, + options: Options, +) => Res | Promise; + +class Interceptors { + fns: Array = []; + + clear(): void { + this.fns = []; + } + + eject(id: number | Interceptor): void { + const index = this.getInterceptorIndex(id); + if (this.fns[index]) { + this.fns[index] = null; + } + } + + exists(id: number | Interceptor): boolean { + const index = this.getInterceptorIndex(id); + return Boolean(this.fns[index]); + } + + getInterceptorIndex(id: number | Interceptor): number { + if (typeof id === 'number') { + return this.fns[id] ? id : -1; + } + return this.fns.indexOf(id); + } + + update( + id: number | Interceptor, + fn: Interceptor, + ): number | Interceptor | false { + const index = this.getInterceptorIndex(id); + if (this.fns[index]) { + this.fns[index] = fn; + return id; + } + return false; + } + + use(fn: Interceptor): number { + this.fns.push(fn); + return this.fns.length - 1; + } +} + +export interface Middleware { + error: Interceptors>; + request: Interceptors>; + response: Interceptors>; +} + +export const createInterceptors = (): Middleware< + Req, + Res, + Err, + Options +> => ({ + error: new Interceptors>(), + request: new Interceptors>(), + response: new Interceptors>(), +}); + +const defaultQuerySerializer = createQuerySerializer({ + allowReserved: false, + array: { + explode: true, + style: 'form', + }, + object: { + explode: true, + style: 'deepObject', + }, +}); + +const defaultHeaders = { + 'Content-Type': 'application/json', +}; + +export const createConfig = ( + override: Config & T> = {}, +): Config & T> => ({ + ...jsonBodySerializer, + headers: defaultHeaders, + parseAs: 'auto', + querySerializer: defaultQuerySerializer, + ...override, +}); diff --git a/src/api-clients/client/core/auth.gen.ts b/src/api-clients/client/core/auth.gen.ts new file mode 100644 index 0000000..f8a7326 --- /dev/null +++ b/src/api-clients/client/core/auth.gen.ts @@ -0,0 +1,42 @@ +// This file is auto-generated by @hey-api/openapi-ts + +export type AuthToken = string | undefined; + +export interface Auth { + /** + * Which part of the request do we use to send the auth? + * + * @default 'header' + */ + in?: 'header' | 'query' | 'cookie'; + /** + * Header or query parameter name. + * + * @default 'Authorization' + */ + name?: string; + scheme?: 'basic' | 'bearer'; + type: 'apiKey' | 'http'; +} + +export const getAuthToken = async ( + auth: Auth, + callback: ((auth: Auth) => Promise | AuthToken) | AuthToken, +): Promise => { + const token = + typeof callback === 'function' ? await callback(auth) : callback; + + if (!token) { + return; + } + + if (auth.scheme === 'bearer') { + return `Bearer ${token}`; + } + + if (auth.scheme === 'basic') { + return `Basic ${btoa(token)}`; + } + + return token; +}; diff --git a/src/api-clients/client/core/bodySerializer.gen.ts b/src/api-clients/client/core/bodySerializer.gen.ts new file mode 100644 index 0000000..49cd892 --- /dev/null +++ b/src/api-clients/client/core/bodySerializer.gen.ts @@ -0,0 +1,92 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import type { + ArrayStyle, + ObjectStyle, + SerializerOptions, +} from './pathSerializer.gen'; + +export type QuerySerializer = (query: Record) => string; + +export type BodySerializer = (body: any) => any; + +export interface QuerySerializerOptions { + allowReserved?: boolean; + array?: SerializerOptions; + object?: SerializerOptions; +} + +const serializeFormDataPair = ( + data: FormData, + key: string, + value: unknown, +): void => { + if (typeof value === 'string' || value instanceof Blob) { + data.append(key, value); + } else if (value instanceof Date) { + data.append(key, value.toISOString()); + } else { + data.append(key, JSON.stringify(value)); + } +}; + +const serializeUrlSearchParamsPair = ( + data: URLSearchParams, + key: string, + value: unknown, +): void => { + if (typeof value === 'string') { + data.append(key, value); + } else { + data.append(key, JSON.stringify(value)); + } +}; + +export const formDataBodySerializer = { + bodySerializer: | Array>>( + body: T, + ): FormData => { + const data = new FormData(); + + Object.entries(body).forEach(([key, value]) => { + if (value === undefined || value === null) { + return; + } + if (Array.isArray(value)) { + value.forEach((v) => serializeFormDataPair(data, key, v)); + } else { + serializeFormDataPair(data, key, value); + } + }); + + return data; + }, +}; + +export const jsonBodySerializer = { + bodySerializer: (body: T): string => + JSON.stringify(body, (_key, value) => + typeof value === 'bigint' ? value.toString() : value, + ), +}; + +export const urlSearchParamsBodySerializer = { + bodySerializer: | Array>>( + body: T, + ): string => { + const data = new URLSearchParams(); + + Object.entries(body).forEach(([key, value]) => { + if (value === undefined || value === null) { + return; + } + if (Array.isArray(value)) { + value.forEach((v) => serializeUrlSearchParamsPair(data, key, v)); + } else { + serializeUrlSearchParamsPair(data, key, value); + } + }); + + return data.toString(); + }, +}; diff --git a/src/api-clients/client/core/params.gen.ts b/src/api-clients/client/core/params.gen.ts new file mode 100644 index 0000000..71c88e8 --- /dev/null +++ b/src/api-clients/client/core/params.gen.ts @@ -0,0 +1,153 @@ +// This file is auto-generated by @hey-api/openapi-ts + +type Slot = 'body' | 'headers' | 'path' | 'query'; + +export type Field = + | { + in: Exclude; + /** + * Field name. This is the name we want the user to see and use. + */ + key: string; + /** + * Field mapped name. This is the name we want to use in the request. + * If omitted, we use the same value as `key`. + */ + map?: string; + } + | { + in: Extract; + /** + * Key isn't required for bodies. + */ + key?: string; + map?: string; + }; + +export interface Fields { + allowExtra?: Partial>; + args?: ReadonlyArray; +} + +export type FieldsConfig = ReadonlyArray; + +const extraPrefixesMap: Record = { + $body_: 'body', + $headers_: 'headers', + $path_: 'path', + $query_: 'query', +}; +const extraPrefixes = Object.entries(extraPrefixesMap); + +type KeyMap = Map< + string, + { + in: Slot; + map?: string; + } +>; + +const buildKeyMap = (fields: FieldsConfig, map?: KeyMap): KeyMap => { + if (!map) { + map = new Map(); + } + + for (const config of fields) { + if ('in' in config) { + if (config.key) { + map.set(config.key, { + in: config.in, + map: config.map, + }); + } + } else if (config.args) { + buildKeyMap(config.args, map); + } + } + + return map; +}; + +interface Params { + body: unknown; + headers: Record; + path: Record; + query: Record; +} + +const stripEmptySlots = (params: Params) => { + for (const [slot, value] of Object.entries(params)) { + if (value && typeof value === 'object' && !Object.keys(value).length) { + delete params[slot as Slot]; + } + } +}; + +export const buildClientParams = ( + args: ReadonlyArray, + fields: FieldsConfig, +) => { + const params: Params = { + body: {}, + headers: {}, + path: {}, + query: {}, + }; + + const map = buildKeyMap(fields); + + let config: FieldsConfig[number] | undefined; + + for (const [index, arg] of args.entries()) { + if (fields[index]) { + config = fields[index]; + } + + if (!config) { + continue; + } + + if ('in' in config) { + if (config.key) { + const field = map.get(config.key)!; + const name = field.map || config.key; + (params[field.in] as Record)[name] = arg; + } else { + params.body = arg; + } + } else { + for (const [key, value] of Object.entries(arg ?? {})) { + const field = map.get(key); + + if (field) { + const name = field.map || key; + (params[field.in] as Record)[name] = value; + } else { + const extra = extraPrefixes.find(([prefix]) => + key.startsWith(prefix), + ); + + if (extra) { + const [prefix, slot] = extra; + (params[slot] as Record)[ + key.slice(prefix.length) + ] = value; + } else { + for (const [slot, allowed] of Object.entries( + config.allowExtra ?? {}, + )) { + if (allowed) { + (params[slot as Slot] as Record)[key] = value; + break; + } + } + } + } + } + } + } + + stripEmptySlots(params); + + return params; +}; diff --git a/src/api-clients/client/core/pathSerializer.gen.ts b/src/api-clients/client/core/pathSerializer.gen.ts new file mode 100644 index 0000000..8d99931 --- /dev/null +++ b/src/api-clients/client/core/pathSerializer.gen.ts @@ -0,0 +1,181 @@ +// This file is auto-generated by @hey-api/openapi-ts + +interface SerializeOptions + extends SerializePrimitiveOptions, + SerializerOptions {} + +interface SerializePrimitiveOptions { + allowReserved?: boolean; + name: string; +} + +export interface SerializerOptions { + /** + * @default true + */ + explode: boolean; + style: T; +} + +export type ArrayStyle = 'form' | 'spaceDelimited' | 'pipeDelimited'; +export type ArraySeparatorStyle = ArrayStyle | MatrixStyle; +type MatrixStyle = 'label' | 'matrix' | 'simple'; +export type ObjectStyle = 'form' | 'deepObject'; +type ObjectSeparatorStyle = ObjectStyle | MatrixStyle; + +interface SerializePrimitiveParam extends SerializePrimitiveOptions { + value: string; +} + +export const separatorArrayExplode = (style: ArraySeparatorStyle) => { + switch (style) { + case 'label': + return '.'; + case 'matrix': + return ';'; + case 'simple': + return ','; + default: + return '&'; + } +}; + +export const separatorArrayNoExplode = (style: ArraySeparatorStyle) => { + switch (style) { + case 'form': + return ','; + case 'pipeDelimited': + return '|'; + case 'spaceDelimited': + return '%20'; + default: + return ','; + } +}; + +export const separatorObjectExplode = (style: ObjectSeparatorStyle) => { + switch (style) { + case 'label': + return '.'; + case 'matrix': + return ';'; + case 'simple': + return ','; + default: + return '&'; + } +}; + +export const serializeArrayParam = ({ + allowReserved, + explode, + name, + style, + value, +}: SerializeOptions & { + value: unknown[]; +}) => { + if (!explode) { + const joinedValues = ( + allowReserved ? value : value.map((v) => encodeURIComponent(v as string)) + ).join(separatorArrayNoExplode(style)); + switch (style) { + case 'label': + return `.${joinedValues}`; + case 'matrix': + return `;${name}=${joinedValues}`; + case 'simple': + return joinedValues; + default: + return `${name}=${joinedValues}`; + } + } + + const separator = separatorArrayExplode(style); + const joinedValues = value + .map((v) => { + if (style === 'label' || style === 'simple') { + return allowReserved ? v : encodeURIComponent(v as string); + } + + return serializePrimitiveParam({ + allowReserved, + name, + value: v as string, + }); + }) + .join(separator); + return style === 'label' || style === 'matrix' + ? separator + joinedValues + : joinedValues; +}; + +export const serializePrimitiveParam = ({ + allowReserved, + name, + value, +}: SerializePrimitiveParam) => { + if (value === undefined || value === null) { + return ''; + } + + if (typeof value === 'object') { + throw new Error( + 'Deeply-nested arrays/objects aren’t supported. Provide your own `querySerializer()` to handle these.', + ); + } + + return `${name}=${allowReserved ? value : encodeURIComponent(value)}`; +}; + +export const serializeObjectParam = ({ + allowReserved, + explode, + name, + style, + value, + valueOnly, +}: SerializeOptions & { + value: Record | Date; + valueOnly?: boolean; +}) => { + if (value instanceof Date) { + return valueOnly ? value.toISOString() : `${name}=${value.toISOString()}`; + } + + if (style !== 'deepObject' && !explode) { + let values: string[] = []; + Object.entries(value).forEach(([key, v]) => { + values = [ + ...values, + key, + allowReserved ? (v as string) : encodeURIComponent(v as string), + ]; + }); + const joinedValues = values.join(','); + switch (style) { + case 'form': + return `${name}=${joinedValues}`; + case 'label': + return `.${joinedValues}`; + case 'matrix': + return `;${name}=${joinedValues}`; + default: + return joinedValues; + } + } + + const separator = separatorObjectExplode(style); + const joinedValues = Object.entries(value) + .map(([key, v]) => + serializePrimitiveParam({ + allowReserved, + name: style === 'deepObject' ? `${name}[${key}]` : key, + value: v as string, + }), + ) + .join(separator); + return style === 'label' || style === 'matrix' + ? separator + joinedValues + : joinedValues; +}; diff --git a/src/api-clients/client/core/serverSentEvents.gen.ts b/src/api-clients/client/core/serverSentEvents.gen.ts new file mode 100644 index 0000000..f8fd78e --- /dev/null +++ b/src/api-clients/client/core/serverSentEvents.gen.ts @@ -0,0 +1,264 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import type { Config } from './types.gen'; + +export type ServerSentEventsOptions = Omit< + RequestInit, + 'method' +> & + Pick & { + /** + * Fetch API implementation. You can use this option to provide a custom + * fetch instance. + * + * @default globalThis.fetch + */ + fetch?: typeof fetch; + /** + * Implementing clients can call request interceptors inside this hook. + */ + onRequest?: (url: string, init: RequestInit) => Promise; + /** + * Callback invoked when a network or parsing error occurs during streaming. + * + * This option applies only if the endpoint returns a stream of events. + * + * @param error The error that occurred. + */ + onSseError?: (error: unknown) => void; + /** + * Callback invoked when an event is streamed from the server. + * + * This option applies only if the endpoint returns a stream of events. + * + * @param event Event streamed from the server. + * @returns Nothing (void). + */ + onSseEvent?: (event: StreamEvent) => void; + serializedBody?: RequestInit['body']; + /** + * Default retry delay in milliseconds. + * + * This option applies only if the endpoint returns a stream of events. + * + * @default 3000 + */ + sseDefaultRetryDelay?: number; + /** + * Maximum number of retry attempts before giving up. + */ + sseMaxRetryAttempts?: number; + /** + * Maximum retry delay in milliseconds. + * + * Applies only when exponential backoff is used. + * + * This option applies only if the endpoint returns a stream of events. + * + * @default 30000 + */ + sseMaxRetryDelay?: number; + /** + * Optional sleep function for retry backoff. + * + * Defaults to using `setTimeout`. + */ + sseSleepFn?: (ms: number) => Promise; + url: string; + }; + +export interface StreamEvent { + data: TData; + event?: string; + id?: string; + retry?: number; +} + +export type ServerSentEventsResult< + TData = unknown, + TReturn = void, + TNext = unknown, +> = { + stream: AsyncGenerator< + TData extends Record ? TData[keyof TData] : TData, + TReturn, + TNext + >; +}; + +export const createSseClient = ({ + onRequest, + onSseError, + onSseEvent, + responseTransformer, + responseValidator, + sseDefaultRetryDelay, + sseMaxRetryAttempts, + sseMaxRetryDelay, + sseSleepFn, + url, + ...options +}: ServerSentEventsOptions): ServerSentEventsResult => { + let lastEventId: string | undefined; + + const sleep = + sseSleepFn ?? + ((ms: number) => new Promise((resolve) => setTimeout(resolve, ms))); + + const createStream = async function* () { + let retryDelay: number = sseDefaultRetryDelay ?? 3000; + let attempt = 0; + const signal = options.signal ?? new AbortController().signal; + + while (true) { + if (signal.aborted) break; + + attempt++; + + const headers = + options.headers instanceof Headers + ? options.headers + : new Headers(options.headers as Record | undefined); + + if (lastEventId !== undefined) { + headers.set('Last-Event-ID', lastEventId); + } + + try { + const requestInit: RequestInit = { + redirect: 'follow', + ...options, + body: options.serializedBody, + headers, + signal, + }; + let request = new Request(url, requestInit); + if (onRequest) { + request = await onRequest(url, requestInit); + } + // fetch must be assigned here, otherwise it would throw the error: + // TypeError: Failed to execute 'fetch' on 'Window': Illegal invocation + const _fetch = options.fetch ?? globalThis.fetch; + const response = await _fetch(request); + + if (!response.ok) + throw new Error( + `SSE failed: ${response.status} ${response.statusText}`, + ); + + if (!response.body) throw new Error('No body in SSE response'); + + const reader = response.body + .pipeThrough(new TextDecoderStream()) + .getReader(); + + let buffer = ''; + + const abortHandler = () => { + try { + reader.cancel(); + } catch { + // noop + } + }; + + signal.addEventListener('abort', abortHandler); + + try { + while (true) { + const { done, value } = await reader.read(); + if (done) break; + buffer += value; + + const chunks = buffer.split('\n\n'); + buffer = chunks.pop() ?? ''; + + for (const chunk of chunks) { + const lines = chunk.split('\n'); + const dataLines: Array = []; + let eventName: string | undefined; + + for (const line of lines) { + if (line.startsWith('data:')) { + dataLines.push(line.replace(/^data:\s*/, '')); + } else if (line.startsWith('event:')) { + eventName = line.replace(/^event:\s*/, ''); + } else if (line.startsWith('id:')) { + lastEventId = line.replace(/^id:\s*/, ''); + } else if (line.startsWith('retry:')) { + const parsed = Number.parseInt( + line.replace(/^retry:\s*/, ''), + 10, + ); + if (!Number.isNaN(parsed)) { + retryDelay = parsed; + } + } + } + + let data: unknown; + let parsedJson = false; + + if (dataLines.length) { + const rawData = dataLines.join('\n'); + try { + data = JSON.parse(rawData); + parsedJson = true; + } catch { + data = rawData; + } + } + + if (parsedJson) { + if (responseValidator) { + await responseValidator(data); + } + + if (responseTransformer) { + data = await responseTransformer(data); + } + } + + onSseEvent?.({ + data, + event: eventName, + id: lastEventId, + retry: retryDelay, + }); + + if (dataLines.length) { + yield data as any; + } + } + } + } finally { + signal.removeEventListener('abort', abortHandler); + reader.releaseLock(); + } + + break; // exit loop on normal completion + } catch (error) { + // connection failed or aborted; retry after delay + onSseError?.(error); + + if ( + sseMaxRetryAttempts !== undefined && + attempt >= sseMaxRetryAttempts + ) { + break; // stop after firing error + } + + // exponential backoff: double retry each attempt, cap at 30s + const backoff = Math.min( + retryDelay * 2 ** (attempt - 1), + sseMaxRetryDelay ?? 30000, + ); + await sleep(backoff); + } + } + }; + + const stream = createStream(); + + return { stream }; +}; diff --git a/src/api-clients/client/core/types.gen.ts b/src/api-clients/client/core/types.gen.ts new file mode 100644 index 0000000..643c070 --- /dev/null +++ b/src/api-clients/client/core/types.gen.ts @@ -0,0 +1,118 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import type { Auth, AuthToken } from './auth.gen'; +import type { + BodySerializer, + QuerySerializer, + QuerySerializerOptions, +} from './bodySerializer.gen'; + +export type HttpMethod = + | 'connect' + | 'delete' + | 'get' + | 'head' + | 'options' + | 'patch' + | 'post' + | 'put' + | 'trace'; + +export type Client< + RequestFn = never, + Config = unknown, + MethodFn = never, + BuildUrlFn = never, + SseFn = never, +> = { + /** + * Returns the final request URL. + */ + buildUrl: BuildUrlFn; + getConfig: () => Config; + request: RequestFn; + setConfig: (config: Config) => Config; +} & { + [K in HttpMethod]: MethodFn; +} & ([SseFn] extends [never] + ? { sse?: never } + : { sse: { [K in HttpMethod]: SseFn } }); + +export interface Config { + /** + * Auth token or a function returning auth token. The resolved value will be + * added to the request payload as defined by its `security` array. + */ + auth?: ((auth: Auth) => Promise | AuthToken) | AuthToken; + /** + * A function for serializing request body parameter. By default, + * {@link JSON.stringify()} will be used. + */ + bodySerializer?: BodySerializer | null; + /** + * An object containing any HTTP headers that you want to pre-populate your + * `Headers` object with. + * + * {@link https://developer.mozilla.org/docs/Web/API/Headers/Headers#init See more} + */ + headers?: + | RequestInit['headers'] + | Record< + string, + | string + | number + | boolean + | (string | number | boolean)[] + | null + | undefined + | unknown + >; + /** + * The request method. + * + * {@link https://developer.mozilla.org/docs/Web/API/fetch#method See more} + */ + method?: Uppercase; + /** + * A function for serializing request query parameters. By default, arrays + * will be exploded in form style, objects will be exploded in deepObject + * style, and reserved characters are percent-encoded. + * + * This method will have no effect if the native `paramsSerializer()` Axios + * API function is used. + * + * {@link https://swagger.io/docs/specification/serialization/#query View examples} + */ + querySerializer?: QuerySerializer | QuerySerializerOptions; + /** + * A function validating request data. This is useful if you want to ensure + * the request conforms to the desired shape, so it can be safely sent to + * the server. + */ + requestValidator?: (data: unknown) => Promise; + /** + * A function transforming response data before it's returned. This is useful + * for post-processing data, e.g. converting ISO strings into Date objects. + */ + responseTransformer?: (data: unknown) => Promise; + /** + * A function validating response data. This is useful if you want to ensure + * the response conforms to the desired shape, so it can be safely passed to + * the transformers and returned to the user. + */ + responseValidator?: (data: unknown) => Promise; +} + +type IsExactlyNeverOrNeverUndefined = [T] extends [never] + ? true + : [T] extends [never | undefined] + ? [undefined] extends [T] + ? false + : true + : false; + +export type OmitNever> = { + [K in keyof T as IsExactlyNeverOrNeverUndefined extends true + ? never + : K]: T[K]; +}; diff --git a/src/api-clients/client/core/utils.gen.ts b/src/api-clients/client/core/utils.gen.ts new file mode 100644 index 0000000..0b5389d --- /dev/null +++ b/src/api-clients/client/core/utils.gen.ts @@ -0,0 +1,143 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import type { BodySerializer, QuerySerializer } from './bodySerializer.gen'; +import { + type ArraySeparatorStyle, + serializeArrayParam, + serializeObjectParam, + serializePrimitiveParam, +} from './pathSerializer.gen'; + +export interface PathSerializer { + path: Record; + url: string; +} + +export const PATH_PARAM_RE = /\{[^{}]+\}/g; + +export const defaultPathSerializer = ({ path, url: _url }: PathSerializer) => { + let url = _url; + const matches = _url.match(PATH_PARAM_RE); + if (matches) { + for (const match of matches) { + let explode = false; + let name = match.substring(1, match.length - 1); + let style: ArraySeparatorStyle = 'simple'; + + if (name.endsWith('*')) { + explode = true; + name = name.substring(0, name.length - 1); + } + + if (name.startsWith('.')) { + name = name.substring(1); + style = 'label'; + } else if (name.startsWith(';')) { + name = name.substring(1); + style = 'matrix'; + } + + const value = path[name]; + + if (value === undefined || value === null) { + continue; + } + + if (Array.isArray(value)) { + url = url.replace( + match, + serializeArrayParam({ explode, name, style, value }), + ); + continue; + } + + if (typeof value === 'object') { + url = url.replace( + match, + serializeObjectParam({ + explode, + name, + style, + value: value as Record, + valueOnly: true, + }), + ); + continue; + } + + if (style === 'matrix') { + url = url.replace( + match, + `;${serializePrimitiveParam({ + name, + value: value as string, + })}`, + ); + continue; + } + + const replaceValue = encodeURIComponent( + style === 'label' ? `.${value as string}` : (value as string), + ); + url = url.replace(match, replaceValue); + } + } + return url; +}; + +export const getUrl = ({ + baseUrl, + path, + query, + querySerializer, + url: _url, +}: { + baseUrl?: string; + path?: Record; + query?: Record; + querySerializer: QuerySerializer; + url: string; +}) => { + const pathUrl = _url.startsWith('/') ? _url : `/${_url}`; + let url = (baseUrl ?? '') + pathUrl; + if (path) { + url = defaultPathSerializer({ path, url }); + } + let search = query ? querySerializer(query) : ''; + if (search.startsWith('?')) { + search = search.substring(1); + } + if (search) { + url += `?${search}`; + } + return url; +}; + +export function getValidRequestBody(options: { + body?: unknown; + bodySerializer?: BodySerializer | null; + serializedBody?: unknown; +}) { + const hasBody = options.body !== undefined; + const isSerializedBody = hasBody && options.bodySerializer; + + if (isSerializedBody) { + if ('serializedBody' in options) { + const hasSerializedBody = + options.serializedBody !== undefined && options.serializedBody !== ''; + + return hasSerializedBody ? options.serializedBody : null; + } + + // not all clients implement a serializedBody property (i.e. client-axios) + return options.body !== '' ? options.body : null; + } + + // plain/text body + if (hasBody) { + return options.body; + } + + // no body was provided + return undefined; +} diff --git a/src/api-clients/client/index.ts b/src/api-clients/client/index.ts index e64537d..c352c10 100644 --- a/src/api-clients/client/index.ts +++ b/src/api-clients/client/index.ts @@ -1,3 +1,4 @@ // This file is auto-generated by @hey-api/openapi-ts -export * from './types.gen'; -export * from './sdk.gen'; \ No newline at end of file + +export type * from './types.gen'; +export * from './sdk.gen'; diff --git a/src/api-clients/client/sdk.gen.ts b/src/api-clients/client/sdk.gen.ts index b2ab6bf..a9a0dee 100644 --- a/src/api-clients/client/sdk.gen.ts +++ b/src/api-clients/client/sdk.gen.ts @@ -1,23 +1,28 @@ // This file is auto-generated by @hey-api/openapi-ts -import type { Options as ClientOptions, TDataShape, Client } from '@hey-api/client-fetch'; -import type { MetaInfoData, MetaInfoResponse, WorkspaceCreateData, WorkspaceCreateResponse2, TokenCreateData, TokenCreateResponse2, TokenUpdateData, TokenUpdateResponse2, SandboxListData, SandboxListResponse2, SandboxCreateData, SandboxCreateResponse2, SandboxGetData, SandboxGetResponse2, SandboxForkData, SandboxForkResponse2, PreviewTokenRevokeAllData, PreviewTokenRevokeAllResponse2, PreviewTokenListData, PreviewTokenListResponse2, PreviewTokenCreateData, PreviewTokenCreateResponse2, PreviewTokenUpdateData, PreviewTokenUpdateResponse2, TemplatesCreateData, TemplatesCreateResponse, VmAssignTagAliasData, VmAssignTagAliasResponse2, VmListClustersData, VmListClustersResponse2, VmListRunningVmsData, VmListRunningVmsResponse2, VmCreateTagData, VmCreateTagResponse2, VmDeleteData, VmDeleteResponse2, VmHibernateData, VmHibernateResponse2, VmUpdateHibernationTimeoutData, VmUpdateHibernationTimeoutResponse2, VmCreateSessionData, VmCreateSessionResponse2, VmShutdownData, VmShutdownResponse2, VmUpdateSpecsData, VmUpdateSpecsResponse2, VmStartData, VmStartResponse2, VmUpdateSpecs2Data, VmUpdateSpecs2Response, PreviewHostListData, PreviewHostListResponse2, PreviewHostCreateData, PreviewHostCreateResponse, PreviewHostUpdateData, PreviewHostUpdateResponse } from './types.gen'; -import { client as _heyApiClient } from './client.gen'; +import type { Client, Options as Options2, TDataShape } from './client'; +import { client } from './client.gen'; +import type { MetaInfoData, MetaInfoResponses, PreviewHostCreateData, PreviewHostCreateResponses, PreviewHostListData, PreviewHostListResponses, PreviewHostUpdateData, PreviewHostUpdateResponses, PreviewTokenCreateData, PreviewTokenCreateResponses, PreviewTokenListData, PreviewTokenListResponses, PreviewTokenRevokeAllData, PreviewTokenRevokeAllResponses, PreviewTokenUpdateData, PreviewTokenUpdateResponses, SandboxCreateData, SandboxCreateResponses, SandboxForkData, SandboxForkResponses, SandboxGetData, SandboxGetResponses, SandboxListData, SandboxListResponses, TemplatesCreateData, TemplatesCreateResponses, TokenCreateData, TokenCreateResponses, TokenUpdateData, TokenUpdateResponses, VmAssignTagAliasData, VmAssignTagAliasResponses, VmCreateSessionData, VmCreateSessionResponses, VmCreateTagData, VmCreateTagResponses, VmDeleteData, VmDeleteResponses, VmHibernateData, VmHibernateResponses, VmListClustersData, VmListClustersResponses, VmListRunningVmsData, VmListRunningVmsResponses, VmShutdownData, VmShutdownResponses, VmStartData, VmStartResponses, VmUpdateHibernationTimeoutData, VmUpdateHibernationTimeoutResponses, VmUpdateSpecs2Data, VmUpdateSpecs2Responses, VmUpdateSpecsData, VmUpdateSpecsResponses, WorkspaceCreateData, WorkspaceCreateResponses } from './types.gen'; -export type Options = ClientOptions & { +export type Options = Options2 & { /** * You can provide a client instance returned by `createClient()` instead of * individual options. This might be also useful if you want to implement a * custom client. */ client?: Client; + /** + * You can pass arbitrary values through the `meta` object. This can be + * used to access values that aren't defined as part of the SDK function. + */ + meta?: Record; }; /** * Metadata about the API */ export const metaInfo = (options?: Options) => { - return (options?.client ?? _heyApiClient).get({ + return (options?.client ?? client).get({ url: '/meta/info', ...options }); @@ -29,7 +34,7 @@ export const metaInfo = (options?: Options * */ export const workspaceCreate = (options?: Options) => { - return (options?.client ?? _heyApiClient).post({ + return (options?.client ?? client).post({ security: [ { scheme: 'bearer', @@ -51,7 +56,7 @@ export const workspaceCreate = (options?: * */ export const tokenCreate = (options: Options) => { - return (options.client ?? _heyApiClient).post({ + return (options.client ?? client).post({ security: [ { scheme: 'bearer', @@ -62,7 +67,7 @@ export const tokenCreate = (options: Optio ...options, headers: { 'Content-Type': 'application/json', - ...options?.headers + ...options.headers } }); }; @@ -73,7 +78,7 @@ export const tokenCreate = (options: Optio * */ export const tokenUpdate = (options: Options) => { - return (options.client ?? _heyApiClient).patch({ + return (options.client ?? client).patch({ security: [ { scheme: 'bearer', @@ -84,7 +89,7 @@ export const tokenUpdate = (options: Optio ...options, headers: { 'Content-Type': 'application/json', - ...options?.headers + ...options.headers } }); }; @@ -96,7 +101,7 @@ export const tokenUpdate = (options: Optio * */ export const sandboxList = (options?: Options) => { - return (options?.client ?? _heyApiClient).get({ + return (options?.client ?? client).get({ security: [ { scheme: 'bearer', @@ -114,7 +119,7 @@ export const sandboxList = (options?: Opti * */ export const sandboxCreate = (options?: Options) => { - return (options?.client ?? _heyApiClient).post({ + return (options?.client ?? client).post({ security: [ { scheme: 'bearer', @@ -136,7 +141,7 @@ export const sandboxCreate = (options?: Op * */ export const sandboxGet = (options: Options) => { - return (options.client ?? _heyApiClient).get({ + return (options.client ?? client).get({ security: [ { scheme: 'bearer', @@ -154,7 +159,7 @@ export const sandboxGet = (options: Option * */ export const sandboxFork = (options: Options) => { - return (options.client ?? _heyApiClient).post({ + return (options.client ?? client).post({ security: [ { scheme: 'bearer', @@ -165,7 +170,7 @@ export const sandboxFork = (options: Optio ...options, headers: { 'Content-Type': 'application/json', - ...options?.headers + ...options.headers } }); }; @@ -176,7 +181,7 @@ export const sandboxFork = (options: Optio * */ export const previewTokenRevokeAll = (options: Options) => { - return (options.client ?? _heyApiClient).delete({ + return (options.client ?? client).delete({ security: [ { scheme: 'bearer', @@ -194,7 +199,7 @@ export const previewTokenRevokeAll = (opti * */ export const previewTokenList = (options: Options) => { - return (options.client ?? _heyApiClient).get({ + return (options.client ?? client).get({ security: [ { scheme: 'bearer', @@ -212,7 +217,7 @@ export const previewTokenList = (options: * */ export const previewTokenCreate = (options: Options) => { - return (options.client ?? _heyApiClient).post({ + return (options.client ?? client).post({ security: [ { scheme: 'bearer', @@ -223,7 +228,7 @@ export const previewTokenCreate = (options ...options, headers: { 'Content-Type': 'application/json', - ...options?.headers + ...options.headers } }); }; @@ -234,7 +239,7 @@ export const previewTokenCreate = (options * */ export const previewTokenUpdate = (options: Options) => { - return (options.client ?? _heyApiClient).patch({ + return (options.client ?? client).patch({ security: [ { scheme: 'bearer', @@ -245,7 +250,7 @@ export const previewTokenUpdate = (options ...options, headers: { 'Content-Type': 'application/json', - ...options?.headers + ...options.headers } }); }; @@ -256,7 +261,7 @@ export const previewTokenUpdate = (options * */ export const templatesCreate = (options?: Options) => { - return (options?.client ?? _heyApiClient).post({ + return (options?.client ?? client).post({ security: [ { scheme: 'bearer', @@ -278,7 +283,7 @@ export const templatesCreate = (options?: * */ export const vmAssignTagAlias = (options: Options) => { - return (options.client ?? _heyApiClient).put({ + return (options.client ?? client).put({ security: [ { scheme: 'bearer', @@ -289,7 +294,7 @@ export const vmAssignTagAlias = (options: ...options, headers: { 'Content-Type': 'application/json', - ...options?.headers + ...options.headers } }); }; @@ -300,7 +305,7 @@ export const vmAssignTagAlias = (options: * */ export const vmListClusters = (options?: Options) => { - return (options?.client ?? _heyApiClient).get({ + return (options?.client ?? client).get({ security: [ { scheme: 'bearer', @@ -318,7 +323,7 @@ export const vmListClusters = (options?: O * */ export const vmListRunningVms = (options?: Options) => { - return (options?.client ?? _heyApiClient).get({ + return (options?.client ?? client).get({ security: [ { scheme: 'bearer', @@ -336,7 +341,7 @@ export const vmListRunningVms = (options?: * */ export const vmCreateTag = (options?: Options) => { - return (options?.client ?? _heyApiClient).post({ + return (options?.client ?? client).post({ security: [ { scheme: 'bearer', @@ -361,7 +366,7 @@ export const vmCreateTag = (options?: Opti * */ export const vmDelete = (options: Options) => { - return (options.client ?? _heyApiClient).delete({ + return (options.client ?? client).delete({ security: [ { scheme: 'bearer', @@ -385,7 +390,7 @@ export const vmDelete = (options: Options< * */ export const vmHibernate = (options: Options) => { - return (options.client ?? _heyApiClient).post({ + return (options.client ?? client).post({ security: [ { scheme: 'bearer', @@ -396,7 +401,7 @@ export const vmHibernate = (options: Optio ...options, headers: { 'Content-Type': 'application/json', - ...options?.headers + ...options.headers } }); }; @@ -410,7 +415,7 @@ export const vmHibernate = (options: Optio * */ export const vmUpdateHibernationTimeout = (options: Options) => { - return (options.client ?? _heyApiClient).put({ + return (options.client ?? client).put({ security: [ { scheme: 'bearer', @@ -421,7 +426,7 @@ export const vmUpdateHibernationTimeout = ...options, headers: { 'Content-Type': 'application/json', - ...options?.headers + ...options.headers } }); }; @@ -438,7 +443,7 @@ export const vmUpdateHibernationTimeout = * */ export const vmCreateSession = (options: Options) => { - return (options.client ?? _heyApiClient).post({ + return (options.client ?? client).post({ security: [ { scheme: 'bearer', @@ -449,7 +454,7 @@ export const vmCreateSession = (options: O ...options, headers: { 'Content-Type': 'application/json', - ...options?.headers + ...options.headers } }); }; @@ -465,7 +470,7 @@ export const vmCreateSession = (options: O * */ export const vmShutdown = (options: Options) => { - return (options.client ?? _heyApiClient).post({ + return (options.client ?? client).post({ security: [ { scheme: 'bearer', @@ -476,7 +481,7 @@ export const vmShutdown = (options: Option ...options, headers: { 'Content-Type': 'application/json', - ...options?.headers + ...options.headers } }); }; @@ -490,7 +495,7 @@ export const vmShutdown = (options: Option * */ export const vmUpdateSpecs = (options: Options) => { - return (options.client ?? _heyApiClient).put({ + return (options.client ?? client).put({ security: [ { scheme: 'bearer', @@ -501,7 +506,7 @@ export const vmUpdateSpecs = (options: Opt ...options, headers: { 'Content-Type': 'application/json', - ...options?.headers + ...options.headers } }); }; @@ -519,7 +524,7 @@ export const vmUpdateSpecs = (options: Opt * */ export const vmStart = (options: Options) => { - return (options.client ?? _heyApiClient).post({ + return (options.client ?? client).post({ security: [ { scheme: 'bearer', @@ -530,7 +535,7 @@ export const vmStart = (options: Options(options: Options(options: Options) => { - return (options.client ?? _heyApiClient).post({ + return (options.client ?? client).post({ security: [ { scheme: 'bearer', @@ -555,7 +560,7 @@ export const vmUpdateSpecs2 = (options: Op ...options, headers: { 'Content-Type': 'application/json', - ...options?.headers + ...options.headers } }); }; @@ -566,7 +571,7 @@ export const vmUpdateSpecs2 = (options: Op * */ export const previewHostList = (options?: Options) => { - return (options?.client ?? _heyApiClient).get({ + return (options?.client ?? client).get({ security: [ { scheme: 'bearer', @@ -584,7 +589,7 @@ export const previewHostList = (options?: * */ export const previewHostCreate = (options?: Options) => { - return (options?.client ?? _heyApiClient).post({ + return (options?.client ?? client).post({ security: [ { scheme: 'bearer', @@ -606,7 +611,7 @@ export const previewHostCreate = (options? * */ export const previewHostUpdate = (options?: Options) => { - return (options?.client ?? _heyApiClient).put({ + return (options?.client ?? client).put({ security: [ { scheme: 'bearer', @@ -620,4 +625,4 @@ export const previewHostUpdate = (options? ...options?.headers } }); -}; \ No newline at end of file +}; diff --git a/src/api-clients/client/types.gen.ts b/src/api-clients/client/types.gen.ts index 0de3dac..ee3c79a 100644 --- a/src/api-clients/client/types.gen.ts +++ b/src/api-clients/client/types.gen.ts @@ -1,13 +1,29 @@ // This file is auto-generated by @hey-api/openapi-ts +export type ClientOptions = { + baseUrl: 'https://api.codesandbox.io' | (string & {}); +}; + +/** + * Response + */ export type Response = { + /** + * Error + */ errors?: Array; success?: boolean; }; +/** + * PreviewTokenListResponse + */ export type PreviewTokenListResponse = { + /** + * Error + */ errors?: Array; @@ -19,6 +35,9 @@ export type PreviewTokenListResponse = { }; }; +/** + * VMUpdateHibernationTimeoutRequest + */ export type VmUpdateHibernationTimeoutRequest = { /** * The new hibernation timeout in seconds. @@ -29,7 +48,13 @@ export type VmUpdateHibernationTimeoutRequest = { hibernation_timeout_seconds: number; }; +/** + * VMAssignTagAliasResponse + */ export type VmAssignTagAliasResponse = { + /** + * Error + */ errors?: Array; @@ -44,6 +69,9 @@ export type VmAssignTagAliasResponse = { }; }; +/** + * TemplateCreateRequest + */ export type TemplateCreateRequest = { /** * Template description. Maximum 255 characters. Defaults to description of original sandbox. @@ -63,6 +91,9 @@ export type TemplateCreateRequest = { title?: string; }; +/** + * PreviewToken + */ export type PreviewToken = { expires_at: string | null; last_used_at: string | null; @@ -70,7 +101,13 @@ export type PreviewToken = { token_prefix: string; }; +/** + * VMShutdownResponse + */ export type VmShutdownResponse = { + /** + * Error + */ errors?: Array; @@ -81,7 +118,13 @@ export type VmShutdownResponse = { }; }; +/** + * PreviewTokenRevokeAllResponse + */ export type PreviewTokenRevokeAllResponse = { + /** + * Error + */ errors?: Array; @@ -92,25 +135,40 @@ export type PreviewTokenRevokeAllResponse = { }; }; +/** + * Sandbox + */ export type Sandbox = { created_at: string; description?: string | null; id: string; is_frozen: boolean; privacy: number; + settings: { + use_pint?: boolean; + }; tags: Array; title?: string | null; updated_at: string; }; +/** + * Error + */ export type _Error = string | { [key: string]: unknown; }; +/** + * VMHibernateRequest + */ export type VmHibernateRequest = { [key: string]: unknown; }; +/** + * PreviewTokenCreateRequest + */ export type PreviewTokenCreateRequest = { /** * UTC Timestamp until when this token is valid. Omitting this field will create a token without an expiry. @@ -118,7 +176,13 @@ export type PreviewTokenCreateRequest = { expires_at?: string | null; }; +/** + * VMCreateTagResponse + */ export type VmCreateTagResponse = { + /** + * Error + */ errors?: Array; @@ -129,7 +193,13 @@ export type VmCreateTagResponse = { }; }; +/** + * VMListRunningVMsResponse + */ export type VmListRunningVmsResponse = { + /** + * Error + */ errors?: Array; @@ -152,7 +222,13 @@ export type VmListRunningVmsResponse = { }; }; +/** + * SandboxGetResponse + */ export type SandboxGetResponse = { + /** + * Error + */ errors?: Array; @@ -161,6 +237,9 @@ export type SandboxGetResponse = { data?: Sandbox; }; +/** + * SandboxForkRequest + */ export type SandboxForkRequest = { /** * Sandbox description. Maximum 255 characters. Defaults to description of original sandbox. @@ -230,7 +309,13 @@ export type SandboxForkRequest = { title?: string; }; +/** + * SandboxListResponse + */ export type SandboxListResponse = { + /** + * Error + */ errors?: Array; @@ -249,6 +334,9 @@ export type SandboxListResponse = { }; }; +/** + * MetaInformation + */ export type MetaInformation = { /** * Meta information about the CodeSandbox API @@ -287,6 +375,7 @@ export type MetaInformation = { }; /** + * TokenUpdateRequest * Updateable fields for API Tokens. Omitting a field will not update it; explicitly passing null or an empty list will clear the value. */ export type TokenUpdateRequest = { @@ -309,13 +398,20 @@ export type TokenUpdateRequest = { }; /** + * VMAssignTagAliasRequest * Assign a tag alias to a VM */ export type VmAssignTagAliasRequest = { tag_id: string; }; +/** + * VMHibernateResponse + */ export type VmHibernateResponse = { + /** + * Error + */ errors?: Array; @@ -326,7 +422,13 @@ export type VmHibernateResponse = { }; }; +/** + * PreviewTokenUpdateResponse + */ export type PreviewTokenUpdateResponse = { + /** + * Error + */ errors?: Array; @@ -338,7 +440,13 @@ export type PreviewTokenUpdateResponse = { }; }; +/** + * SandboxCreateResponse + */ export type SandboxCreateResponse = { + /** + * Error + */ errors?: Array; @@ -351,6 +459,9 @@ export type SandboxCreateResponse = { }; }; +/** + * VMStartRequest + */ export type VmStartRequest = { /** * Configuration for when the VM should automatically wake up from hibernation @@ -387,6 +498,9 @@ export type VmStartRequest = { tier?: 'Pico' | 'Nano' | 'Micro' | 'Small' | 'Medium' | 'Large' | 'XLarge'; }; +/** + * PreviewTokenUpdateRequest + */ export type PreviewTokenUpdateRequest = { /** * UTC Timestamp until when this token is valid. Omitting this field will create a token without an expiry. @@ -394,7 +508,13 @@ export type PreviewTokenUpdateRequest = { expires_at?: string | null; }; +/** + * PreviewHostListResponse + */ export type PreviewHostListResponse = { + /** + * Error + */ errors?: Array; @@ -408,10 +528,16 @@ export type PreviewHostListResponse = { }; }; +/** + * VMShutdownRequest + */ export type VmShutdownRequest = { [key: string]: unknown; }; +/** + * VMUpdateSpecsRequest + */ export type VmUpdateSpecsRequest = { /** * Determines which specs to update the VM with. @@ -422,6 +548,9 @@ export type VmUpdateSpecsRequest = { tier: 'Pico' | 'Nano' | 'Micro' | 'Small' | 'Medium' | 'Large' | 'XLarge'; }; +/** + * WorkspaceCreateRequest + */ export type WorkspaceCreateRequest = { /** * Name for the new workspace. Maximum length 64 characters. @@ -430,17 +559,27 @@ export type WorkspaceCreateRequest = { }; /** + * VMCreateTagRequest * Create a tag for a list of VM IDs */ export type VmCreateTagRequest = { vm_ids: Array; }; +/** + * PreviewHostRequest + */ export type PreviewHostRequest = { hosts: Array; }; +/** + * VMStartResponse + */ export type VmStartResponse = { + /** + * Error + */ errors?: Array; @@ -456,12 +595,19 @@ export type VmStartResponse = { pitcher_url: string; pitcher_version: string; reconnect_token: string; + use_pint: boolean; user_workspace_path: string; workspace_path: string; }; }; +/** + * VMUpdateSpecsResponse + */ export type VmUpdateSpecsResponse = { + /** + * Error + */ errors?: Array; @@ -473,6 +619,9 @@ export type VmUpdateSpecsResponse = { }; }; +/** + * SandboxCreateRequest + */ export type SandboxCreateRequest = { /** * Optional text description of the sandbox. Defaults to no description. @@ -554,7 +703,13 @@ export type SandboxCreateRequest = { title?: string; }; +/** + * VMListClustersResponse + */ export type VmListClustersResponse = { + /** + * Error + */ errors?: Array; @@ -568,7 +723,13 @@ export type VmListClustersResponse = { }; }; +/** + * TokenUpdateResponse + */ export type TokenUpdateResponse = { + /** + * Error + */ errors?: Array; @@ -583,7 +744,13 @@ export type TokenUpdateResponse = { }; }; +/** + * TokenCreateResponse + */ export type TokenCreateResponse = { + /** + * Error + */ errors?: Array; @@ -599,7 +766,13 @@ export type TokenCreateResponse = { }; }; +/** + * VMDeleteResponse + */ export type VmDeleteResponse = { + /** + * Error + */ errors?: Array; @@ -610,7 +783,13 @@ export type VmDeleteResponse = { }; }; +/** + * TemplateCreateResponse + */ export type TemplateCreateResponse = { + /** + * Error + */ errors?: Array; @@ -625,6 +804,9 @@ export type TemplateCreateResponse = { }; }; +/** + * TokenCreateRequest + */ export type TokenCreateRequest = { /** * API Version to use, formatted as YYYY-MM-DD. Defaults to the latest version at time of creation. @@ -644,6 +826,9 @@ export type TokenCreateRequest = { scopes?: Array<'sandbox_create' | 'sandbox_edit_code' | 'sandbox_read' | 'vm_manage'>; }; +/** + * VMCreateSessionRequest + */ export type VmCreateSessionRequest = { /** * GitHub token for the session @@ -667,7 +852,13 @@ export type VmCreateSessionRequest = { session_id: string; }; +/** + * VMCreateSessionResponse + */ export type VmCreateSessionResponse = { + /** + * Error + */ errors?: Array; @@ -703,7 +894,13 @@ export type VmCreateSessionResponse = { }; }; +/** + * WorkspaceCreateResponse + */ export type WorkspaceCreateResponse = { + /** + * Error + */ errors?: Array; @@ -715,7 +912,13 @@ export type WorkspaceCreateResponse = { }; }; +/** + * VMUpdateHibernationTimeoutResponse + */ export type VmUpdateHibernationTimeoutResponse = { + /** + * Error + */ errors?: Array; @@ -727,7 +930,13 @@ export type VmUpdateHibernationTimeoutResponse = { }; }; +/** + * SandboxForkResponse + */ export type SandboxForkResponse = { + /** + * Error + */ errors?: Array; @@ -749,6 +958,7 @@ export type SandboxForkResponse = { pitcher_url: string; pitcher_version: string; reconnect_token: string; + use_pint: boolean; user_workspace_path: string; workspace_path: string; } | null; @@ -756,7 +966,13 @@ export type SandboxForkResponse = { }; }; +/** + * PreviewTokenCreateResponse + */ export type PreviewTokenCreateResponse = { + /** + * Error + */ errors?: Array; @@ -1400,4 +1616,4 @@ export type PreviewHostUpdateResponses = { 201: PreviewHostListResponse; }; -export type PreviewHostUpdateResponse = PreviewHostUpdateResponses[keyof PreviewHostUpdateResponses]; \ No newline at end of file +export type PreviewHostUpdateResponse = PreviewHostUpdateResponses[keyof PreviewHostUpdateResponses]; diff --git a/src/api-clients/pint/client.gen.ts b/src/api-clients/pint/client.gen.ts new file mode 100644 index 0000000..4a7b977 --- /dev/null +++ b/src/api-clients/pint/client.gen.ts @@ -0,0 +1,18 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import { type ClientOptions, type Config, createClient, createConfig } from './client'; +import type { ClientOptions as ClientOptions2 } from './types.gen'; + +/** + * The `createClientConfig()` function will be called on client initialization + * and the returned object will become the client's initial configuration. + * + * You may want to initialize your client this way instead of calling + * `setConfig()`. This is useful for example if you're using Next.js + * to ensure your client always has the correct values. + */ +export type CreateClientConfig = (override?: Config) => Config & T>; + +export const client = createClient(createConfig({ + baseUrl: 'http://localhost:57468' +})); diff --git a/src/api-clients/pint/client/client.gen.ts b/src/api-clients/pint/client/client.gen.ts new file mode 100644 index 0000000..a439d27 --- /dev/null +++ b/src/api-clients/pint/client/client.gen.ts @@ -0,0 +1,268 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import { createSseClient } from '../core/serverSentEvents.gen'; +import type { HttpMethod } from '../core/types.gen'; +import { getValidRequestBody } from '../core/utils.gen'; +import type { + Client, + Config, + RequestOptions, + ResolvedRequestOptions, +} from './types.gen'; +import { + buildUrl, + createConfig, + createInterceptors, + getParseAs, + mergeConfigs, + mergeHeaders, + setAuthParams, +} from './utils.gen'; + +type ReqInit = Omit & { + body?: any; + headers: ReturnType; +}; + +export const createClient = (config: Config = {}): Client => { + let _config = mergeConfigs(createConfig(), config); + + const getConfig = (): Config => ({ ..._config }); + + const setConfig = (config: Config): Config => { + _config = mergeConfigs(_config, config); + return getConfig(); + }; + + const interceptors = createInterceptors< + Request, + Response, + unknown, + ResolvedRequestOptions + >(); + + const beforeRequest = async (options: RequestOptions) => { + const opts = { + ..._config, + ...options, + fetch: options.fetch ?? _config.fetch ?? globalThis.fetch, + headers: mergeHeaders(_config.headers, options.headers), + serializedBody: undefined, + }; + + if (opts.security) { + await setAuthParams({ + ...opts, + security: opts.security, + }); + } + + if (opts.requestValidator) { + await opts.requestValidator(opts); + } + + if (opts.body !== undefined && opts.bodySerializer) { + opts.serializedBody = opts.bodySerializer(opts.body); + } + + // remove Content-Type header if body is empty to avoid sending invalid requests + if (opts.body === undefined || opts.serializedBody === '') { + opts.headers.delete('Content-Type'); + } + + const url = buildUrl(opts); + + return { opts, url }; + }; + + const request: Client['request'] = async (options) => { + // @ts-expect-error + const { opts, url } = await beforeRequest(options); + const requestInit: ReqInit = { + redirect: 'follow', + ...opts, + body: getValidRequestBody(opts), + }; + + let request = new Request(url, requestInit); + + for (const fn of interceptors.request.fns) { + if (fn) { + request = await fn(request, opts); + } + } + + // fetch must be assigned here, otherwise it would throw the error: + // TypeError: Failed to execute 'fetch' on 'Window': Illegal invocation + const _fetch = opts.fetch!; + let response = await _fetch(request); + + for (const fn of interceptors.response.fns) { + if (fn) { + response = await fn(response, request, opts); + } + } + + const result = { + request, + response, + }; + + if (response.ok) { + const parseAs = + (opts.parseAs === 'auto' + ? getParseAs(response.headers.get('Content-Type')) + : opts.parseAs) ?? 'json'; + + if ( + response.status === 204 || + response.headers.get('Content-Length') === '0' + ) { + let emptyData: any; + switch (parseAs) { + case 'arrayBuffer': + case 'blob': + case 'text': + emptyData = await response[parseAs](); + break; + case 'formData': + emptyData = new FormData(); + break; + case 'stream': + emptyData = response.body; + break; + case 'json': + default: + emptyData = {}; + break; + } + return opts.responseStyle === 'data' + ? emptyData + : { + data: emptyData, + ...result, + }; + } + + let data: any; + switch (parseAs) { + case 'arrayBuffer': + case 'blob': + case 'formData': + case 'json': + case 'text': + data = await response[parseAs](); + break; + case 'stream': + return opts.responseStyle === 'data' + ? response.body + : { + data: response.body, + ...result, + }; + } + + if (parseAs === 'json') { + if (opts.responseValidator) { + await opts.responseValidator(data); + } + + if (opts.responseTransformer) { + data = await opts.responseTransformer(data); + } + } + + return opts.responseStyle === 'data' + ? data + : { + data, + ...result, + }; + } + + const textError = await response.text(); + let jsonError: unknown; + + try { + jsonError = JSON.parse(textError); + } catch { + // noop + } + + const error = jsonError ?? textError; + let finalError = error; + + for (const fn of interceptors.error.fns) { + if (fn) { + finalError = (await fn(error, response, request, opts)) as string; + } + } + + finalError = finalError || ({} as string); + + if (opts.throwOnError) { + throw finalError; + } + + // TODO: we probably want to return error and improve types + return opts.responseStyle === 'data' + ? undefined + : { + error: finalError, + ...result, + }; + }; + + const makeMethodFn = + (method: Uppercase) => (options: RequestOptions) => + request({ ...options, method }); + + const makeSseFn = + (method: Uppercase) => async (options: RequestOptions) => { + const { opts, url } = await beforeRequest(options); + return createSseClient({ + ...opts, + body: opts.body as BodyInit | null | undefined, + headers: opts.headers as unknown as Record, + method, + onRequest: async (url, init) => { + let request = new Request(url, init); + for (const fn of interceptors.request.fns) { + if (fn) { + request = await fn(request, opts); + } + } + return request; + }, + url, + }); + }; + + return { + buildUrl, + connect: makeMethodFn('CONNECT'), + delete: makeMethodFn('DELETE'), + get: makeMethodFn('GET'), + getConfig, + head: makeMethodFn('HEAD'), + interceptors, + options: makeMethodFn('OPTIONS'), + patch: makeMethodFn('PATCH'), + post: makeMethodFn('POST'), + put: makeMethodFn('PUT'), + request, + setConfig, + sse: { + connect: makeSseFn('CONNECT'), + delete: makeSseFn('DELETE'), + get: makeSseFn('GET'), + head: makeSseFn('HEAD'), + options: makeSseFn('OPTIONS'), + patch: makeSseFn('PATCH'), + post: makeSseFn('POST'), + put: makeSseFn('PUT'), + trace: makeSseFn('TRACE'), + }, + trace: makeMethodFn('TRACE'), + } as Client; +}; diff --git a/src/api-clients/pint/client/index.ts b/src/api-clients/pint/client/index.ts new file mode 100644 index 0000000..318a84b --- /dev/null +++ b/src/api-clients/pint/client/index.ts @@ -0,0 +1,25 @@ +// This file is auto-generated by @hey-api/openapi-ts + +export type { Auth } from '../core/auth.gen'; +export type { QuerySerializerOptions } from '../core/bodySerializer.gen'; +export { + formDataBodySerializer, + jsonBodySerializer, + urlSearchParamsBodySerializer, +} from '../core/bodySerializer.gen'; +export { buildClientParams } from '../core/params.gen'; +export { createClient } from './client.gen'; +export type { + Client, + ClientOptions, + Config, + CreateClientConfig, + Options, + OptionsLegacyParser, + RequestOptions, + RequestResult, + ResolvedRequestOptions, + ResponseStyle, + TDataShape, +} from './types.gen'; +export { createConfig, mergeHeaders } from './utils.gen'; diff --git a/src/api-clients/pint/client/types.gen.ts b/src/api-clients/pint/client/types.gen.ts new file mode 100644 index 0000000..1a005b5 --- /dev/null +++ b/src/api-clients/pint/client/types.gen.ts @@ -0,0 +1,268 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import type { Auth } from '../core/auth.gen'; +import type { + ServerSentEventsOptions, + ServerSentEventsResult, +} from '../core/serverSentEvents.gen'; +import type { + Client as CoreClient, + Config as CoreConfig, +} from '../core/types.gen'; +import type { Middleware } from './utils.gen'; + +export type ResponseStyle = 'data' | 'fields'; + +export interface Config + extends Omit, + CoreConfig { + /** + * Base URL for all requests made by this client. + */ + baseUrl?: T['baseUrl']; + /** + * Fetch API implementation. You can use this option to provide a custom + * fetch instance. + * + * @default globalThis.fetch + */ + fetch?: typeof fetch; + /** + * Please don't use the Fetch client for Next.js applications. The `next` + * options won't have any effect. + * + * Install {@link https://www.npmjs.com/package/@hey-api/client-next `@hey-api/client-next`} instead. + */ + next?: never; + /** + * Return the response data parsed in a specified format. By default, `auto` + * will infer the appropriate method from the `Content-Type` response header. + * You can override this behavior with any of the {@link Body} methods. + * Select `stream` if you don't want to parse response data at all. + * + * @default 'auto' + */ + parseAs?: + | 'arrayBuffer' + | 'auto' + | 'blob' + | 'formData' + | 'json' + | 'stream' + | 'text'; + /** + * Should we return only data or multiple fields (data, error, response, etc.)? + * + * @default 'fields' + */ + responseStyle?: ResponseStyle; + /** + * Throw an error instead of returning it in the response? + * + * @default false + */ + throwOnError?: T['throwOnError']; +} + +export interface RequestOptions< + TData = unknown, + TResponseStyle extends ResponseStyle = 'fields', + ThrowOnError extends boolean = boolean, + Url extends string = string, +> extends Config<{ + responseStyle: TResponseStyle; + throwOnError: ThrowOnError; + }>, + Pick< + ServerSentEventsOptions, + | 'onSseError' + | 'onSseEvent' + | 'sseDefaultRetryDelay' + | 'sseMaxRetryAttempts' + | 'sseMaxRetryDelay' + > { + /** + * Any body that you want to add to your request. + * + * {@link https://developer.mozilla.org/docs/Web/API/fetch#body} + */ + body?: unknown; + path?: Record; + query?: Record; + /** + * Security mechanism(s) to use for the request. + */ + security?: ReadonlyArray; + url: Url; +} + +export interface ResolvedRequestOptions< + TResponseStyle extends ResponseStyle = 'fields', + ThrowOnError extends boolean = boolean, + Url extends string = string, +> extends RequestOptions { + serializedBody?: string; +} + +export type RequestResult< + TData = unknown, + TError = unknown, + ThrowOnError extends boolean = boolean, + TResponseStyle extends ResponseStyle = 'fields', +> = ThrowOnError extends true + ? Promise< + TResponseStyle extends 'data' + ? TData extends Record + ? TData[keyof TData] + : TData + : { + data: TData extends Record + ? TData[keyof TData] + : TData; + request: Request; + response: Response; + } + > + : Promise< + TResponseStyle extends 'data' + ? + | (TData extends Record + ? TData[keyof TData] + : TData) + | undefined + : ( + | { + data: TData extends Record + ? TData[keyof TData] + : TData; + error: undefined; + } + | { + data: undefined; + error: TError extends Record + ? TError[keyof TError] + : TError; + } + ) & { + request: Request; + response: Response; + } + >; + +export interface ClientOptions { + baseUrl?: string; + responseStyle?: ResponseStyle; + throwOnError?: boolean; +} + +type MethodFn = < + TData = unknown, + TError = unknown, + ThrowOnError extends boolean = false, + TResponseStyle extends ResponseStyle = 'fields', +>( + options: Omit, 'method'>, +) => RequestResult; + +type SseFn = < + TData = unknown, + TError = unknown, + ThrowOnError extends boolean = false, + TResponseStyle extends ResponseStyle = 'fields', +>( + options: Omit, 'method'>, +) => Promise>; + +type RequestFn = < + TData = unknown, + TError = unknown, + ThrowOnError extends boolean = false, + TResponseStyle extends ResponseStyle = 'fields', +>( + options: Omit, 'method'> & + Pick< + Required>, + 'method' + >, +) => RequestResult; + +type BuildUrlFn = < + TData extends { + body?: unknown; + path?: Record; + query?: Record; + url: string; + }, +>( + options: Pick & Options, +) => string; + +export type Client = CoreClient< + RequestFn, + Config, + MethodFn, + BuildUrlFn, + SseFn +> & { + interceptors: Middleware; +}; + +/** + * The `createClientConfig()` function will be called on client initialization + * and the returned object will become the client's initial configuration. + * + * You may want to initialize your client this way instead of calling + * `setConfig()`. This is useful for example if you're using Next.js + * to ensure your client always has the correct values. + */ +export type CreateClientConfig = ( + override?: Config, +) => Config & T>; + +export interface TDataShape { + body?: unknown; + headers?: unknown; + path?: unknown; + query?: unknown; + url: string; +} + +type OmitKeys = Pick>; + +export type Options< + TData extends TDataShape = TDataShape, + ThrowOnError extends boolean = boolean, + TResponse = unknown, + TResponseStyle extends ResponseStyle = 'fields', +> = OmitKeys< + RequestOptions, + 'body' | 'path' | 'query' | 'url' +> & + Omit; + +export type OptionsLegacyParser< + TData = unknown, + ThrowOnError extends boolean = boolean, + TResponseStyle extends ResponseStyle = 'fields', +> = TData extends { body?: any } + ? TData extends { headers?: any } + ? OmitKeys< + RequestOptions, + 'body' | 'headers' | 'url' + > & + TData + : OmitKeys< + RequestOptions, + 'body' | 'url' + > & + TData & + Pick, 'headers'> + : TData extends { headers?: any } + ? OmitKeys< + RequestOptions, + 'headers' | 'url' + > & + TData & + Pick, 'body'> + : OmitKeys, 'url'> & + TData; diff --git a/src/api-clients/pint/client/utils.gen.ts b/src/api-clients/pint/client/utils.gen.ts new file mode 100644 index 0000000..b4bcc4d --- /dev/null +++ b/src/api-clients/pint/client/utils.gen.ts @@ -0,0 +1,331 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import { getAuthToken } from '../core/auth.gen'; +import type { QuerySerializerOptions } from '../core/bodySerializer.gen'; +import { jsonBodySerializer } from '../core/bodySerializer.gen'; +import { + serializeArrayParam, + serializeObjectParam, + serializePrimitiveParam, +} from '../core/pathSerializer.gen'; +import { getUrl } from '../core/utils.gen'; +import type { Client, ClientOptions, Config, RequestOptions } from './types.gen'; + +export const createQuerySerializer = ({ + allowReserved, + array, + object, +}: QuerySerializerOptions = {}) => { + const querySerializer = (queryParams: T) => { + const search: string[] = []; + if (queryParams && typeof queryParams === 'object') { + for (const name in queryParams) { + const value = queryParams[name]; + + if (value === undefined || value === null) { + continue; + } + + if (Array.isArray(value)) { + const serializedArray = serializeArrayParam({ + allowReserved, + explode: true, + name, + style: 'form', + value, + ...array, + }); + if (serializedArray) search.push(serializedArray); + } else if (typeof value === 'object') { + const serializedObject = serializeObjectParam({ + allowReserved, + explode: true, + name, + style: 'deepObject', + value: value as Record, + ...object, + }); + if (serializedObject) search.push(serializedObject); + } else { + const serializedPrimitive = serializePrimitiveParam({ + allowReserved, + name, + value: value as string, + }); + if (serializedPrimitive) search.push(serializedPrimitive); + } + } + } + return search.join('&'); + }; + return querySerializer; +}; + +/** + * Infers parseAs value from provided Content-Type header. + */ +export const getParseAs = ( + contentType: string | null, +): Exclude => { + if (!contentType) { + // If no Content-Type header is provided, the best we can do is return the raw response body, + // which is effectively the same as the 'stream' option. + return 'stream'; + } + + const cleanContent = contentType.split(';')[0]?.trim(); + + if (!cleanContent) { + return; + } + + if ( + cleanContent.startsWith('application/json') || + cleanContent.endsWith('+json') + ) { + return 'json'; + } + + if (cleanContent === 'multipart/form-data') { + return 'formData'; + } + + if ( + ['application/', 'audio/', 'image/', 'video/'].some((type) => + cleanContent.startsWith(type), + ) + ) { + return 'blob'; + } + + if (cleanContent.startsWith('text/')) { + return 'text'; + } + + return; +}; + +const checkForExistence = ( + options: Pick & { + headers: Headers; + }, + name?: string, +): boolean => { + if (!name) { + return false; + } + if ( + options.headers.has(name) || + options.query?.[name] || + options.headers.get('Cookie')?.includes(`${name}=`) + ) { + return true; + } + return false; +}; + +export const setAuthParams = async ({ + security, + ...options +}: Pick, 'security'> & + Pick & { + headers: Headers; + }) => { + for (const auth of security) { + if (checkForExistence(options, auth.name)) { + continue; + } + + const token = await getAuthToken(auth, options.auth); + + if (!token) { + continue; + } + + const name = auth.name ?? 'Authorization'; + + switch (auth.in) { + case 'query': + if (!options.query) { + options.query = {}; + } + options.query[name] = token; + break; + case 'cookie': + options.headers.append('Cookie', `${name}=${token}`); + break; + case 'header': + default: + options.headers.set(name, token); + break; + } + } +}; + +export const buildUrl: Client['buildUrl'] = (options) => + getUrl({ + baseUrl: options.baseUrl as string, + path: options.path, + query: options.query, + querySerializer: + typeof options.querySerializer === 'function' + ? options.querySerializer + : createQuerySerializer(options.querySerializer), + url: options.url, + }); + +export const mergeConfigs = (a: Config, b: Config): Config => { + const config = { ...a, ...b }; + if (config.baseUrl?.endsWith('/')) { + config.baseUrl = config.baseUrl.substring(0, config.baseUrl.length - 1); + } + config.headers = mergeHeaders(a.headers, b.headers); + return config; +}; + +const headersEntries = (headers: Headers): Array<[string, string]> => { + const entries: Array<[string, string]> = []; + headers.forEach((value, key) => { + entries.push([key, value]); + }); + return entries; +}; + +export const mergeHeaders = ( + ...headers: Array['headers'] | undefined> +): Headers => { + const mergedHeaders = new Headers(); + for (const header of headers) { + if (!header) { + continue; + } + + const iterator = + header instanceof Headers + ? headersEntries(header) + : Object.entries(header); + + for (const [key, value] of iterator) { + if (value === null) { + mergedHeaders.delete(key); + } else if (Array.isArray(value)) { + for (const v of value) { + mergedHeaders.append(key, v as string); + } + } else if (value !== undefined) { + // assume object headers are meant to be JSON stringified, i.e. their + // content value in OpenAPI specification is 'application/json' + mergedHeaders.set( + key, + typeof value === 'object' ? JSON.stringify(value) : (value as string), + ); + } + } + } + return mergedHeaders; +}; + +type ErrInterceptor = ( + error: Err, + response: Res, + request: Req, + options: Options, +) => Err | Promise; + +type ReqInterceptor = ( + request: Req, + options: Options, +) => Req | Promise; + +type ResInterceptor = ( + response: Res, + request: Req, + options: Options, +) => Res | Promise; + +class Interceptors { + fns: Array = []; + + clear(): void { + this.fns = []; + } + + eject(id: number | Interceptor): void { + const index = this.getInterceptorIndex(id); + if (this.fns[index]) { + this.fns[index] = null; + } + } + + exists(id: number | Interceptor): boolean { + const index = this.getInterceptorIndex(id); + return Boolean(this.fns[index]); + } + + getInterceptorIndex(id: number | Interceptor): number { + if (typeof id === 'number') { + return this.fns[id] ? id : -1; + } + return this.fns.indexOf(id); + } + + update( + id: number | Interceptor, + fn: Interceptor, + ): number | Interceptor | false { + const index = this.getInterceptorIndex(id); + if (this.fns[index]) { + this.fns[index] = fn; + return id; + } + return false; + } + + use(fn: Interceptor): number { + this.fns.push(fn); + return this.fns.length - 1; + } +} + +export interface Middleware { + error: Interceptors>; + request: Interceptors>; + response: Interceptors>; +} + +export const createInterceptors = (): Middleware< + Req, + Res, + Err, + Options +> => ({ + error: new Interceptors>(), + request: new Interceptors>(), + response: new Interceptors>(), +}); + +const defaultQuerySerializer = createQuerySerializer({ + allowReserved: false, + array: { + explode: true, + style: 'form', + }, + object: { + explode: true, + style: 'deepObject', + }, +}); + +const defaultHeaders = { + 'Content-Type': 'application/json', +}; + +export const createConfig = ( + override: Config & T> = {}, +): Config & T> => ({ + ...jsonBodySerializer, + headers: defaultHeaders, + parseAs: 'auto', + querySerializer: defaultQuerySerializer, + ...override, +}); diff --git a/src/api-clients/pint/core/auth.gen.ts b/src/api-clients/pint/core/auth.gen.ts new file mode 100644 index 0000000..f8a7326 --- /dev/null +++ b/src/api-clients/pint/core/auth.gen.ts @@ -0,0 +1,42 @@ +// This file is auto-generated by @hey-api/openapi-ts + +export type AuthToken = string | undefined; + +export interface Auth { + /** + * Which part of the request do we use to send the auth? + * + * @default 'header' + */ + in?: 'header' | 'query' | 'cookie'; + /** + * Header or query parameter name. + * + * @default 'Authorization' + */ + name?: string; + scheme?: 'basic' | 'bearer'; + type: 'apiKey' | 'http'; +} + +export const getAuthToken = async ( + auth: Auth, + callback: ((auth: Auth) => Promise | AuthToken) | AuthToken, +): Promise => { + const token = + typeof callback === 'function' ? await callback(auth) : callback; + + if (!token) { + return; + } + + if (auth.scheme === 'bearer') { + return `Bearer ${token}`; + } + + if (auth.scheme === 'basic') { + return `Basic ${btoa(token)}`; + } + + return token; +}; diff --git a/src/api-clients/pint/core/bodySerializer.gen.ts b/src/api-clients/pint/core/bodySerializer.gen.ts new file mode 100644 index 0000000..49cd892 --- /dev/null +++ b/src/api-clients/pint/core/bodySerializer.gen.ts @@ -0,0 +1,92 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import type { + ArrayStyle, + ObjectStyle, + SerializerOptions, +} from './pathSerializer.gen'; + +export type QuerySerializer = (query: Record) => string; + +export type BodySerializer = (body: any) => any; + +export interface QuerySerializerOptions { + allowReserved?: boolean; + array?: SerializerOptions; + object?: SerializerOptions; +} + +const serializeFormDataPair = ( + data: FormData, + key: string, + value: unknown, +): void => { + if (typeof value === 'string' || value instanceof Blob) { + data.append(key, value); + } else if (value instanceof Date) { + data.append(key, value.toISOString()); + } else { + data.append(key, JSON.stringify(value)); + } +}; + +const serializeUrlSearchParamsPair = ( + data: URLSearchParams, + key: string, + value: unknown, +): void => { + if (typeof value === 'string') { + data.append(key, value); + } else { + data.append(key, JSON.stringify(value)); + } +}; + +export const formDataBodySerializer = { + bodySerializer: | Array>>( + body: T, + ): FormData => { + const data = new FormData(); + + Object.entries(body).forEach(([key, value]) => { + if (value === undefined || value === null) { + return; + } + if (Array.isArray(value)) { + value.forEach((v) => serializeFormDataPair(data, key, v)); + } else { + serializeFormDataPair(data, key, value); + } + }); + + return data; + }, +}; + +export const jsonBodySerializer = { + bodySerializer: (body: T): string => + JSON.stringify(body, (_key, value) => + typeof value === 'bigint' ? value.toString() : value, + ), +}; + +export const urlSearchParamsBodySerializer = { + bodySerializer: | Array>>( + body: T, + ): string => { + const data = new URLSearchParams(); + + Object.entries(body).forEach(([key, value]) => { + if (value === undefined || value === null) { + return; + } + if (Array.isArray(value)) { + value.forEach((v) => serializeUrlSearchParamsPair(data, key, v)); + } else { + serializeUrlSearchParamsPair(data, key, value); + } + }); + + return data.toString(); + }, +}; diff --git a/src/api-clients/pint/core/params.gen.ts b/src/api-clients/pint/core/params.gen.ts new file mode 100644 index 0000000..71c88e8 --- /dev/null +++ b/src/api-clients/pint/core/params.gen.ts @@ -0,0 +1,153 @@ +// This file is auto-generated by @hey-api/openapi-ts + +type Slot = 'body' | 'headers' | 'path' | 'query'; + +export type Field = + | { + in: Exclude; + /** + * Field name. This is the name we want the user to see and use. + */ + key: string; + /** + * Field mapped name. This is the name we want to use in the request. + * If omitted, we use the same value as `key`. + */ + map?: string; + } + | { + in: Extract; + /** + * Key isn't required for bodies. + */ + key?: string; + map?: string; + }; + +export interface Fields { + allowExtra?: Partial>; + args?: ReadonlyArray; +} + +export type FieldsConfig = ReadonlyArray; + +const extraPrefixesMap: Record = { + $body_: 'body', + $headers_: 'headers', + $path_: 'path', + $query_: 'query', +}; +const extraPrefixes = Object.entries(extraPrefixesMap); + +type KeyMap = Map< + string, + { + in: Slot; + map?: string; + } +>; + +const buildKeyMap = (fields: FieldsConfig, map?: KeyMap): KeyMap => { + if (!map) { + map = new Map(); + } + + for (const config of fields) { + if ('in' in config) { + if (config.key) { + map.set(config.key, { + in: config.in, + map: config.map, + }); + } + } else if (config.args) { + buildKeyMap(config.args, map); + } + } + + return map; +}; + +interface Params { + body: unknown; + headers: Record; + path: Record; + query: Record; +} + +const stripEmptySlots = (params: Params) => { + for (const [slot, value] of Object.entries(params)) { + if (value && typeof value === 'object' && !Object.keys(value).length) { + delete params[slot as Slot]; + } + } +}; + +export const buildClientParams = ( + args: ReadonlyArray, + fields: FieldsConfig, +) => { + const params: Params = { + body: {}, + headers: {}, + path: {}, + query: {}, + }; + + const map = buildKeyMap(fields); + + let config: FieldsConfig[number] | undefined; + + for (const [index, arg] of args.entries()) { + if (fields[index]) { + config = fields[index]; + } + + if (!config) { + continue; + } + + if ('in' in config) { + if (config.key) { + const field = map.get(config.key)!; + const name = field.map || config.key; + (params[field.in] as Record)[name] = arg; + } else { + params.body = arg; + } + } else { + for (const [key, value] of Object.entries(arg ?? {})) { + const field = map.get(key); + + if (field) { + const name = field.map || key; + (params[field.in] as Record)[name] = value; + } else { + const extra = extraPrefixes.find(([prefix]) => + key.startsWith(prefix), + ); + + if (extra) { + const [prefix, slot] = extra; + (params[slot] as Record)[ + key.slice(prefix.length) + ] = value; + } else { + for (const [slot, allowed] of Object.entries( + config.allowExtra ?? {}, + )) { + if (allowed) { + (params[slot as Slot] as Record)[key] = value; + break; + } + } + } + } + } + } + } + + stripEmptySlots(params); + + return params; +}; diff --git a/src/api-clients/pint/core/pathSerializer.gen.ts b/src/api-clients/pint/core/pathSerializer.gen.ts new file mode 100644 index 0000000..8d99931 --- /dev/null +++ b/src/api-clients/pint/core/pathSerializer.gen.ts @@ -0,0 +1,181 @@ +// This file is auto-generated by @hey-api/openapi-ts + +interface SerializeOptions + extends SerializePrimitiveOptions, + SerializerOptions {} + +interface SerializePrimitiveOptions { + allowReserved?: boolean; + name: string; +} + +export interface SerializerOptions { + /** + * @default true + */ + explode: boolean; + style: T; +} + +export type ArrayStyle = 'form' | 'spaceDelimited' | 'pipeDelimited'; +export type ArraySeparatorStyle = ArrayStyle | MatrixStyle; +type MatrixStyle = 'label' | 'matrix' | 'simple'; +export type ObjectStyle = 'form' | 'deepObject'; +type ObjectSeparatorStyle = ObjectStyle | MatrixStyle; + +interface SerializePrimitiveParam extends SerializePrimitiveOptions { + value: string; +} + +export const separatorArrayExplode = (style: ArraySeparatorStyle) => { + switch (style) { + case 'label': + return '.'; + case 'matrix': + return ';'; + case 'simple': + return ','; + default: + return '&'; + } +}; + +export const separatorArrayNoExplode = (style: ArraySeparatorStyle) => { + switch (style) { + case 'form': + return ','; + case 'pipeDelimited': + return '|'; + case 'spaceDelimited': + return '%20'; + default: + return ','; + } +}; + +export const separatorObjectExplode = (style: ObjectSeparatorStyle) => { + switch (style) { + case 'label': + return '.'; + case 'matrix': + return ';'; + case 'simple': + return ','; + default: + return '&'; + } +}; + +export const serializeArrayParam = ({ + allowReserved, + explode, + name, + style, + value, +}: SerializeOptions & { + value: unknown[]; +}) => { + if (!explode) { + const joinedValues = ( + allowReserved ? value : value.map((v) => encodeURIComponent(v as string)) + ).join(separatorArrayNoExplode(style)); + switch (style) { + case 'label': + return `.${joinedValues}`; + case 'matrix': + return `;${name}=${joinedValues}`; + case 'simple': + return joinedValues; + default: + return `${name}=${joinedValues}`; + } + } + + const separator = separatorArrayExplode(style); + const joinedValues = value + .map((v) => { + if (style === 'label' || style === 'simple') { + return allowReserved ? v : encodeURIComponent(v as string); + } + + return serializePrimitiveParam({ + allowReserved, + name, + value: v as string, + }); + }) + .join(separator); + return style === 'label' || style === 'matrix' + ? separator + joinedValues + : joinedValues; +}; + +export const serializePrimitiveParam = ({ + allowReserved, + name, + value, +}: SerializePrimitiveParam) => { + if (value === undefined || value === null) { + return ''; + } + + if (typeof value === 'object') { + throw new Error( + 'Deeply-nested arrays/objects aren’t supported. Provide your own `querySerializer()` to handle these.', + ); + } + + return `${name}=${allowReserved ? value : encodeURIComponent(value)}`; +}; + +export const serializeObjectParam = ({ + allowReserved, + explode, + name, + style, + value, + valueOnly, +}: SerializeOptions & { + value: Record | Date; + valueOnly?: boolean; +}) => { + if (value instanceof Date) { + return valueOnly ? value.toISOString() : `${name}=${value.toISOString()}`; + } + + if (style !== 'deepObject' && !explode) { + let values: string[] = []; + Object.entries(value).forEach(([key, v]) => { + values = [ + ...values, + key, + allowReserved ? (v as string) : encodeURIComponent(v as string), + ]; + }); + const joinedValues = values.join(','); + switch (style) { + case 'form': + return `${name}=${joinedValues}`; + case 'label': + return `.${joinedValues}`; + case 'matrix': + return `;${name}=${joinedValues}`; + default: + return joinedValues; + } + } + + const separator = separatorObjectExplode(style); + const joinedValues = Object.entries(value) + .map(([key, v]) => + serializePrimitiveParam({ + allowReserved, + name: style === 'deepObject' ? `${name}[${key}]` : key, + value: v as string, + }), + ) + .join(separator); + return style === 'label' || style === 'matrix' + ? separator + joinedValues + : joinedValues; +}; diff --git a/src/api-clients/pint/core/serverSentEvents.gen.ts b/src/api-clients/pint/core/serverSentEvents.gen.ts new file mode 100644 index 0000000..f8fd78e --- /dev/null +++ b/src/api-clients/pint/core/serverSentEvents.gen.ts @@ -0,0 +1,264 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import type { Config } from './types.gen'; + +export type ServerSentEventsOptions = Omit< + RequestInit, + 'method' +> & + Pick & { + /** + * Fetch API implementation. You can use this option to provide a custom + * fetch instance. + * + * @default globalThis.fetch + */ + fetch?: typeof fetch; + /** + * Implementing clients can call request interceptors inside this hook. + */ + onRequest?: (url: string, init: RequestInit) => Promise; + /** + * Callback invoked when a network or parsing error occurs during streaming. + * + * This option applies only if the endpoint returns a stream of events. + * + * @param error The error that occurred. + */ + onSseError?: (error: unknown) => void; + /** + * Callback invoked when an event is streamed from the server. + * + * This option applies only if the endpoint returns a stream of events. + * + * @param event Event streamed from the server. + * @returns Nothing (void). + */ + onSseEvent?: (event: StreamEvent) => void; + serializedBody?: RequestInit['body']; + /** + * Default retry delay in milliseconds. + * + * This option applies only if the endpoint returns a stream of events. + * + * @default 3000 + */ + sseDefaultRetryDelay?: number; + /** + * Maximum number of retry attempts before giving up. + */ + sseMaxRetryAttempts?: number; + /** + * Maximum retry delay in milliseconds. + * + * Applies only when exponential backoff is used. + * + * This option applies only if the endpoint returns a stream of events. + * + * @default 30000 + */ + sseMaxRetryDelay?: number; + /** + * Optional sleep function for retry backoff. + * + * Defaults to using `setTimeout`. + */ + sseSleepFn?: (ms: number) => Promise; + url: string; + }; + +export interface StreamEvent { + data: TData; + event?: string; + id?: string; + retry?: number; +} + +export type ServerSentEventsResult< + TData = unknown, + TReturn = void, + TNext = unknown, +> = { + stream: AsyncGenerator< + TData extends Record ? TData[keyof TData] : TData, + TReturn, + TNext + >; +}; + +export const createSseClient = ({ + onRequest, + onSseError, + onSseEvent, + responseTransformer, + responseValidator, + sseDefaultRetryDelay, + sseMaxRetryAttempts, + sseMaxRetryDelay, + sseSleepFn, + url, + ...options +}: ServerSentEventsOptions): ServerSentEventsResult => { + let lastEventId: string | undefined; + + const sleep = + sseSleepFn ?? + ((ms: number) => new Promise((resolve) => setTimeout(resolve, ms))); + + const createStream = async function* () { + let retryDelay: number = sseDefaultRetryDelay ?? 3000; + let attempt = 0; + const signal = options.signal ?? new AbortController().signal; + + while (true) { + if (signal.aborted) break; + + attempt++; + + const headers = + options.headers instanceof Headers + ? options.headers + : new Headers(options.headers as Record | undefined); + + if (lastEventId !== undefined) { + headers.set('Last-Event-ID', lastEventId); + } + + try { + const requestInit: RequestInit = { + redirect: 'follow', + ...options, + body: options.serializedBody, + headers, + signal, + }; + let request = new Request(url, requestInit); + if (onRequest) { + request = await onRequest(url, requestInit); + } + // fetch must be assigned here, otherwise it would throw the error: + // TypeError: Failed to execute 'fetch' on 'Window': Illegal invocation + const _fetch = options.fetch ?? globalThis.fetch; + const response = await _fetch(request); + + if (!response.ok) + throw new Error( + `SSE failed: ${response.status} ${response.statusText}`, + ); + + if (!response.body) throw new Error('No body in SSE response'); + + const reader = response.body + .pipeThrough(new TextDecoderStream()) + .getReader(); + + let buffer = ''; + + const abortHandler = () => { + try { + reader.cancel(); + } catch { + // noop + } + }; + + signal.addEventListener('abort', abortHandler); + + try { + while (true) { + const { done, value } = await reader.read(); + if (done) break; + buffer += value; + + const chunks = buffer.split('\n\n'); + buffer = chunks.pop() ?? ''; + + for (const chunk of chunks) { + const lines = chunk.split('\n'); + const dataLines: Array = []; + let eventName: string | undefined; + + for (const line of lines) { + if (line.startsWith('data:')) { + dataLines.push(line.replace(/^data:\s*/, '')); + } else if (line.startsWith('event:')) { + eventName = line.replace(/^event:\s*/, ''); + } else if (line.startsWith('id:')) { + lastEventId = line.replace(/^id:\s*/, ''); + } else if (line.startsWith('retry:')) { + const parsed = Number.parseInt( + line.replace(/^retry:\s*/, ''), + 10, + ); + if (!Number.isNaN(parsed)) { + retryDelay = parsed; + } + } + } + + let data: unknown; + let parsedJson = false; + + if (dataLines.length) { + const rawData = dataLines.join('\n'); + try { + data = JSON.parse(rawData); + parsedJson = true; + } catch { + data = rawData; + } + } + + if (parsedJson) { + if (responseValidator) { + await responseValidator(data); + } + + if (responseTransformer) { + data = await responseTransformer(data); + } + } + + onSseEvent?.({ + data, + event: eventName, + id: lastEventId, + retry: retryDelay, + }); + + if (dataLines.length) { + yield data as any; + } + } + } + } finally { + signal.removeEventListener('abort', abortHandler); + reader.releaseLock(); + } + + break; // exit loop on normal completion + } catch (error) { + // connection failed or aborted; retry after delay + onSseError?.(error); + + if ( + sseMaxRetryAttempts !== undefined && + attempt >= sseMaxRetryAttempts + ) { + break; // stop after firing error + } + + // exponential backoff: double retry each attempt, cap at 30s + const backoff = Math.min( + retryDelay * 2 ** (attempt - 1), + sseMaxRetryDelay ?? 30000, + ); + await sleep(backoff); + } + } + }; + + const stream = createStream(); + + return { stream }; +}; diff --git a/src/api-clients/pint/core/types.gen.ts b/src/api-clients/pint/core/types.gen.ts new file mode 100644 index 0000000..643c070 --- /dev/null +++ b/src/api-clients/pint/core/types.gen.ts @@ -0,0 +1,118 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import type { Auth, AuthToken } from './auth.gen'; +import type { + BodySerializer, + QuerySerializer, + QuerySerializerOptions, +} from './bodySerializer.gen'; + +export type HttpMethod = + | 'connect' + | 'delete' + | 'get' + | 'head' + | 'options' + | 'patch' + | 'post' + | 'put' + | 'trace'; + +export type Client< + RequestFn = never, + Config = unknown, + MethodFn = never, + BuildUrlFn = never, + SseFn = never, +> = { + /** + * Returns the final request URL. + */ + buildUrl: BuildUrlFn; + getConfig: () => Config; + request: RequestFn; + setConfig: (config: Config) => Config; +} & { + [K in HttpMethod]: MethodFn; +} & ([SseFn] extends [never] + ? { sse?: never } + : { sse: { [K in HttpMethod]: SseFn } }); + +export interface Config { + /** + * Auth token or a function returning auth token. The resolved value will be + * added to the request payload as defined by its `security` array. + */ + auth?: ((auth: Auth) => Promise | AuthToken) | AuthToken; + /** + * A function for serializing request body parameter. By default, + * {@link JSON.stringify()} will be used. + */ + bodySerializer?: BodySerializer | null; + /** + * An object containing any HTTP headers that you want to pre-populate your + * `Headers` object with. + * + * {@link https://developer.mozilla.org/docs/Web/API/Headers/Headers#init See more} + */ + headers?: + | RequestInit['headers'] + | Record< + string, + | string + | number + | boolean + | (string | number | boolean)[] + | null + | undefined + | unknown + >; + /** + * The request method. + * + * {@link https://developer.mozilla.org/docs/Web/API/fetch#method See more} + */ + method?: Uppercase; + /** + * A function for serializing request query parameters. By default, arrays + * will be exploded in form style, objects will be exploded in deepObject + * style, and reserved characters are percent-encoded. + * + * This method will have no effect if the native `paramsSerializer()` Axios + * API function is used. + * + * {@link https://swagger.io/docs/specification/serialization/#query View examples} + */ + querySerializer?: QuerySerializer | QuerySerializerOptions; + /** + * A function validating request data. This is useful if you want to ensure + * the request conforms to the desired shape, so it can be safely sent to + * the server. + */ + requestValidator?: (data: unknown) => Promise; + /** + * A function transforming response data before it's returned. This is useful + * for post-processing data, e.g. converting ISO strings into Date objects. + */ + responseTransformer?: (data: unknown) => Promise; + /** + * A function validating response data. This is useful if you want to ensure + * the response conforms to the desired shape, so it can be safely passed to + * the transformers and returned to the user. + */ + responseValidator?: (data: unknown) => Promise; +} + +type IsExactlyNeverOrNeverUndefined = [T] extends [never] + ? true + : [T] extends [never | undefined] + ? [undefined] extends [T] + ? false + : true + : false; + +export type OmitNever> = { + [K in keyof T as IsExactlyNeverOrNeverUndefined extends true + ? never + : K]: T[K]; +}; diff --git a/src/api-clients/pint/core/utils.gen.ts b/src/api-clients/pint/core/utils.gen.ts new file mode 100644 index 0000000..0b5389d --- /dev/null +++ b/src/api-clients/pint/core/utils.gen.ts @@ -0,0 +1,143 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import type { BodySerializer, QuerySerializer } from './bodySerializer.gen'; +import { + type ArraySeparatorStyle, + serializeArrayParam, + serializeObjectParam, + serializePrimitiveParam, +} from './pathSerializer.gen'; + +export interface PathSerializer { + path: Record; + url: string; +} + +export const PATH_PARAM_RE = /\{[^{}]+\}/g; + +export const defaultPathSerializer = ({ path, url: _url }: PathSerializer) => { + let url = _url; + const matches = _url.match(PATH_PARAM_RE); + if (matches) { + for (const match of matches) { + let explode = false; + let name = match.substring(1, match.length - 1); + let style: ArraySeparatorStyle = 'simple'; + + if (name.endsWith('*')) { + explode = true; + name = name.substring(0, name.length - 1); + } + + if (name.startsWith('.')) { + name = name.substring(1); + style = 'label'; + } else if (name.startsWith(';')) { + name = name.substring(1); + style = 'matrix'; + } + + const value = path[name]; + + if (value === undefined || value === null) { + continue; + } + + if (Array.isArray(value)) { + url = url.replace( + match, + serializeArrayParam({ explode, name, style, value }), + ); + continue; + } + + if (typeof value === 'object') { + url = url.replace( + match, + serializeObjectParam({ + explode, + name, + style, + value: value as Record, + valueOnly: true, + }), + ); + continue; + } + + if (style === 'matrix') { + url = url.replace( + match, + `;${serializePrimitiveParam({ + name, + value: value as string, + })}`, + ); + continue; + } + + const replaceValue = encodeURIComponent( + style === 'label' ? `.${value as string}` : (value as string), + ); + url = url.replace(match, replaceValue); + } + } + return url; +}; + +export const getUrl = ({ + baseUrl, + path, + query, + querySerializer, + url: _url, +}: { + baseUrl?: string; + path?: Record; + query?: Record; + querySerializer: QuerySerializer; + url: string; +}) => { + const pathUrl = _url.startsWith('/') ? _url : `/${_url}`; + let url = (baseUrl ?? '') + pathUrl; + if (path) { + url = defaultPathSerializer({ path, url }); + } + let search = query ? querySerializer(query) : ''; + if (search.startsWith('?')) { + search = search.substring(1); + } + if (search) { + url += `?${search}`; + } + return url; +}; + +export function getValidRequestBody(options: { + body?: unknown; + bodySerializer?: BodySerializer | null; + serializedBody?: unknown; +}) { + const hasBody = options.body !== undefined; + const isSerializedBody = hasBody && options.bodySerializer; + + if (isSerializedBody) { + if ('serializedBody' in options) { + const hasSerializedBody = + options.serializedBody !== undefined && options.serializedBody !== ''; + + return hasSerializedBody ? options.serializedBody : null; + } + + // not all clients implement a serializedBody property (i.e. client-axios) + return options.body !== '' ? options.body : null; + } + + // plain/text body + if (hasBody) { + return options.body; + } + + // no body was provided + return undefined; +} diff --git a/src/api-clients/pint/index.ts b/src/api-clients/pint/index.ts new file mode 100644 index 0000000..c352c10 --- /dev/null +++ b/src/api-clients/pint/index.ts @@ -0,0 +1,4 @@ +// This file is auto-generated by @hey-api/openapi-ts + +export type * from './types.gen'; +export * from './sdk.gen'; diff --git a/src/api-clients/pint/sdk.gen.ts b/src/api-clients/pint/sdk.gen.ts new file mode 100644 index 0000000..d7e4b86 --- /dev/null +++ b/src/api-clients/pint/sdk.gen.ts @@ -0,0 +1,410 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import type { Client, Options as Options2, TDataShape } from './client'; +import { client } from './client.gen'; +import type { ConnectToExecWebSocketData, ConnectToExecWebSocketErrors, ConnectToExecWebSocketResponses, CreateDirectoryData, CreateDirectoryErrors, CreateDirectoryResponses, CreateExecData, CreateExecErrors, CreateExecResponses, CreateFileData, CreateFileErrors, CreateFileResponses, DeleteDirectoryData, DeleteDirectoryErrors, DeleteDirectoryResponses, DeleteExecData, DeleteExecErrors, DeleteExecResponses, DeleteFileData, DeleteFileErrors, DeleteFileResponses, ExecExecStdinData, ExecExecStdinErrors, ExecExecStdinResponses, ExecuteTaskActionData, ExecuteTaskActionErrors, ExecuteTaskActionResponses, GetExecData, GetExecErrors, GetExecOutputData, GetExecOutputErrors, GetExecOutputResponses, GetExecResponses, GetTaskData, GetTaskErrors, GetTaskResponses, ListDirectoryData, ListDirectoryErrors, ListDirectoryResponses, ListExecsData, ListExecsErrors, ListExecsResponses, ListPortsData, ListPortsErrors, ListPortsResponses, ListPortsSseData, ListPortsSseErrors, ListPortsSseResponses, ListSetupTasksData, ListSetupTasksErrors, ListSetupTasksResponses, ListTasksData, ListTasksErrors, ListTasksResponses, PerformFileActionData, PerformFileActionErrors, PerformFileActionResponses, ReadFileData, ReadFileErrors, ReadFileResponses, UpdateExecData, UpdateExecErrors, UpdateExecResponses } from './types.gen'; + +export type Options = Options2 & { + /** + * You can provide a client instance returned by `createClient()` instead of + * individual options. This might be also useful if you want to implement a + * custom client. + */ + client?: Client; + /** + * You can pass arbitrary values through the `meta` object. This can be + * used to access values that aren't defined as part of the SDK function. + */ + meta?: Record; +}; + +/** + * Delete a file + * Deletes a file at the specified path. + */ +export const deleteFile = (options: Options) => { + return (options.client ?? client).delete({ + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/files/{path}', + ...options + }); +}; + +/** + * Read file content + * Reads the content of a file at the specified path. + */ +export const readFile = (options: Options) => { + return (options.client ?? client).get({ + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/files/{path}', + ...options + }); +}; + +/** + * Perform file actions + * Performs actions on files (e.g., move operations). + */ +export const performFileAction = (options: Options) => { + return (options.client ?? client).patch({ + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/files/{path}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Create a file + * Creates a new file at the specified path with optional content. + */ +export const createFile = (options: Options) => { + return (options.client ?? client).post({ + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/files/{path}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Delete a directory + * Deletes a directory at the specified path. + */ +export const deleteDirectory = (options: Options) => { + return (options.client ?? client).delete({ + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/directories/{path}', + ...options + }); +}; + +/** + * List directory contents + * Lists the contents of a directory at the specified path. + */ +export const listDirectory = (options: Options) => { + return (options.client ?? client).get({ + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/directories/{path}', + ...options + }); +}; + +/** + * Create a directory + * Creates a new directory at the specified path. + */ +export const createDirectory = (options: Options) => { + return (options.client ?? client).post({ + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/directories/{path}', + ...options + }); +}; + +/** + * List all execs + * Returns a list of all active execs. + */ +export const listExecs = (options?: Options) => { + return (options?.client ?? client).get({ + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/execs', + ...options + }); +}; + +/** + * Create a new exec + * Creates a new exec with specified command and arguments. + */ +export const createExec = (options: Options) => { + return (options.client ?? client).post({ + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/execs', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Delete Exec + * Deletes a exec and execs its process. + */ +export const deleteExec = (options: Options) => { + return (options.client ?? client).delete({ + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/execs/{id}', + ...options + }); +}; + +/** + * Get exec by ID + * Retrieves a specific exec by its ID. + */ +export const getExec = (options: Options) => { + return (options.client ?? client).get({ + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/execs/{id}', + ...options + }); +}; + +/** + * Update exec + * Updates exec status (e.g., start a stopped exec). + */ +export const updateExec = (options: Options) => { + return (options.client ?? client).put({ + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/execs/{id}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Get Exec output + * Retrieves the plain text output from a exec's buffer. + */ +export const getExecOutput = (options: Options) => { + return (options.client ?? client).get({ + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/execs/{id}/io', + ...options + }); +}; + +/** + * exec exec stdin + * exec exec command (e.g., npm install). + */ +export const execExecStdin = (options: Options) => { + return (options.client ?? client).post({ + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/execs/{id}/io', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); +}; + +/** + * Connect to exec via WebSocket + * Establishes a WebSocket connection for real-time exec interaction. + * + * Authentication can be provided via: + * - Authorization header: `Authorization: Bearer ` + * - Query parameter: `?token=` + * + * Permissions: + * - Admin users: Can send input and receive output + * - Readonly users: Can only receive output + * + */ +export const connectToExecWebSocket = (options: Options) => { + return (options.client ?? client).get({ + security: [ + { + scheme: 'bearer', + type: 'http' + }, + { + in: 'query', + name: 'token', + type: 'apiKey' + } + ], + url: '/ws/v1/execs/{id}', + ...options + }); +}; + +/** + * List all tasks + * Lists all configured tasks from .codesandbox/tasks.json with their current status. + */ +export const listTasks = (options?: Options) => { + return (options?.client ?? client).get({ + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/tasks', + ...options + }); +}; + +/** + * Get task by ID + * Retrieves a specific task by its ID with current status and configuration. + */ +export const getTask = (options: Options) => { + return (options.client ?? client).get({ + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/tasks/{id}', + ...options + }); +}; + +/** + * Execute task action + * Executes an action on a specific task (start, stop, or restart). + */ +export const executeTaskAction = (options: Options) => { + return (options.client ?? client).patch({ + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/tasks/{id}/actions', + ...options + }); +}; + +/** + * List setup tasks + * Lists all setup tasks with their execution status. Setup tasks are auto-executed during server start. + */ +export const listSetupTasks = (options?: Options) => { + return (options?.client ?? client).get({ + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/setup-tasks', + ...options + }); +}; + +/** + * List open ports + * Lists all open TCP ports on the system, excluding ignored ports configured in the server. + */ +export const listPorts = (options?: Options) => { + return (options?.client ?? client).get({ + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/ports', + ...options + }); +}; + +/** + * List open ports using Server-Sent Events (SSE) + * Lists all open TCP ports on the system AND LISTEN TO THE CHANGES, excluding ignored ports configured in the server. + */ +export const listPortsSse = (options?: Options) => { + return (options?.client ?? client).sse.get({ + security: [ + { + scheme: 'bearer', + type: 'http' + } + ], + url: '/api/v1/ports/stream', + ...options + }); +}; diff --git a/src/api-clients/pint/types.gen.ts b/src/api-clients/pint/types.gen.ts new file mode 100644 index 0000000..b9bd964 --- /dev/null +++ b/src/api-clients/pint/types.gen.ts @@ -0,0 +1,1184 @@ +// This file is auto-generated by @hey-api/openapi-ts + +export type ClientOptions = { + baseUrl: 'http://localhost:57468' | 'http://localhost:{port}' | (string & {}); +}; + +export type _Error = { + code: number; + message: string; +}; + +export type FileReadResponse = { + /** + * File path + */ + path: string; + /** + * File content + */ + content: string; +}; + +export type FileCreateRequest = { + /** + * File content to create + */ + content?: string; +}; + +export type FileOperationResponse = { + /** + * Operation result message + */ + message: string; + /** + * File or directory path + */ + path: string; +}; + +export type FileActionRequest = { + /** + * Type of action to perform on the file + */ + action: 'move'; + /** + * Destination path for move operation + */ + destination: string; +}; + +export type FileActionResponse = { + /** + * Operation result message + */ + message: string; + /** + * Source path + */ + from: string; + /** + * Destination path + */ + to: string; +}; + +export type FileInfo = { + /** + * File or directory name + */ + name: string; + /** + * Full path to the file or directory + */ + path: string; + /** + * Whether this entry is a directory + */ + isDir: boolean; + /** + * File size in bytes + */ + size: number; + /** + * Last modification time + */ + modTime: string; +}; + +export type DirectoryListResponse = { + /** + * Directory path + */ + path: string; + /** + * List of files and directories + */ + files: Array; +}; + +export type ExecItem = { + /** + * Exec unique identifier + */ + id: string; + /** + * Command being executed + */ + command: string; + /** + * Command line arguments + */ + args: Array; + /** + * Exec status (e.g., running, stopped, finished) + */ + status: string; + /** + * Process ID of the exec + */ + pid: number; +}; + +export type ExecListResponse = { + /** + * List of execs + */ + execs: Array; +}; + +export type CreateExecRequest = { + /** + * Command to execute in the exec + */ + command: string; + /** + * Command line arguments + */ + args: Array; + /** + * Whether to automatically start the exec (defaults to true) + */ + autorun?: boolean; + /** + * Whether to start interactive shell session or not (defaults to false) + */ + interactive?: boolean; +}; + +export type UpdateExecRequest = { + /** + * Status to set for the exec (currently only 'running' is supported) + */ + status: 'running'; +}; + +export type ExecDeleteResponse = { + /** + * Deletion confirmation message + */ + message: string; +}; + +export type ExecStdout = { + /** + * Type of the exec output + */ + type: 'stdout' | 'stderr'; + /** + * Data associated with the exec output + */ + output: string; + /** + * Sequence number of the output message + */ + sequence: number; + /** + * Timestamp of when the output was generated + */ + timestamp?: string; +}; + +export type ExecStdin = { + /** + * Type of the exec input + */ + type: 'stdin' | 'resize'; + /** + * Data associated with the exec input + */ + input: string; +}; + +export type TaskStatus = 'RUNNING' | 'FINISHED' | 'ERROR' | 'KILLED' | 'RESTARTING' | 'IDLE'; + +/** + * TaskBase + * Base schema for a task item, containing common fields shared across different task types. + */ +export type TaskBase = { + status: TaskStatus; + /** + * Exec identifier associated with the task + */ + execId: string; + /** + * Task start time in RFC3339 format + */ + startTime: string; + /** + * Task end time in RFC3339 format + */ + endTime: string; +}; + +export type TaskRestart = { + files: Array; + clone: boolean; + resume: boolean; + branch: boolean; +}; + +export type TaskPreview = { + port: number; +}; + +export type TaskConfig = { + name: string; + command: string; + runAtStart: boolean; + restartOn: TaskRestart; + preview: TaskPreview; +}; + +export type TaskItem = TaskBase & { + /** + * Task identifier + */ + id: string; + config: TaskConfig; +}; + +export type TaskListResponse = { + tasks: Array; +}; + +export type GetTaskResponse = { + task: TaskItem; +}; + +/** + * Type of action to execute on a task + */ +export type TaskActionType = 'start' | 'stop' | 'restart'; + +/** + * TaskActionResponse + * Schema for task action responses, containing details about the task and the action performed. + */ +export type TaskActionResponse = TaskBase & { + /** + * Task identifier + */ + id: string; + /** + * Task name + */ + name: string; + /** + * Task command + */ + command: string; + /** + * Action result message + */ + message: string; +}; + +export type SetupTaskItem = TaskBase & { + /** + * Setup task name + */ + name: string; + /** + * Setup task command + */ + command: string; +}; + +export type SetupTaskListResponse = { + setupTasks: Array; +}; + +export type PortInfo = { + /** + * Port number + */ + port: number; + /** + * IP address the port is bound to + */ + address: string; +}; + +export type PortsListResponse = { + /** + * List of open ports + */ + ports: Array; +}; + +export type Task = TaskItem; + +export type DeleteFileData = { + body?: never; + path: { + /** + * File path + */ + path: string; + }; + query?: never; + url: '/api/v1/files/{path}'; +}; + +export type DeleteFileErrors = { + /** + * Bad Request - Path is required or invalid path + */ + 400: _Error; + /** + * Unauthorized + */ + 401: _Error; + /** + * File not found + */ + 404: _Error; + /** + * Internal Server Error - Failed to delete file + */ + 500: _Error; + /** + * Unexpected Error + */ + default: _Error; +}; + +export type DeleteFileError = DeleteFileErrors[keyof DeleteFileErrors]; + +export type DeleteFileResponses = { + /** + * File deleted successfully + */ + 200: FileOperationResponse; +}; + +export type DeleteFileResponse = DeleteFileResponses[keyof DeleteFileResponses]; + +export type ReadFileData = { + body?: never; + path: { + /** + * File path + */ + path: string; + }; + query?: never; + url: '/api/v1/files/{path}'; +}; + +export type ReadFileErrors = { + /** + * Bad Request - Path is required or invalid path + */ + 400: _Error; + /** + * Unauthorized + */ + 401: _Error; + /** + * File not found + */ + 404: _Error; + /** + * Unexpected Error + */ + default: _Error; +}; + +export type ReadFileError = ReadFileErrors[keyof ReadFileErrors]; + +export type ReadFileResponses = { + /** + * File content retrieved successfully + */ + 200: FileReadResponse; +}; + +export type ReadFileResponse = ReadFileResponses[keyof ReadFileResponses]; + +export type PerformFileActionData = { + /** + * File action request + */ + body: FileActionRequest; + path: { + /** + * Source file path (will be URL decoded) + */ + path: string; + }; + query?: never; + url: '/api/v1/files/{path}'; +}; + +export type PerformFileActionErrors = { + /** + * Bad Request - Path is required, invalid action, or missing destination + */ + 400: _Error; + /** + * Unauthorized + */ + 401: _Error; + /** + * Internal Server Error - Failed to perform action + */ + 500: _Error; + /** + * Unexpected Error + */ + default: _Error; +}; + +export type PerformFileActionError = PerformFileActionErrors[keyof PerformFileActionErrors]; + +export type PerformFileActionResponses = { + /** + * Action performed successfully + */ + 200: FileActionResponse; +}; + +export type PerformFileActionResponse = PerformFileActionResponses[keyof PerformFileActionResponses]; + +export type CreateFileData = { + /** + * File creation request + */ + body?: FileCreateRequest; + path: { + /** + * File path + */ + path: string; + }; + query?: never; + url: '/api/v1/files/{path}'; +}; + +export type CreateFileErrors = { + /** + * Bad Request - Path is required or invalid path + */ + 400: _Error; + /** + * Unauthorized + */ + 401: _Error; + /** + * Internal Server Error - Failed to create file + */ + 500: _Error; + /** + * Unexpected Error + */ + default: _Error; +}; + +export type CreateFileError = CreateFileErrors[keyof CreateFileErrors]; + +export type CreateFileResponses = { + /** + * File created successfully + */ + 201: FileReadResponse; +}; + +export type CreateFileResponse = CreateFileResponses[keyof CreateFileResponses]; + +export type DeleteDirectoryData = { + body?: never; + path: { + /** + * Directory path + */ + path: string; + }; + query?: never; + url: '/api/v1/directories/{path}'; +}; + +export type DeleteDirectoryErrors = { + /** + * Bad Request - Path is required or invalid path + */ + 400: _Error; + /** + * Unauthorized + */ + 401: _Error; + /** + * Directory not found + */ + 404: _Error; + /** + * Internal Server Error - Failed to delete directory + */ + 500: _Error; + /** + * Unexpected Error + */ + default: _Error; +}; + +export type DeleteDirectoryError = DeleteDirectoryErrors[keyof DeleteDirectoryErrors]; + +export type DeleteDirectoryResponses = { + /** + * Directory deleted successfully + */ + 200: FileOperationResponse; +}; + +export type DeleteDirectoryResponse = DeleteDirectoryResponses[keyof DeleteDirectoryResponses]; + +export type ListDirectoryData = { + body?: never; + path: { + /** + * Directory path (will be URL decoded). Use "/" for root directory. + */ + path: string; + }; + query?: never; + url: '/api/v1/directories/{path}'; +}; + +export type ListDirectoryErrors = { + /** + * Bad Request - Invalid path + */ + 400: _Error; + /** + * Unauthorized + */ + 401: _Error; + /** + * Directory not found + */ + 404: _Error; + /** + * Unexpected Error + */ + default: _Error; +}; + +export type ListDirectoryError = ListDirectoryErrors[keyof ListDirectoryErrors]; + +export type ListDirectoryResponses = { + /** + * Directory contents retrieved successfully + */ + 200: DirectoryListResponse; +}; + +export type ListDirectoryResponse = ListDirectoryResponses[keyof ListDirectoryResponses]; + +export type CreateDirectoryData = { + body?: never; + path: { + /** + * Directory path + */ + path: string; + }; + query?: never; + url: '/api/v1/directories/{path}'; +}; + +export type CreateDirectoryErrors = { + /** + * Bad Request - Path is required or invalid path + */ + 400: _Error; + /** + * Unauthorized + */ + 401: _Error; + /** + * Internal Server Error - Failed to create directory + */ + 500: _Error; + /** + * Unexpected Error + */ + default: _Error; +}; + +export type CreateDirectoryError = CreateDirectoryErrors[keyof CreateDirectoryErrors]; + +export type CreateDirectoryResponses = { + /** + * Directory created successfully + */ + 201: FileOperationResponse; +}; + +export type CreateDirectoryResponse = CreateDirectoryResponses[keyof CreateDirectoryResponses]; + +export type ListExecsData = { + body?: never; + path?: never; + query?: never; + url: '/api/v1/execs'; +}; + +export type ListExecsErrors = { + /** + * Unauthorized + */ + 401: _Error; + /** + * Unexpected Error + */ + default: _Error; +}; + +export type ListExecsError = ListExecsErrors[keyof ListExecsErrors]; + +export type ListExecsResponses = { + /** + * Execs retrieved successfully + */ + 200: ExecListResponse; +}; + +export type ListExecsResponse = ListExecsResponses[keyof ListExecsResponses]; + +export type CreateExecData = { + /** + * Exec creation request + */ + body: CreateExecRequest; + path?: never; + query?: never; + url: '/api/v1/execs'; +}; + +export type CreateExecErrors = { + /** + * Bad Request - Invalid request body + */ + 400: _Error; + /** + * Unauthorized + */ + 401: _Error; + /** + * Internal Server Error - Failed to create exec + */ + 500: _Error; + /** + * Unexpected Error + */ + default: _Error; +}; + +export type CreateExecError = CreateExecErrors[keyof CreateExecErrors]; + +export type CreateExecResponses = { + /** + * Exec created successfully + */ + 201: ExecItem; +}; + +export type CreateExecResponse = CreateExecResponses[keyof CreateExecResponses]; + +export type DeleteExecData = { + body?: never; + path: { + /** + * Exec identifier + */ + id: string; + }; + query?: never; + url: '/api/v1/execs/{id}'; +}; + +export type DeleteExecErrors = { + /** + * Bad Request - Exec ID is required + */ + 400: _Error; + /** + * Unauthorized + */ + 401: _Error; + /** + * Exec not found + */ + 404: _Error; + /** + * Unexpected Error + */ + default: _Error; +}; + +export type DeleteExecError = DeleteExecErrors[keyof DeleteExecErrors]; + +export type DeleteExecResponses = { + /** + * Exec deleted successfully + */ + 200: ExecDeleteResponse; +}; + +export type DeleteExecResponse = DeleteExecResponses[keyof DeleteExecResponses]; + +export type GetExecData = { + body?: never; + path: { + /** + * Exec identifier + */ + id: string; + }; + query?: never; + url: '/api/v1/execs/{id}'; +}; + +export type GetExecErrors = { + /** + * Bad Request - Exec ID is required + */ + 400: _Error; + /** + * Unauthorized + */ + 401: _Error; + /** + * Exec not found + */ + 404: _Error; + /** + * Unexpected Error + */ + default: _Error; +}; + +export type GetExecError = GetExecErrors[keyof GetExecErrors]; + +export type GetExecResponses = { + /** + * Exec retrieved successfully + */ + 200: ExecItem; +}; + +export type GetExecResponse = GetExecResponses[keyof GetExecResponses]; + +export type UpdateExecData = { + /** + * Exec update request + */ + body: UpdateExecRequest; + path: { + /** + * Exec identifier + */ + id: string; + }; + query?: never; + url: '/api/v1/execs/{id}'; +}; + +export type UpdateExecErrors = { + /** + * Bad Request - Exec ID is required or invalid status + */ + 400: _Error; + /** + * Unauthorized + */ + 401: _Error; + /** + * Exec not found + */ + 404: _Error; + /** + * Conflict - Exec is already in the requested state + */ + 409: _Error; + /** + * Internal Server Error - Failed to update exec + */ + 500: _Error; + /** + * Unexpected Error + */ + default: _Error; +}; + +export type UpdateExecError = UpdateExecErrors[keyof UpdateExecErrors]; + +export type UpdateExecResponses = { + /** + * Exec updated successfully + */ + 200: ExecItem; +}; + +export type UpdateExecResponse = UpdateExecResponses[keyof UpdateExecResponses]; + +export type GetExecOutputData = { + body?: never; + path: { + /** + * Exec identifier + */ + id: string; + }; + query?: never; + url: '/api/v1/execs/{id}/io'; +}; + +export type GetExecOutputErrors = { + /** + * Bad Request - Exec ID is required + */ + 400: _Error; + /** + * Unauthorized + */ + 401: _Error; + /** + * Exec not found + */ + 404: _Error; + /** + * Unexpected Error + */ + default: _Error; +}; + +export type GetExecOutputError = GetExecOutputErrors[keyof GetExecOutputErrors]; + +export type GetExecOutputResponses = { + /** + * Exec output retrieved successfully + */ + 200: ExecStdout; +}; + +export type GetExecOutputResponse = GetExecOutputResponses[keyof GetExecOutputResponses]; + +export type ExecExecStdinData = { + /** + * Exec update request + */ + body: ExecStdin; + path: { + /** + * Exec identifier + */ + id: string; + }; + query?: never; + url: '/api/v1/execs/{id}/io'; +}; + +export type ExecExecStdinErrors = { + /** + * Bad Request - Exec ID is required or invalid status + */ + 400: _Error; + /** + * Unauthorized + */ + 401: _Error; + /** + * Exec not found + */ + 404: _Error; + /** + * Internal Server Error - Failed to update exec + */ + 500: _Error; + /** + * Unexpected Error + */ + default: _Error; +}; + +export type ExecExecStdinError = ExecExecStdinErrors[keyof ExecExecStdinErrors]; + +export type ExecExecStdinResponses = { + /** + * Exec stdin executed successfully + */ + 200: ExecItem; +}; + +export type ExecExecStdinResponse = ExecExecStdinResponses[keyof ExecExecStdinResponses]; + +export type ConnectToExecWebSocketData = { + body?: never; + path: { + /** + * Exec identifier + */ + id: string; + }; + query?: never; + url: '/ws/v1/execs/{id}'; +}; + +export type ConnectToExecWebSocketErrors = { + /** + * Bad Request - Exec ID is required + */ + 400: _Error; + /** + * Unauthorized - Authentication token required or invalid + */ + 401: _Error; + /** + * Exec not found + */ + 404: _Error; + /** + * Internal Server Error - Failed to establish WebSocket connection + */ + 500: _Error; + /** + * Unexpected Error + */ + default: _Error; +}; + +export type ConnectToExecWebSocketError = ConnectToExecWebSocketErrors[keyof ConnectToExecWebSocketErrors]; + +export type ConnectToExecWebSocketResponses = { + /** + * Unexpected Error + */ + default: _Error; +}; + +export type ConnectToExecWebSocketResponse = ConnectToExecWebSocketResponses[keyof ConnectToExecWebSocketResponses]; + +export type ListTasksData = { + body?: never; + path?: never; + query?: never; + url: '/api/v1/tasks'; +}; + +export type ListTasksErrors = { + /** + * Unauthorized + */ + 401: _Error; + /** + * Unexpected Error + */ + default: _Error; +}; + +export type ListTasksError = ListTasksErrors[keyof ListTasksErrors]; + +export type ListTasksResponses = { + /** + * List of tasks retrieved successfully + */ + 200: TaskListResponse; +}; + +export type ListTasksResponse = ListTasksResponses[keyof ListTasksResponses]; + +export type GetTaskData = { + body?: never; + path: { + /** + * Task identifier + */ + id: string; + }; + query?: never; + url: '/api/v1/tasks/{id}'; +}; + +export type GetTaskErrors = { + /** + * Bad Request - Task ID is required + */ + 400: _Error; + /** + * Unauthorized + */ + 401: _Error; + /** + * Task not found + */ + 404: _Error; + /** + * Unexpected Error + */ + default: _Error; +}; + +export type GetTaskError = GetTaskErrors[keyof GetTaskErrors]; + +export type GetTaskResponses = { + /** + * Task retrieved successfully + */ + 200: GetTaskResponse; +}; + +export type GetTaskResponse2 = GetTaskResponses[keyof GetTaskResponses]; + +export type ExecuteTaskActionData = { + body?: never; + path: { + /** + * Task identifier + */ + id: string; + }; + query: { + /** + * Type of action to execute + */ + actionType: TaskActionType; + }; + url: '/api/v1/tasks/{id}/actions'; +}; + +export type ExecuteTaskActionErrors = { + /** + * Bad Request - Task ID is required, invalid action type, or invalid command + */ + 400: _Error; + /** + * Unauthorized + */ + 401: _Error; + /** + * Task not found + */ + 404: _Error; + /** + * Conflict - Invalid state transition (e.g., task already running for start, task not running for stop) + */ + 409: _Error; + /** + * Internal Server Error - Failed to execute action + */ + 500: _Error; + /** + * Unexpected Error + */ + default: _Error; +}; + +export type ExecuteTaskActionError = ExecuteTaskActionErrors[keyof ExecuteTaskActionErrors]; + +export type ExecuteTaskActionResponses = { + /** + * Action executed successfully + */ + 200: TaskActionResponse; +}; + +export type ExecuteTaskActionResponse = ExecuteTaskActionResponses[keyof ExecuteTaskActionResponses]; + +export type ListSetupTasksData = { + body?: never; + path?: never; + query?: never; + url: '/api/v1/setup-tasks'; +}; + +export type ListSetupTasksErrors = { + /** + * Unauthorized + */ + 401: _Error; + /** + * Unexpected Error + */ + default: _Error; +}; + +export type ListSetupTasksError = ListSetupTasksErrors[keyof ListSetupTasksErrors]; + +export type ListSetupTasksResponses = { + /** + * Setup tasks retrieved successfully + */ + 200: SetupTaskListResponse; +}; + +export type ListSetupTasksResponse = ListSetupTasksResponses[keyof ListSetupTasksResponses]; + +export type ListPortsData = { + body?: never; + path?: never; + query?: never; + url: '/api/v1/ports'; +}; + +export type ListPortsErrors = { + /** + * Unauthorized + */ + 401: _Error; + /** + * Internal Server Error - Failed to read port information + */ + 500: _Error; + /** + * Unexpected Error + */ + default: _Error; +}; + +export type ListPortsError = ListPortsErrors[keyof ListPortsErrors]; + +export type ListPortsResponses = { + /** + * Open ports retrieved successfully + */ + 200: PortsListResponse; +}; + +export type ListPortsResponse = ListPortsResponses[keyof ListPortsResponses]; + +export type ListPortsSseData = { + body?: never; + path?: never; + query?: never; + url: '/api/v1/ports/stream'; +}; + +export type ListPortsSseErrors = { + /** + * Unauthorized + */ + 401: _Error; + /** + * Internal Server Error - Failed to read port information + */ + 500: _Error; + /** + * Unexpected Error + */ + default: _Error; +}; + +export type ListPortsSseError = ListPortsSseErrors[keyof ListPortsSseErrors]; + +export type ListPortsSseResponses = { + /** + * Server-Sent Events stream of ports list updates + */ + 200: string; +}; + +export type ListPortsSseResponse = ListPortsSseResponses[keyof ListPortsSseResponses]; diff --git a/src/utils/api.ts b/src/utils/api.ts index 72dd452..671b8c5 100644 --- a/src/utils/api.ts +++ b/src/utils/api.ts @@ -1,19 +1,21 @@ import { StartSandboxOpts } from "../types"; import { RateLimitError } from "./rate-limit"; +import { getInferredBaseUrl } from "./constants"; import { Client, Config, createClient, createConfig, -} from "@hey-api/client-fetch"; -import { getInferredBaseUrl } from "./constants"; +} from "../api-clients/client/client"; async function enhanceFetch( - request: Request, + request: RequestInfo | URL, instrumentation?: (request: Request) => Promise ) { // Clone the request to modify headers - const headers = new Headers(request.headers); + const headers = new Headers( + request instanceof Request ? request.headers : undefined + ); const existingUserAgent = headers.get("User-Agent") || ""; // Extend User-Agent with SDK version diff --git a/src/utils/event.ts b/src/utils/event.ts index c7a32f0..270219a 100644 --- a/src/utils/event.ts +++ b/src/utils/event.ts @@ -130,3 +130,53 @@ export class AsyncEmitter implements IDisposable { this.registeredListeners = new Set(); } } + +/** + * EmitterSubscription provides an abstraction that manages a subscription lifecycle + * tied to the number of listeners on an emitter. The subscription is created when + * the first listener is added and disposed when the last listener is removed. + */ +export class EmitterSubscription implements IDisposable { + private emitter = new Emitter(); + private subscription: IDisposable | undefined; + private listenerCount = 0; + + constructor( + private createSubscription: (fire: (value: T) => void) => IDisposable + ) {} + + get event(): Event { + return (listener: (e: T) => void) => { + // Add listener to emitter + const listenerDisposable = this.emitter.event(listener); + + // Create subscription if this is the first listener + if (this.listenerCount === 0) { + this.subscription = this.createSubscription((value) => + this.emitter.fire(value) + ); + } + + this.listenerCount++; + + // Return disposable that removes listener and cleans up subscription if needed + return Disposable.create(() => { + listenerDisposable.dispose(); + this.listenerCount--; + + // Dispose subscription when last listener is removed + if (this.listenerCount === 0 && this.subscription) { + this.subscription.dispose(); + this.subscription = undefined; + } + }); + }; + } + + dispose(): void { + this.subscription?.dispose(); + this.subscription = undefined; + this.emitter.dispose(); + this.listenerCount = 0; + } +} diff --git a/tests/emitter-subscription.test.ts b/tests/emitter-subscription.test.ts new file mode 100644 index 0000000..4aaf590 --- /dev/null +++ b/tests/emitter-subscription.test.ts @@ -0,0 +1,212 @@ +import { describe, it, expect, vi } from 'vitest' +import { EmitterSubscription } from '../src/utils/event' +import { Disposable } from '../src/utils/disposable' + +describe('EmitterSubscription', () => { + it('should create subscription when first listener is added', () => { + const createSubscription = vi.fn(() => Disposable.create(() => {})) + const subscription = new EmitterSubscription(createSubscription) + + expect(createSubscription).not.toHaveBeenCalled() + + const disposable = subscription.event(() => {}) + + expect(createSubscription).toHaveBeenCalledTimes(1) + + disposable.dispose() + }) + + it('should not create multiple subscriptions for multiple listeners', () => { + const createSubscription = vi.fn(() => Disposable.create(() => {})) + const subscription = new EmitterSubscription(createSubscription) + + const disposable1 = subscription.event(() => {}) + const disposable2 = subscription.event(() => {}) + const disposable3 = subscription.event(() => {}) + + expect(createSubscription).toHaveBeenCalledTimes(1) + + disposable1.dispose() + disposable2.dispose() + disposable3.dispose() + }) + + it('should fire events to all listeners', () => { + const subscription = new EmitterSubscription((fire) => { + fire(42) + return Disposable.create(() => {}) + }) + + const listener1 = vi.fn() + const listener2 = vi.fn() + const listener3 = vi.fn() + + subscription.event(listener1) + subscription.event(listener2) + subscription.event(listener3) + + expect(listener1).toHaveBeenCalledWith(42) + expect(listener2).toHaveBeenCalledWith(42) + expect(listener3).toHaveBeenCalledWith(42) + }) + + it('should allow firing events from subscription callback', () => { + let fireFn: ((value: number) => void) | undefined + + const subscription = new EmitterSubscription((fire) => { + fireFn = fire + return Disposable.create(() => {}) + }) + + const listener = vi.fn() + subscription.event(listener) + + expect(fireFn).toBeDefined() + + fireFn!(100) + fireFn!(200) + fireFn!(300) + + expect(listener).toHaveBeenCalledTimes(3) + expect(listener).toHaveBeenNthCalledWith(1, 100) + expect(listener).toHaveBeenNthCalledWith(2, 200) + expect(listener).toHaveBeenNthCalledWith(3, 300) + }) + + it('should dispose subscription when last listener is removed', () => { + const dispose = vi.fn() + const createSubscription = vi.fn(() => Disposable.create(dispose)) + const subscription = new EmitterSubscription(createSubscription) + + const disposable1 = subscription.event(() => {}) + const disposable2 = subscription.event(() => {}) + + expect(dispose).not.toHaveBeenCalled() + + disposable1.dispose() + expect(dispose).not.toHaveBeenCalled() + + disposable2.dispose() + expect(dispose).toHaveBeenCalledTimes(1) + }) + + it('should recreate subscription if listener is added again after all removed', () => { + const dispose = vi.fn() + const createSubscription = vi.fn(() => Disposable.create(dispose)) + const subscription = new EmitterSubscription(createSubscription) + + const disposable1 = subscription.event(() => {}) + disposable1.dispose() + + expect(createSubscription).toHaveBeenCalledTimes(1) + expect(dispose).toHaveBeenCalledTimes(1) + + const disposable2 = subscription.event(() => {}) + + expect(createSubscription).toHaveBeenCalledTimes(2) + expect(dispose).toHaveBeenCalledTimes(1) + + disposable2.dispose() + expect(dispose).toHaveBeenCalledTimes(2) + }) + + it('should stop firing to disposed listeners', () => { + let fireFn: ((value: number) => void) | undefined + + const subscription = new EmitterSubscription((fire) => { + fireFn = fire + return Disposable.create(() => {}) + }) + + const listener1 = vi.fn() + const listener2 = vi.fn() + const listener3 = vi.fn() + + const disposable1 = subscription.event(listener1) + subscription.event(listener2) + subscription.event(listener3) + + fireFn!(1) + expect(listener1).toHaveBeenCalledTimes(1) + expect(listener2).toHaveBeenCalledTimes(1) + expect(listener3).toHaveBeenCalledTimes(1) + + disposable1.dispose() + + fireFn!(2) + expect(listener1).toHaveBeenCalledTimes(1) // Not called again + expect(listener2).toHaveBeenCalledTimes(2) + expect(listener3).toHaveBeenCalledTimes(2) + }) + + it('should cleanup everything on dispose', () => { + const subscriptionDispose = vi.fn() + const createSubscription = vi.fn(() => Disposable.create(subscriptionDispose)) + const subscription = new EmitterSubscription(createSubscription) + + let fireFn: ((value: number) => void) | undefined + subscription = new EmitterSubscription((fire) => { + fireFn = fire + return Disposable.create(subscriptionDispose) + }) + + const listener = vi.fn() + subscription.event(listener) + + subscription.dispose() + + expect(subscriptionDispose).toHaveBeenCalledTimes(1) + + // Should not fire to listeners after dispose + fireFn!(42) + expect(listener).not.toHaveBeenCalled() + }) + + it('should work with interval example', () => { + vi.useFakeTimers() + + let intervalId: NodeJS.Timeout + const subscription = new EmitterSubscription((fire) => { + intervalId = setInterval(() => fire(Date.now()), 1000) + return Disposable.create(() => clearInterval(intervalId)) + }) + + const listener = vi.fn() + const disposable = subscription.event(listener) + + vi.advanceTimersByTime(3500) + expect(listener).toHaveBeenCalledTimes(3) + + disposable.dispose() + + // Should not receive more events after dispose + vi.advanceTimersByTime(5000) + expect(listener).toHaveBeenCalledTimes(3) + + vi.useRealTimers() + }) + + it('should handle multiple add/remove cycles correctly', () => { + const dispose = vi.fn() + const createSubscription = vi.fn(() => Disposable.create(dispose)) + const subscription = new EmitterSubscription(createSubscription) + + // Cycle 1 + const d1 = subscription.event(() => {}) + d1.dispose() + expect(createSubscription).toHaveBeenCalledTimes(1) + expect(dispose).toHaveBeenCalledTimes(1) + + // Cycle 2 + const d2 = subscription.event(() => {}) + d2.dispose() + expect(createSubscription).toHaveBeenCalledTimes(2) + expect(dispose).toHaveBeenCalledTimes(2) + + // Cycle 3 + const d3 = subscription.event(() => {}) + d3.dispose() + expect(createSubscription).toHaveBeenCalledTimes(3) + expect(dispose).toHaveBeenCalledTimes(3) + }) +}) From 199ec3f8d2cdb4663ea3a29d66e58f6c657a5c35 Mon Sep 17 00:00:00 2001 From: Christian Alfoni Date: Wed, 8 Oct 2025 14:03:23 +0200 Subject: [PATCH 2/3] basic exec run and list --- src/PintClient/index.ts | 112 ++++++++++++++++++++++++++++++++++++- src/Sandbox.ts | 7 +++ src/SandboxClient/index.ts | 15 +++++ src/types.ts | 3 +- 4 files changed, 134 insertions(+), 3 deletions(-) diff --git a/src/PintClient/index.ts b/src/PintClient/index.ts index cae0548..eda4247 100644 --- a/src/PintClient/index.ts +++ b/src/PintClient/index.ts @@ -1,14 +1,31 @@ import { IAgentClient, IAgentClientPorts, + IAgentClientShells, IAgentClientState, } from "../agent-client-interface"; import { Port } from "../pitcher-protocol/messages/port"; -import { listPorts, listPortsSse } from "../api-clients/pint"; +import { + createExec, + ExecItem, + listExecs, + listPorts, + listPortsSse, +} from "../api-clients/pint"; import { SandboxSession } from "../types"; import { Emitter, EmitterSubscription, Event } from "../utils/event"; import { Disposable } from "../utils/disposable"; import { Client, createClient, createConfig } from "../api-clients/pint/client"; +import { + ShellSize, + ShellProcessType, + OpenShellDTO, + CommandShellDTO, + ShellId, + TerminalShellDTO, + ShellDTO, + ShellProcessStatus, +} from "../pitcher-protocol/messages/shell"; class PintPortsClient implements IAgentClientPorts { private onPortsUpdatedEmitter = new EmitterSubscription((fire) => { @@ -49,6 +66,96 @@ class PintPortsClient implements IAgentClientPorts { } } +export class PintShellsClient implements IAgentClientShells { + private onShellExitedEmitter = new EmitterSubscription<{ + shellId: string; + exitCode: number; + }>(() => { + return Disposable.create(() => {}); + }); + onShellExited = this.onShellExitedEmitter.event; + private onShellOutEmitter = new EmitterSubscription<{ + shellId: ShellId; + out: string; + }>(() => { + return Disposable.create(() => {}); + }); + onShellOut = this.onShellOutEmitter.event; + private onShellTerminatedEmitter = new EmitterSubscription<{ + shellId: ShellId; + author: string; + }>(() => { + return Disposable.create(() => {}); + }); + onShellTerminated = this.onShellTerminatedEmitter.event; + constructor(private apiClient: Client, private sandboxId: string) {} + private convertExecToShellDTO(exec: ExecItem) { + return { + isSystemShell: true, + name: JSON.stringify({ + type: "command", + command: exec.command, + name: "", + }), + ownerUsername: "root", + shellId: exec.id, + shellType: "TERMINAL" as const, + startCommand: exec.command, + status: exec.status as ShellProcessStatus, + }; + } + async create( + projectPath: string, + size: ShellSize, + command?: string, + type?: ShellProcessType, + isSystemShell?: boolean + ): Promise { + const exec = await createExec({ + client: this.apiClient, + body: { + args: [], + command: command || "bash", + interactive: true, + }, + }); + + if (!exec.data) { + console.log(exec); + throw new Error("Nooooooooo"); + } + + return { + ...this.convertExecToShellDTO(exec.data), + buffer: [], + }; + } + delete(shellId: ShellId): Promise { + throw new Error("Not implemented"); + } + async getShells(): Promise { + const execs = await listExecs({ + client: this.apiClient, + }); + + return ( + execs.data?.execs.map((exec) => this.convertExecToShellDTO(exec)) ?? [] + ); + } + open(shellId: ShellId, size: ShellSize): Promise { + throw new Error("Not implemented"); + } + rename(shellId: ShellId, name: string): Promise { + throw new Error("Not implemented"); + } + restart(shellId: ShellId): Promise { + throw new Error("Not implemented"); + } + send(shellId: ShellId, input: string, size: ShellSize): Promise { + throw new Error("Not implemented"); + } +} + export class PintClient implements IAgentClient { static async create(session: SandboxSession) { return new PintClient(session); @@ -65,7 +172,7 @@ export class PintClient implements IAgentClient { isUpToDate: boolean; ports: IAgentClientPorts; - shells: any = null; // TODO: Implement + shells: IAgentClientShells; fs: any = null; // TODO: Implement setup: any = null; // TODO: Implement tasks: any = null; // TODO: Implement @@ -86,6 +193,7 @@ export class PintClient implements IAgentClient { ); this.ports = new PintPortsClient(apiClient, this.sandboxId); + this.shells = new PintShellsClient(apiClient, this.sandboxId); } ping(): void {} diff --git a/src/Sandbox.ts b/src/Sandbox.ts index 76e9287..ad87957 100644 --- a/src/Sandbox.ts +++ b/src/Sandbox.ts @@ -178,8 +178,14 @@ export class Sandbox { pitcherManagerResponse: PitcherManagerResponse, customSession?: SessionCreateOptions ): Promise { + // HACK: we currently do not get a flag for pint, but this is a check we can use for now + const isPint = + pitcherManagerResponse.userWorkspacePath === + pitcherManagerResponse.workspacePath; + if (!customSession || !customSession.id) { return { + isPint, sandboxId: this.id, bootupType: this.bootupType, hostToken: customSession?.hostToken, @@ -204,6 +210,7 @@ export class Sandbox { }); return { + isPint, sandboxId: this.id, sessionId: customSession?.id, hostToken: customSession?.hostToken, diff --git a/src/SandboxClient/index.ts b/src/SandboxClient/index.ts index ca83b2f..105d054 100644 --- a/src/SandboxClient/index.ts +++ b/src/SandboxClient/index.ts @@ -16,6 +16,7 @@ import { Barrier } from "../utils/barrier"; import { AgentClient } from "../AgentClient"; import { SandboxSession } from "../types"; import { Tracer, SpanStatusCode } from "@opentelemetry/api"; +import { PintClient } from "../PintClient"; export * from "./filesystem"; export * from "./ports"; @@ -41,6 +42,20 @@ export class SandboxClient { initStatusCb?: (event: system.InitStatus) => void, tracer?: Tracer ) { + if (session.isPint) { + const agentClient = await PintClient.create(session); + + return new SandboxClient( + agentClient, + { hostToken: session.hostToken, tracer }, + { + currentStepIndex: 0, + state: "FINISHED", + steps: [], + } + ); + } + const { client: agentClient, joinResult } = await AgentClient.create({ session, getSession, diff --git a/src/types.ts b/src/types.ts index 4c20bce..75990d9 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,7 +1,7 @@ import { VMTier } from "./VMTier"; import { HostToken } from "./HostTokens"; -import { Config } from "@hey-api/client-fetch"; import { Tracer } from "@opentelemetry/api"; +import { Config } from "./api-clients/client/client"; export interface PitcherManagerResponse { bootupType: "RUNNING" | "CLEAN" | "RESUME" | "FORK"; @@ -235,6 +235,7 @@ export type SandboxOpts = { }; export type SandboxSession = PitcherManagerResponse & { + isPint: boolean; sandboxId: string; sessionId?: string; hostToken?: HostToken; From 6ec99e48d4996878896eeb4002714c80b861f6fd Mon Sep 17 00:00:00 2001 From: Christian Alfoni Date: Mon, 13 Oct 2025 09:45:01 +0200 Subject: [PATCH 3/3] WIP: running execs --- .gitignore | 1 - src/PintClient/index.ts | 6 ++++-- src/Sandbox.ts | 1 + src/SandboxClient/commands.ts | 6 +++--- test.ts | 22 ++++++++++++++++++++++ 5 files changed, 30 insertions(+), 6 deletions(-) create mode 100644 test.ts diff --git a/.gitignore b/.gitignore index 68e2089..e344c2b 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,6 @@ # Generated stuff dist -test.ts test-template ### macOS ### diff --git a/src/PintClient/index.ts b/src/PintClient/index.ts index eda4247..27330a5 100644 --- a/src/PintClient/index.ts +++ b/src/PintClient/index.ts @@ -111,11 +111,13 @@ export class PintShellsClient implements IAgentClientShells { type?: ShellProcessType, isSystemShell?: boolean ): Promise { + const [cmd, ...rgs] = command!.split(" "); + const exec = await createExec({ client: this.apiClient, body: { - args: [], - command: command || "bash", + args: rgs, + command: cmd, interactive: true, }, }); diff --git a/src/Sandbox.ts b/src/Sandbox.ts index ad87957..bf9e8fb 100644 --- a/src/Sandbox.ts +++ b/src/Sandbox.ts @@ -145,6 +145,7 @@ export class Sandbox { }) .join("\n"); const cmd = [ + `mkdir -p "$HOME/.private"`, `cat << 'EOF' > "$HOME/.private/.env"`, envStrings, `EOF`, diff --git a/src/SandboxClient/commands.ts b/src/SandboxClient/commands.ts index 2b52abb..24cbc5f 100644 --- a/src/SandboxClient/commands.ts +++ b/src/SandboxClient/commands.ts @@ -103,12 +103,12 @@ export class SandboxCommands { // TODO: use a new shell API that natively supports cwd & env let commandWithEnv = Object.keys(passedEnv).length - ? `source $HOME/.private/.env 2>/dev/null || true && env ${Object.entries( + ? `bash -c 'source $HOME/.private/.env 2>/dev/null || true && env ${Object.entries( passedEnv ) .map(([key, value]) => `${key}=${value}`) - .join(" ")} bash -c '${escapedCommand}'` - : `source $HOME/.private/.env 2>/dev/null || true && bash -c '${escapedCommand}'`; + .join(" ")} ${escapedCommand}'` + : `bash -c 'source $HOME/.private/.env 2>/dev/null || true && ${escapedCommand}'`; if (opts?.cwd) { commandWithEnv = `cd ${opts.cwd} && ${commandWithEnv}`; diff --git a/test.ts b/test.ts new file mode 100644 index 0000000..e8f449d --- /dev/null +++ b/test.ts @@ -0,0 +1,22 @@ +import { CodeSandbox } from "@codesandbox/sdk"; + +const sdk = new CodeSandbox( + "csb_v1_uv6a0J6nM43YEMkqJ2RE3T8fuFGmPKsnkZEUkhCBbcU", + { + baseUrl: "https://api.codesandbox.stream", + } +); +console.log("Getting sandbox..."); +let sandbox = await sdk.sandboxes.resume("37zz6l"); + +sandbox["pitcherManagerResponse"].pitcherURL = "https://37zz6l-57468.csb.dev"; +sandbox["pitcherManagerResponse"].pitcherToken = + "0085a669aaf703166263c038751432a5fcb5e56a7ededa131a953b253b5bee12"; + +const client = await sandbox.connect({ + env: { + FOO: "BAR", + }, +}); + +console.log(client.commands.run(""));