From d395483ea85172ccdb72f696df5b7e88c6dac066 Mon Sep 17 00:00:00 2001 From: Ehab Younes Date: Sun, 11 Oct 2026 14:26:57 +0300 Subject: [PATCH 1/2] ci: migrate the setup action to pnpm/setup Replace pnpm/action-setup, actions/setup-node, and the install step with a single pnpm/setup step. It installs Node 22 through `pnpm runtime set`, runs a frozen install, and keeps the platform fallback for the store cache. Only the default branch saves the store. Add `pnpm clean` to delete installs, build output, and caches from an explicit list, and have `pnpm package` clean and reinstall first. Webviews build into `dist/webviews/`, so a webview from another branch otherwise ships in the package. Closes #1119 --- .github/actions/setup/action.yml | 17 +++++------------ docs/TOOLING.md | 1 + package.json | 2 ++ scripts/clean.mjs | 19 +++++++++++++++++++ 4 files changed, 27 insertions(+), 12 deletions(-) create mode 100644 scripts/clean.mjs diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml index f980648f76..05d927f038 100644 --- a/.github/actions/setup/action.yml +++ b/.github/actions/setup/action.yml @@ -9,17 +9,10 @@ runs: # Needed for git-based deps (e.g. github:coder/coder) so pnpm can clone without SSH. run: git config --global url."https://github.com/".insteadOf "git@github.com:" - - uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0 + - uses: pnpm/setup@a0881977cd8ea73b1ed212236c99ca10dcc80cf4 # v3.1.1 with: - # Caches the pnpm store, keyed by lockfile hash with a prefix fallback, - # so a dependency bump reuses the rest of the store. setup-node's own - # pnpm cache restores on an exact hash only. + runtime: node@22 + require-lockfile: true + # Falls back to the platform's last store, so a bump fetches only the diff. cache: true - - - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 - with: - node-version: "22" - - - name: Install dependencies - shell: bash - run: pnpm install --frozen-lockfile + save-cache: ${{ github.ref == 'refs/heads/main' }} diff --git a/docs/TOOLING.md b/docs/TOOLING.md index d4b7b2f7fb..d470154695 100644 --- a/docs/TOOLING.md +++ b/docs/TOOLING.md @@ -64,6 +64,7 @@ When updating the minimum Node.js version, update these files: - **tsconfig.json**: `extends` (the `@tsconfig/nodeXX` package), `lib` (match base ESNext version) - **scripts/esbuild.mjs**: `target` - **.github/workflows/ci.yaml**: `electron-version` and `vscode-version` matrices +- **.github/actions/setup/action.yml**: `runtime` ## Dependencies diff --git a/package.json b/package.json index 4eaf7ed8cf..05925dd13e 100644 --- a/package.json +++ b/package.json @@ -29,12 +29,14 @@ "build:production": "cross-env NODE_ENV=production pnpm build", "build:test": "tsc -p test/integration --outDir out --noCheck", "build:webviews": "pnpm -r --filter \"./packages/*\" --parallel build", + "clean": "node scripts/clean.mjs", "format": "node scripts/run-tool.mjs oxfmt --write .", "format:check": "node scripts/run-tool.mjs oxfmt --check .", "lint": "concurrently -g -n oxlint,eslint \"pnpm lint:oxlint\" \"pnpm lint:eslint\"", "lint:eslint": "node scripts/run-tool.mjs eslint --cache --cache-strategy content --cache-location node_modules/.cache/eslint/ .", "lint:fix": "pnpm lint:oxlint --fix && pnpm lint:eslint --fix", "lint:oxlint": "node scripts/run-tool.mjs oxlint --type-aware .", + "prepackage": "pnpm clean && pnpm install --frozen-lockfile", "package": "pnpm build:production && vsce package --no-dependencies", "package:prerelease": "pnpm build:production && vsce package --pre-release --no-dependencies", "storybook": "storybook dev -p 6006 --config-dir .storybook", diff --git a/scripts/clean.mjs b/scripts/clean.mjs new file mode 100644 index 0000000000..6226e1987c --- /dev/null +++ b/scripts/clean.mjs @@ -0,0 +1,19 @@ +import { globSync, rmSync } from "node:fs"; + +// Generated files to delete, as paths or globs from the repo root. +const PATTERNS = [ + "node_modules", + "packages/*/node_modules", + "dist", + "out", + "storybook-static", + "coverage", + ".vscode-test", + ".pnpm-store", + "*.vsix", + "*.log", +]; + +for (const path of globSync(PATTERNS)) { + rmSync(path, { recursive: true, force: true }); +} From cc750286975c5ab611d8eca6751cf2020b93e466 Mon Sep 17 00:00:00 2001 From: Ehab Younes Date: Sun, 11 Oct 2026 14:38:07 +0300 Subject: [PATCH 2/2] build: vendor the Coder API client with a sync script The extension imports five files from coder/coder, but the git dependency downloads the whole 125 MB repository on every cold install. Add `pnpm sync:coder` to download the files into `vendor/coder` at a pinned release commit, and link the `coder` dependency to that folder so imports stay `coder/site/src/...`. The Lint job runs the sync and fails on any diff, so hand edits or a bumped commit without a resync can't merge. With no git dependencies left, drop the HTTPS rewrite from CI setup. --- .gitattributes | 1 + .github/actions/setup/action.yml | 5 - .github/workflows/ci.yaml | 3 + .oxfmtrc.jsonc | 5 +- .oxlintrc.jsonc | 3 +- docs/TOOLING.md | 8 +- eslint.config.mjs | 2 + package.json | 3 +- pnpm-lock.yaml | 13 +- pnpm-workspace.yaml | 1 - vendor/coder/site/src/api/api.ts | 4174 ++++++ vendor/coder/site/src/api/errors.ts | 154 + vendor/coder/site/src/api/typesGenerated.ts | 11840 ++++++++++++++++ .../coder/site/src/utils/OneWayWebSocket.ts | 226 + vendor/coder/site/src/utils/delay.ts | 4 + vendor/coder/sync.mjs | 41 + 16 files changed, 16457 insertions(+), 26 deletions(-) create mode 100644 vendor/coder/site/src/api/api.ts create mode 100644 vendor/coder/site/src/api/errors.ts create mode 100644 vendor/coder/site/src/api/typesGenerated.ts create mode 100644 vendor/coder/site/src/utils/OneWayWebSocket.ts create mode 100644 vendor/coder/site/src/utils/delay.ts create mode 100644 vendor/coder/sync.mjs diff --git a/.gitattributes b/.gitattributes index 08d76f5cdb..3437d5dd50 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,4 @@ * text=auto eol=lf pnpm-lock.yaml linguist-generated=true flake.lock linguist-generated=true +vendor/coder/site/** linguist-generated=true diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml index 05d927f038..809dcd6eef 100644 --- a/.github/actions/setup/action.yml +++ b/.github/actions/setup/action.yml @@ -4,11 +4,6 @@ description: Install pnpm, Node.js, and project dependencies. runs: using: composite steps: - - name: Use HTTPS for GitHub git clones - shell: bash - # Needed for git-based deps (e.g. github:coder/coder) so pnpm can clone without SSH. - run: git config --global url."https://github.com/".insteadOf "git@github.com:" - - uses: pnpm/setup@a0881977cd8ea73b1ed212236c99ca10dcc80cf4 # v3.1.1 with: runtime: node@22 diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 6301c1bb6f..34efccd0b0 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -28,6 +28,9 @@ jobs: - run: pnpm format:check + - name: Check vendored Coder client + run: pnpm sync:coder && git diff --exit-code vendor/coder + - run: pnpm lint - run: pnpm build diff --git a/.oxfmtrc.jsonc b/.oxfmtrc.jsonc index cd2940a01e..53d22dbc1b 100644 --- a/.oxfmtrc.jsonc +++ b/.oxfmtrc.jsonc @@ -7,9 +7,10 @@ "ignorePatterns": [ "flake.lock", "pnpm-lock.yaml", - // Golden snapshots and generated VS Code theme dumps are written verbatim. + // Golden snapshots, VS Code theme dumps, and the vendored Coder client are written verbatim. "**/__golden__/", - ".storybook/themes/generated/" + ".storybook/themes/generated/", + "vendor/coder/site/" ], "overrides": [ { diff --git a/.oxlintrc.jsonc b/.oxlintrc.jsonc index 030a401c01..8399b23aa4 100644 --- a/.oxlintrc.jsonc +++ b/.oxlintrc.jsonc @@ -17,7 +17,8 @@ "**/vite.config*.ts", ".vscode-test/**", "test/fixtures/scripts/**", - "storybook-static/**" + "storybook-static/**", + "vendor/coder/site/**" ], "overrides": [ { diff --git a/docs/TOOLING.md b/docs/TOOLING.md index d470154695..c3347080b6 100644 --- a/docs/TOOLING.md +++ b/docs/TOOLING.md @@ -74,11 +74,9 @@ Some dependencies are not directly used in the source but are required anyway. builds are off, so Windows on ARM64 uses their JavaScript fallback. - `ua-parser-js` and `dayjs` are used by the Coder API client. -The coder client is vendored from coder/coder. Pin it to a release tag in -`pnpm-workspace.yaml` (e.g. `coder: github:coder/coder#v2.33.1`), not `#main`. -A tag gives reproducible builds and is not re-fetched on every `pnpm install`, -unlike `#main` which can drift between installs. To update, bump the tag and -run `pnpm install` (or `pnpm update coder`). +The Coder API client is vendored from coder/coder into `vendor/coder/site`. To +update it, set `CODER_COMMIT` in `vendor/coder/sync.mjs` to a release commit +and run `pnpm sync:coder`. After running `pnpm update`, always run `pnpm dedupe` to consolidate duplicate package versions across the workspace. Without this, workspace packages can diff --git a/eslint.config.mjs b/eslint.config.mjs index dd11a8c2c4..2cc79b183b 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -25,6 +25,7 @@ export default defineConfig( ".vscode-test/**", "test/fixtures/scripts/**", "storybook-static/**", + "vendor/coder/site/**", ]), // Parse TypeScript without the type-aware programs; Oxlint owns typed rules. @@ -42,6 +43,7 @@ export default defineConfig( createTypeScriptImportResolver({ project: "./tsconfig.json" }), ], "import-x/internal-regex": "^@/", + "import-x/external-module-folders": ["node_modules", "vendor"], }, rules: { "import-x/order": [ diff --git a/package.json b/package.json index 05925dd13e..9df45e37fc 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ "storybook": "storybook dev -p 6006 --config-dir .storybook", "storybook:build": "storybook build --config-dir .storybook", "storybook:ci": "storybook build --test --config-dir .storybook", + "sync:coder": "node vendor/coder/sync.mjs", "sync:vscode-themes": "node .storybook/themes/sync.mjs", "test": "cross-env CI=true ELECTRON_RUN_AS_NODE=1 electron node_modules/vitest/vitest.mjs", "test:extension": "cross-env ELECTRON_RUN_AS_NODE=1 electron node_modules/vitest/vitest.mjs --project extension", @@ -854,7 +855,7 @@ "@vscode/vsce": "^4.0.0", "babel-plugin-react-compiler": "catalog:", "bufferutil": "^4.1.0", - "coder": "catalog:", + "coder": "link:vendor/coder", "concurrently": "^10.0.5", "cross-env": "^10.1.0", "dayjs": "^1.11.23", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 091f0ceb03..a82ef5ebd2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -207,9 +207,6 @@ catalogs: babel-plugin-react-compiler: specifier: ^1.0.0 version: 1.0.0 - coder: - specifier: github:coder/coder#22f4284f8fa67bc3af6fc38e0079616d39128566 - version: https://codeload.github.com/coder/coder/tar.gz/22f4284f8fa67bc3af6fc38e0079616d39128566 date-fns: specifier: ^4.4.0 version: 4.4.0 @@ -397,8 +394,8 @@ importers: specifier: ^4.1.0 version: 4.1.0 coder: - specifier: 'catalog:' - version: '@coder/coder@https://codeload.github.com/coder/coder/tar.gz/22f4284f8fa67bc3af6fc38e0079616d39128566' + specifier: link:vendor/coder + version: link:vendor/coder concurrently: specifier: ^10.0.5 version: 10.0.5 @@ -896,10 +893,6 @@ packages: '@cacheable/utils@2.5.0': resolution: {integrity: sha512-buipgOVDkkPXNR5+xBpDw7Zk2n1EvU7qBJCNUcL7rhQ//kfpOXPAvQ511Os0vpLYJ1pZnvudNytkQt2hst3wqA==} - '@coder/coder@https://codeload.github.com/coder/coder/tar.gz/22f4284f8fa67bc3af6fc38e0079616d39128566': - resolution: {gitHosted: true, integrity: sha512-jypoiakjE9qbfTR1KTO6U2VJDHN2SpTGwBSBsWXBdC/2nHAmpkVx9CcUYNE98xMAvimh3CaoQ/B79ZTwP2RH2A==, tarball: https://codeload.github.com/coder/coder/tar.gz/22f4284f8fa67bc3af6fc38e0079616d39128566} - version: 0.0.0 - '@coder/pixel-storybook@0.3.0': resolution: {integrity: sha512-+M3tr9lfjN/B2v3XCYDeAcz4TqZnN80Xa1ezpBS1NejsmhGf6RePWDz9ksgLmHGJ0iCmAypjbCX8BFosOfYeoA==} engines: {node: '>=22'} @@ -5712,8 +5705,6 @@ snapshots: hashery: 1.5.1 keyv: 5.6.0 - '@coder/coder@https://codeload.github.com/coder/coder/tar.gz/22f4284f8fa67bc3af6fc38e0079616d39128566': {} - '@coder/pixel-storybook@0.3.0(playwright-core@1.63.0)(storybook@10.6.1)': dependencies: get-port-please: 3.1.2 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index a9ece3e060..8cddb92bba 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -16,7 +16,6 @@ catalog: "@vscode-elements/react-elements": ^2.4.0 "@vscode/codicons": 0.0.46-24 babel-plugin-react-compiler: ^1.0.0 - coder: github:coder/coder#22f4284f8fa67bc3af6fc38e0079616d39128566 # v2.37.1 date-fns: ^4.4.0 react: ^19.3.0 react-dom: ^19.3.0 diff --git a/vendor/coder/site/src/api/api.ts b/vendor/coder/site/src/api/api.ts new file mode 100644 index 0000000000..7d6eba4b8c --- /dev/null +++ b/vendor/coder/site/src/api/api.ts @@ -0,0 +1,4174 @@ +/** + * @file Coder is starting to import the Coder API file into more and more + * external projects, as a "pseudo-SDK". We are not at a stage where we are + * ready to commit to maintaining a public SDK, but we need equivalent + * functionality in other places. + * + * Message somebody from Team Blueberry if you need more context, but so far, + * these projects are importing the file: + * + * - The Coder VS Code extension + * @see {@link https://github.com/coder/vscode-coder} + * - The Coder Backstage plugin + * @see {@link https://github.com/coder/backstage-plugins} + * + * It is important that this file not do any aliased imports, or else the other + * consumers could break (particularly for platforms that limit how much you can + * touch their configuration files, like Backstage). Relative imports are still + * safe, though. + * + * For example, `utils/delay` must be imported using `../utils/delay` instead. + */ +import globalAxios, { type AxiosInstance, isAxiosError } from "axios"; +import type dayjs from "dayjs"; +import userAgentParser from "ua-parser-js"; +import { delay } from "../utils/delay"; +import { + OneWayWebSocket, + type OneWayWebSocketApi, +} from "../utils/OneWayWebSocket"; +import { type FieldError, isApiError } from "./errors"; +import type { + AdvisorConfig, + DeleteExternalAuthByIDResponse, + DynamicParametersRequest, + PostWorkspaceUsageRequest, + UpdateAdvisorConfigRequest, + UsersRequest, +} from "./typesGenerated"; +import * as TypesGen from "./typesGenerated"; + +/** + * Originally from codersdk/client.go. + * The below declaration is required to stop Knip from complaining. + * @public + */ +export const SessionTokenCookie = "coder_session_token"; + +/** + * @param agentId + * @returns {OneWayWebSocket} A OneWayWebSocket that emits Server-Sent Events. + */ +export const watchAgentMetadata = ( + agentId: string, +): OneWayWebSocket => { + return new OneWayWebSocket({ + apiRoute: `/api/v2/workspaceagents/${agentId}/watch-metadata-ws`, + }); +}; + +/** + * @returns {OneWayWebSocket} A OneWayWebSocket that emits Server-Sent Events. + */ +export const watchWorkspace = ( + workspaceId: string, +): OneWayWebSocket => { + return new OneWayWebSocket({ + apiRoute: `/api/v2/workspaces/${workspaceId}/watch-ws`, + }); +}; + +export const watchChat = ( + chatId: string, + afterMessageId?: number, +): OneWayWebSocketApi => { + const params = new URLSearchParams(); + if (afterMessageId !== undefined && afterMessageId > 0) { + params.set("after_id", afterMessageId.toString()); + } + const token = API.getSessionToken(); + if (token) { + params.set(SessionTokenCookie, token); + } + const query = params.toString(); + const route = `/api/v2/chats/${chatId}/stream${query ? `?${query}` : ""}`; + return new OneWayWebSocket({ + apiRoute: route, + }); +}; + +export const watchChats = (): OneWayWebSocket => { + const searchParams: Record = {}; + const token = API.getSessionToken(); + if (token) { + searchParams[SessionTokenCookie] = token; + } + return new OneWayWebSocket({ + apiRoute: "/api/v2/chats/watch", + searchParams, + }); +}; + +export const watchChatGit = (chatId: string): WebSocket => { + return createWebSocket(`/api/v2/chats/${chatId}/stream/git`); +}; + +export const watchChatDesktop = (chatId: string): WebSocket => { + const socket = createWebSocket( + `/api/experimental/chats/${chatId}/stream/desktop`, + ); + // RFB is a binary protocol — noVNC expects arraybuffer, not blob. + socket.binaryType = "arraybuffer"; + return socket; +}; + +export const watchAgentContainers = ( + agentId: string, +): OneWayWebSocket => { + return new OneWayWebSocket({ + apiRoute: `/api/v2/workspaceagents/${agentId}/containers/watch`, + }); +}; + +type WatchInboxNotificationsParams = Readonly<{ + read_status?: "read" | "unread" | "all"; +}>; + +export function watchInboxNotifications( + params?: WatchInboxNotificationsParams, +): OneWayWebSocket { + return new OneWayWebSocket({ + apiRoute: "/api/v2/notifications/inbox/watch", + searchParams: params, + }); +} + +export const getURLWithSearchParams = ( + basePath: string, + options?: object, +): string => { + if (!options) { + return basePath; + } + + const searchParams = new URLSearchParams(); + for (const [key, value] of Object.entries(options)) { + if (value !== undefined && value !== "") { + searchParams.append(key, value.toString()); + } + } + + const searchString = searchParams.toString(); + return searchString ? `${basePath}?${searchString}` : basePath; +}; + +// withDefaultFeatures sets all unspecified features to not_entitled and +// disabled. +export const withDefaultFeatures = ( + fs: Partial, +): TypesGen.Entitlements["features"] => { + for (const feature of TypesGen.FeatureNames) { + // Skip fields that are already filled. + if (fs[feature] !== undefined) { + continue; + } + + fs[feature] = { + enabled: false, + entitlement: "not_entitled", + }; + } + + return fs as TypesGen.Entitlements["features"]; +}; + +type WatchBuildLogsByTemplateVersionIdOptions = { + after?: number; + onMessage: (log: TypesGen.ProvisionerJobLog) => void; + onDone?: () => void; + onError: (error: Error) => void; +}; + +export const watchBuildLogsByTemplateVersionId = ( + versionId: string, + { + onMessage, + onDone, + onError, + after, + }: WatchBuildLogsByTemplateVersionIdOptions, +) => { + const searchParams = new URLSearchParams({ follow: "true" }); + if (after !== undefined) { + searchParams.append("after", after.toString()); + } + + const socket = createWebSocket( + `/api/v2/templateversions/${versionId}/logs`, + searchParams, + ); + + socket.addEventListener("message", (event) => + onMessage(JSON.parse(event.data) as TypesGen.ProvisionerJobLog), + ); + + socket.addEventListener("error", () => { + onError(new Error("Connection for logs failed.")); + socket.close(); + }); + + socket.addEventListener("close", () => { + // When the socket closes, logs have finished streaming! + onDone?.(); + }); + + return socket; +}; + +export const watchWorkspaceAgentLogs = ( + agentId: string, + params?: WatchWorkspaceAgentLogsParams, +) => { + const searchParams = new URLSearchParams({ + follow: "true", + after: params?.after?.toString() ?? "", + }); + + /** + * WebSocket compression in Safari (confirmed in 16.5) is broken when + * the server sends large messages. The following error is seen: + * WebSocket connection to 'wss://...' failed: The operation couldn't be completed. + */ + if (userAgentParser(navigator.userAgent).browser.name === "Safari") { + searchParams.set("no_compression", ""); + } + + return new OneWayWebSocket({ + apiRoute: `/api/v2/workspaceagents/${agentId}/logs`, + searchParams, + }); +}; + +type WatchWorkspaceAgentLogsParams = { + after?: number; +}; + +type WatchBuildLogsByBuildIdOptions = { + after?: number; + onMessage: (log: TypesGen.ProvisionerJobLog) => void; + onDone?: () => void; + onError?: (error: Error) => void; +}; +export const watchBuildLogsByBuildId = ( + buildId: string, + { onMessage, onDone, onError, after }: WatchBuildLogsByBuildIdOptions, +) => { + const searchParams = new URLSearchParams({ follow: "true" }); + if (after !== undefined) { + searchParams.append("after", after.toString()); + } + + const socket = createWebSocket( + `/api/v2/workspacebuilds/${buildId}/logs`, + searchParams, + ); + + socket.addEventListener("message", (event) => + onMessage(JSON.parse(event.data) as TypesGen.ProvisionerJobLog), + ); + + socket.addEventListener("error", () => { + if (socket.readyState === socket.CLOSED) { + return; + } + onError?.(new Error("Connection for logs failed.")); + socket.close(); + }); + + socket.addEventListener("close", () => { + // When the socket closes, logs have finished streaming! + onDone?.(); + }); + + return socket; +}; + +// This is the base header that is used for several requests. This is defined as +// a readonly value, but only copies of it should be passed into the API calls, +// because Axios is able to mutate the headers +const BASE_CONTENT_TYPE_JSON = { + "Content-Type": "application/json", +} as const satisfies HeadersInit; + +export type GetTemplatesOptions = Readonly<{ + readonly deprecated?: boolean; +}>; + +export type GetTemplatesQuery = Readonly<{ + readonly q: string; +}>; + +function normalizeGetTemplatesOptions( + options: GetTemplatesOptions | GetTemplatesQuery = {}, +): Record { + if ("q" in options) { + return options; + } + + const params: Record = {}; + if (options.deprecated !== undefined) { + params.deprecated = String(options.deprecated); + } + return params; +} + +type SearchParamOptions = TypesGen.Pagination & { + q?: string; +}; + +type RestartWorkspaceParameters = Readonly<{ + workspace: TypesGen.Workspace; + buildParameters?: TypesGen.WorkspaceBuildParameter[]; +}>; + +export type DeleteWorkspaceOptions = Pick< + TypesGen.CreateWorkspaceBuildRequest, + "log_level" | "orphan" +>; + +export type DeploymentConfig = Readonly<{ + config: TypesGen.DeploymentValues; + options: TypesGen.SerpentOption[]; +}>; + +/** + * Fetches `items` in concurrent batches of at most `batchSize`, resolving + * with one response per batch, in input order. + */ +async function fetchInBatches( + items: readonly Item[], + batchSize: number, + fetchBatch: (batch: readonly Item[]) => Promise, +): Promise { + const batches: Promise[] = []; + for (let i = 0; i < items.length; i += batchSize) { + batches.push(fetchBatch(items.slice(i, i + batchSize))); + } + return Promise.all(batches); +} + +/** The AI spend endpoints reject requests with more than 100 IDs. */ +const aiSpendBatchSize = 100; + +const aiProviderConfigsPath = "/api/v2/ai/providers"; +const aiGatewayPath = "/api/v2/ai-gateway"; +const chatModelsPath = (organizationId: string) => + `/api/v2/organizations/${encodeURIComponent(organizationId)}/chats/models`; +const chatModelPath = (organizationId: string, modelId: string) => + `${chatModelsPath(organizationId)}/${encodeURIComponent(modelId)}`; +const chatModelACLPath = (organizationId: string, modelId: string) => + `${chatModelPath(organizationId, modelId)}/acl`; +const userSkillsPath = (user: string) => + `/api/experimental/users/${encodeURIComponent(user)}/skills`; +const userSkillPath = (user: string, name: string) => + `${userSkillsPath(user)}/${encodeURIComponent(name)}`; +const userAIProviderKeysPath = (user = "me") => + `/api/v2/users/${encodeURIComponent(user)}/ai-provider-keys`; +const mcpServerConfigsPath = (organization: string) => + `/api/v2/organizations/${encodeURIComponent(organization)}/mcp-servers`; +const mcpServerConfigPath = (organization: string, id: string) => + `${mcpServerConfigsPath(organization)}/${encodeURIComponent(id)}`; +const mcpServerConfigACLPath = (organization: string, id: string) => + `${mcpServerConfigPath(organization, id)}/acl`; +const mcpServerConfigACLAvailablePath = (organization: string, id: string) => + `/api/v2/organizations/${encodeURIComponent(organization)}/mcp-servers/${encodeURIComponent(id)}/acl/available`; +export const mcpServerOAuth2ConnectPath = (organization: string, id: string) => + `${mcpServerConfigPath(organization, id)}/oauth2/connect`; +const mcpServerOAuth2DisconnectPath = (id: string) => + `/api/v2/mcp/servers/${encodeURIComponent(id)}/oauth2/disconnect`; + +type Claims = { + license_expires: number; + // nbf is a standard JWT claim for "not before" - the license valid from date + nbf?: number; + // iat is a standard JWT claim for "issued at"; the merged + // usage_period.issued_at is stamped from the winning license's iat. + iat?: number; + // exp is a standard JWT claim for "expires at" (end of grace period); + // it stamps usage_period.end, and nbf stamps usage_period.start. + exp?: number; + account_type?: string; + account_id?: string; + trial: boolean; + all_features: boolean; + // feature_set is omitted on legacy licenses + feature_set?: string; + addons?: string[]; + version: number; + features: Record; + require_telemetry?: boolean; +}; + +export type GetLicensesResponse = Omit & { + claims: Claims; + expires_at: string; +}; + +export type InsightsParams = { + start_time: string; + end_time: string; + template_ids: string; +}; + +export type InsightsTemplateParams = InsightsParams & { + interval: "day" | "week"; +}; + +export class ParameterValidationError extends Error { + constructor( + public readonly versionId: string, + public readonly validations: FieldError[], + ) { + super("Parameters are not valid for new template version"); + } +} + +export type GetProvisionerJobsParams = { + status?: string; + limit?: number; + // IDs separated by comma + ids?: string; +}; + +export type GetProvisionerDaemonsParams = { + // IDs separated by comma + ids?: string; + // Stringified JSON Object + tags?: string; + limit?: number; + // Include offline provisioner daemons? + offline?: boolean; +}; + +/** + * This is the container for all API methods. It's split off to make it more + * clear where API methods should go, but it is eventually merged into the Api + * class with a more flat hierarchy + * + * All public methods should be defined as arrow functions to ensure that they + * can be passed around the React UI without losing their `this` context. + * + * This is one of the few cases where you have to worry about the difference + * between traditional methods and arrow function properties. Arrow functions + * disable JS's dynamic scope, and force all `this` references to resolve via + * lexical scope. + */ +class ApiMethods { + experimental: ExperimentalApiMethods; + + constructor(protected readonly axios: AxiosInstance) { + this.experimental = new ExperimentalApiMethods(this.axios); + } + + login = async ( + email: string, + password: string, + ): Promise => { + const payload = JSON.stringify({ email, password }); + const response = await this.axios.post( + "/api/v2/users/login", + payload, + { headers: { ...BASE_CONTENT_TYPE_JSON } }, + ); + + return response.data; + }; + + convertToOAUTH = async (request: TypesGen.ConvertLoginRequest) => { + const response = await this.axios.post( + "/api/v2/users/me/convert-login", + request, + ); + + return response.data; + }; + + logout = async (): Promise => { + return this.axios.post("/api/v2/users/logout"); + }; + + getAuthenticatedUser = async () => { + const response = await this.axios.get("/api/v2/users/me"); + return response.data; + }; + + getUserAISpend = async (): Promise => { + const response = await this.axios.get( + "/api/v2/users/me/ai/spend", + ); + return response.data; + }; + + getUser = async (usernameOrId: string) => { + const response = await this.axios.get( + `/api/v2/users/${encodeURIComponent(usernameOrId)}`, + ); + return response.data; + }; + + getUserParameters = async (templateID: string) => { + const response = await this.axios.get( + `/api/v2/users/me/autofill-parameters?template_id=${templateID}`, + ); + + return response.data; + }; + + getAuthMethods = async (): Promise => { + const response = await this.axios.get( + "/api/v2/users/authmethods", + ); + + return response.data; + }; + + getUserLoginType = async (): Promise => { + const response = await this.axios.get( + "/api/v2/users/me/login-type", + ); + + return response.data; + }; + + checkAuthorization = async ( + params: TypesGen.AuthorizationRequest, + ) => { + const response = await this.axios.post( + "/api/v2/authcheck", + params, + ); + + return response.data; + }; + + getApiKey = async (): Promise => { + const response = await this.axios.post( + "/api/v2/users/me/keys", + ); + + return response.data; + }; + + getTokens = async ( + params: TypesGen.TokensFilter, + ): Promise => { + const response = await this.axios.get( + "/api/v2/users/me/keys/tokens", + { params }, + ); + + return response.data; + }; + + deleteToken = async (keyId: string): Promise => { + await this.axios.delete(`/api/v2/users/me/keys/${keyId}`); + }; + + createToken = async ( + params: TypesGen.CreateTokenRequest, + ): Promise => { + const response = await this.axios.post( + "/api/v2/users/me/keys/tokens", + params, + ); + + return response.data; + }; + + getTokenConfig = async (): Promise => { + const response = await this.axios.get( + "/api/v2/users/me/keys/tokens/tokenconfig", + ); + + return response.data; + }; + + getUsers = async ( + options: TypesGen.UsersRequest, + signal?: AbortSignal, + ): Promise => { + const url = getURLWithSearchParams("/api/v2/users", options); + const response = await this.axios.get( + url.toString(), + { signal }, + ); + + return response.data; + }; + + /** + * Get users for workspace owner selection. Requires + * permission to create workspaces for other users in the + * organization. Returns minimal user data (no email, roles, + * etc.). + */ + getWorkspaceAvailableUsers = async ( + organizationId: string, + options: TypesGen.UsersRequest, + signal?: AbortSignal, + ): Promise => { + const url = getURLWithSearchParams( + `/api/v2/organizations/${organizationId}/members/me/workspaces/available-users`, + options, + ); + const response = await this.axios.get( + url.toString(), + { signal }, + ); + return response.data; + }; + + createOrganization = async (params: TypesGen.CreateOrganizationRequest) => { + const response = await this.axios.post( + "/api/v2/organizations", + params, + ); + return response.data; + }; + + /** + * @param organization Can be the organization's ID or name + */ + updateOrganization = async ( + organization: string, + params: TypesGen.UpdateOrganizationRequest, + ) => { + const response = await this.axios.patch( + `/api/v2/organizations/${organization}`, + params, + ); + return response.data; + }; + + /** + * @param organization Can be the organization's ID or name + */ + deleteOrganization = async (organization: string) => { + await this.axios.delete( + `/api/v2/organizations/${organization}`, + ); + }; + + /** + * @param organization Can be the organization's ID or name + */ + getOrganization = async ( + organization: string, + ): Promise => { + const response = await this.axios.get( + `/api/v2/organizations/${organization}`, + ); + + return response.data; + }; + + /** + * @param organization Can be the organization's ID or name + */ + getOrganizationMembers = async (organization: string) => { + const response = await this.axios.get< + TypesGen.OrganizationMemberWithUserData[] + >(`/api/v2/organizations/${organization}/members`); + + return response.data; + }; + + /** + * @param organization Can be the organization's ID or name + * @param options Pagination options + */ + getOrganizationPaginatedMembers = async ( + organization: string, + options?: TypesGen.UsersRequest, + ) => { + const url = getURLWithSearchParams( + `/api/v2/organizations/${organization}/paginated-members`, + options, + ); + const response = + await this.axios.get(url); + + return response.data; + }; + + /** + * @param organization Can be the organization's ID or name + */ + getOrganizationRoles = async (organization: string) => { + const response = await this.axios.get( + `/api/v2/organizations/${organization}/members/roles`, + ); + + return response.data; + }; + + /** + * @param organization Can be the organization's ID or name + */ + updateOrganizationMemberRoles = async ( + organization: string, + userId: string, + roles: TypesGen.SlimRole["name"][], + ): Promise => { + const response = await this.axios.put( + `/api/v2/organizations/${organization}/members/${userId}/roles`, + { roles }, + ); + + return response.data; + }; + + /** + * @param organization Can be the organization's ID or name + */ + createOrganizationRole = async ( + organization: string, + role: TypesGen.Role, + ): Promise => { + const response = await this.axios.post( + `/api/v2/organizations/${organization}/members/roles`, + role, + ); + + return response.data; + }; + + /** + * @param organization Can be the organization's ID or name + */ + updateOrganizationRole = async ( + organization: string, + role: TypesGen.Role, + ): Promise => { + const response = await this.axios.put( + `/api/v2/organizations/${organization}/members/roles`, + role, + ); + + return response.data; + }; + + /** + * @param organization Can be the organization's ID or name + */ + deleteOrganizationRole = async (organization: string, roleName: string) => { + await this.axios.delete( + `/api/v2/organizations/${organization}/members/roles/${roleName}`, + ); + }; + + /** + * @param organization Can be the organization's ID or name + */ + addOrganizationMember = async (organization: string, userId: string) => { + const response = await this.axios.post( + `/api/v2/organizations/${organization}/members/${userId}`, + ); + + return response.data; + }; + + /** + * @param organization Can be the organization's ID or name + */ + removeOrganizationMember = async (organization: string, userId: string) => { + await this.axios.delete( + `/api/v2/organizations/${organization}/members/${userId}`, + ); + }; + + getOrganizations = async (): Promise => { + const response = await this.axios.get( + "/api/v2/organizations", + ); + return response.data; + }; + + getMyOrganizations = async (): Promise => { + const response = await this.axios.get( + "/api/v2/users/me/organizations", + ); + return response.data; + }; + + /** + * @param organization Can be the organization's ID or name + */ + getWorkspaceSharingSettings = async ( + organization: string, + ): Promise => { + const response = await this.axios.get( + `/api/v2/organizations/${organization}/settings/workspace-sharing`, + ); + return response.data; + }; + + /** + * @param organization Can be the organization's ID or name + */ + patchWorkspaceSharingSettings = async ( + organization: string, + data: TypesGen.UpdateWorkspaceSharingSettingsRequest, + ): Promise => { + const response = await this.axios.patch( + `/api/v2/organizations/${organization}/settings/workspace-sharing`, + data, + ); + return response.data; + }; + + getProvisionerDaemonsByOrganization = async ( + organization: string, + params?: GetProvisionerDaemonsParams, + ): Promise => { + const response = await this.axios.get( + `/api/v2/organizations/${organization}/provisionerdaemons`, + { params }, + ); + return response.data; + }; + + /** + * @param organization Can be the organization's ID or name + */ + getProvisionerDaemonGroupsByOrganization = async ( + organization: string, + ): Promise => { + const response = await this.axios.get( + `/api/v2/organizations/${organization}/provisionerkeys/daemons`, + ); + return response.data; + }; + + getOrganizationIdpSyncSettings = + async (): Promise => { + const response = await this.axios.get( + "/api/v2/settings/idpsync/organization", + ); + return response.data; + }; + + patchOrganizationIdpSyncSettings = async ( + data: TypesGen.OrganizationSyncSettings, + ) => { + const response = await this.axios.patch( + "/api/v2/settings/idpsync/organization", + data, + ); + return response.data; + }; + + /** + * @param data + * @param organization Can be the organization's ID or name + */ + patchGroupIdpSyncSettings = async ( + data: TypesGen.GroupSyncSettings, + organization: string, + ) => { + const response = await this.axios.patch( + `/api/v2/organizations/${organization}/settings/idpsync/groups`, + data, + ); + return response.data; + }; + + /** + * @param data + * @param organization Can be the organization's ID or name + */ + patchRoleIdpSyncSettings = async ( + data: TypesGen.RoleSyncSettings, + organization: string, + ) => { + const response = await this.axios.patch( + `/api/v2/organizations/${organization}/settings/idpsync/roles`, + data, + ); + return response.data; + }; + + /** + * @param organization Can be the organization's ID or name + */ + getGroupIdpSyncSettingsByOrganization = async ( + organization: string, + ): Promise => { + const response = await this.axios.get( + `/api/v2/organizations/${organization}/settings/idpsync/groups`, + ); + return response.data; + }; + + /** + * @param organization Can be the organization's ID or name + */ + getRoleIdpSyncSettingsByOrganization = async ( + organization: string, + ): Promise => { + const response = await this.axios.get( + `/api/v2/organizations/${organization}/settings/idpsync/roles`, + ); + return response.data; + }; + + getDeploymentIdpSyncFieldValues = async ( + field: string, + ): Promise => { + const params = new URLSearchParams(); + params.set("claimField", field); + const response = await this.axios.get( + `/api/v2/settings/idpsync/field-values?${params}`, + ); + return response.data; + }; + + getOrganizationIdpSyncClaimFieldValues = async ( + organization: string, + field: string, + ) => { + const params = new URLSearchParams(); + params.set("claimField", field); + const response = await this.axios.get( + `/api/v2/organizations/${organization}/settings/idpsync/field-values?${params}`, + ); + return response.data; + }; + + getTemplate = async (templateId: string): Promise => { + const response = await this.axios.get( + `/api/v2/templates/${templateId}`, + ); + + return response.data; + }; + + getTemplates = async ( + options?: GetTemplatesOptions | GetTemplatesQuery, + ): Promise => { + const params = normalizeGetTemplatesOptions(options); + const response = await this.axios.get( + "/api/v2/templates", + { params }, + ); + + return response.data; + }; + + /** + * @param organization Can be the organization's ID or name + */ + getTemplatesByOrganization = async ( + organization: string, + options?: GetTemplatesOptions, + ): Promise => { + const params = normalizeGetTemplatesOptions(options); + const response = await this.axios.get( + `/api/v2/organizations/${organization}/templates`, + { params }, + ); + + return response.data; + }; + + /** + * @param organization Can be the organization's ID or name + */ + getTemplateByName = async ( + organization: string, + name: string, + ): Promise => { + const response = await this.axios.get( + `/api/v2/organizations/${organization}/templates/${name}`, + ); + + return response.data; + }; + + getTemplateVersion = async ( + versionId: string, + ): Promise => { + const response = await this.axios.get( + `/api/v2/templateversions/${versionId}`, + ); + + return response.data; + }; + + getTemplateVersionResources = async ( + versionId: string, + ): Promise => { + const response = await this.axios.get( + `/api/v2/templateversions/${versionId}/resources`, + ); + + return response.data; + }; + + getTemplateVersionVariables = async ( + versionId: string, + ): Promise => { + // Defined as separate variable to avoid wonky Prettier formatting because + // the type definition is so long + type VerArray = TypesGen.TemplateVersionVariable[]; + + const response = await this.axios.get( + `/api/v2/templateversions/${versionId}/variables`, + ); + + return response.data; + }; + + getTemplateVersions = async ( + templateId: string, + ): Promise => { + const response = await this.axios.get( + `/api/v2/templates/${templateId}/versions`, + ); + return response.data; + }; + + /** + * @param organization Can be the organization's ID or name + */ + getTemplateVersionByName = async ( + organization: string, + templateName: string, + versionName: string, + ): Promise => { + const response = await this.axios.get( + `/api/v2/organizations/${organization}/templates/${templateName}/versions/${versionName}`, + ); + + return response.data; + }; + + /** + * @param organization Can be the organization's ID or name + */ + getPreviousTemplateVersionByName = async ( + organization: string, + templateName: string, + versionName: string, + ) => { + const response = await this.axios.get( + `/api/v2/organizations/${organization}/templates/${templateName}/versions/${versionName}/previous`, + ); + + // The API returns 204 No Content when there is no previous version + // (e.g. the first version of a template). + if (response.status === 204) { + return undefined; + } + + return response.data; + }; + + /** + * @param organization Can be the organization's ID or name + */ + createTemplateVersion = async ( + organization: string, + data: TypesGen.CreateTemplateVersionRequest, + ): Promise => { + const response = await this.axios.post( + `/api/v2/organizations/${organization}/templateversions`, + data, + ); + + return response.data; + }; + + getTemplateVersionExternalAuth = async ( + versionId: string, + userId = "me", + ): Promise => { + const response = await this.axios.get( + `/api/v2/templateversions/${versionId}/external-auth?user_id=${userId}`, + ); + + return response.data; + }; + + getTemplateVersionDynamicParameters = async ( + versionId: string, + data: TypesGen.DynamicParametersRequest, + ): Promise => { + const response = await this.axios.post( + `/api/v2/templateversions/${versionId}/dynamic-parameters/evaluate`, + data, + ); + return response.data; + }; + + getTemplateVersionRichParameters = async ( + versionId: string, + ): Promise => { + const response = await this.axios.get( + `/api/v2/templateversions/${versionId}/rich-parameters`, + ); + return response.data; + }; + + templateVersionDynamicParameters = ( + versionId: string, + userId: string, + { + onOpen, + onMessage, + onError, + onClose, + }: { + onOpen?: () => void; + onMessage: (response: TypesGen.DynamicParametersResponse) => void; + onError: (error: Error) => void; + onClose: () => void; + }, + ): WebSocket => { + const socket = createWebSocket( + `/api/v2/templateversions/${versionId}/dynamic-parameters`, + new URLSearchParams({ user_id: userId }), + ); + + socket.addEventListener("open", () => { + onOpen?.(); + }); + + socket.addEventListener("message", (event) => + onMessage(JSON.parse(event.data) as TypesGen.DynamicParametersResponse), + ); + + socket.addEventListener("error", () => { + onError(new Error("Connection for dynamic parameters failed.")); + socket.close(); + }); + + socket.addEventListener("close", () => { + onClose(); + }); + + return socket; + }; + + /** + * @param organization Can be the organization's ID or name + */ + createTemplate = async ( + organization: string, + data: TypesGen.CreateTemplateRequest, + ): Promise => { + const response = await this.axios.post( + `/api/v2/organizations/${organization}/templates`, + data, + ); + + return response.data; + }; + + updateActiveTemplateVersion = async ( + templateId: string, + data: TypesGen.UpdateActiveTemplateVersion, + ) => { + const response = await this.axios.patch( + `/api/v2/templates/${templateId}/versions`, + data, + ); + return response.data; + }; + + patchTemplateVersion = async ( + templateVersionId: string, + data: TypesGen.PatchTemplateVersionRequest, + ) => { + const response = await this.axios.patch( + `/api/v2/templateversions/${templateVersionId}`, + data, + ); + + return response.data; + }; + + archiveTemplateVersion = async (templateVersionId: string): Promise => { + await this.axios.post( + `/api/v2/templateversions/${templateVersionId}/archive`, + ); + }; + + unarchiveTemplateVersion = async ( + templateVersionId: string, + ): Promise => { + await this.axios.post( + `/api/v2/templateversions/${templateVersionId}/unarchive`, + ); + }; + + /** + * Downloads a template version as a tar or zip archive + * @param fileId The file ID from the template version's job + * @param format Optional format: "zip" for zip archive, empty/undefined for tar + * @returns Promise that resolves to a Blob containing the archive + */ + downloadTemplateVersion = async ( + fileId: string, + format?: "zip", + ): Promise => { + const params = new URLSearchParams(); + if (format) { + params.set("format", format); + } + + const response = await this.axios.get( + `/api/v2/files/${fileId}?${params.toString()}`, + { + responseType: "blob", + }, + ); + + return response.data; + }; + + updateTemplateMeta = async ( + templateId: string, + data: TypesGen.UpdateTemplateMeta, + ): Promise => { + const response = await this.axios.patch( + `/api/v2/templates/${templateId}`, + data, + ); + + // On 304 response there is no data payload. + if (response.status === 304) { + return null; + } + + return response.data; + }; + + deleteTemplate = async (templateId: string): Promise => { + const response = await this.axios.delete( + `/api/v2/templates/${templateId}`, + ); + + return response.data; + }; + + invalidateTemplatePresets = async ( + templateId: string, + ): Promise => { + const response = await this.axios.post( + `/api/v2/templates/${templateId}/prebuilds/invalidate`, + ); + return response.data; + }; + + getWorkspace = async ( + workspaceId: string, + params?: TypesGen.WorkspaceOptions, + ): Promise => { + const response = await this.axios.get( + `/api/v2/workspaces/${workspaceId}`, + { params }, + ); + + return response.data; + }; + + getWorkspaces = async ( + req: TypesGen.WorkspacesRequest, + ): Promise => { + const url = getURLWithSearchParams("/api/v2/workspaces", req); + const response = await this.axios.get(url); + return response.data; + }; + + getWorkspaceByOwnerAndName = async ( + username: string, + workspaceName: string, + params?: TypesGen.WorkspaceOptions, + ): Promise => { + const response = await this.axios.get( + `/api/v2/users/${username}/workspace/${workspaceName}`, + { params }, + ); + + return response.data; + }; + + getWorkspaceBuildByNumber = async ( + username: string, + workspaceName: string, + buildNumber: number, + ): Promise => { + const response = await this.axios.get( + `/api/v2/users/${username}/workspace/${workspaceName}/builds/${buildNumber}`, + ); + + return response.data; + }; + + waitForBuild = (build: TypesGen.WorkspaceBuild) => { + return new Promise((res, reject) => { + void (async () => { + let latestJobInfo: TypesGen.ProvisionerJob | undefined; + + while ( + !["succeeded", "canceled"].some((status) => + latestJobInfo?.status.includes(status), + ) + ) { + const { job } = await this.getWorkspaceBuildByNumber( + build.workspace_owner_name, + build.workspace_name, + build.build_number, + ); + + latestJobInfo = job; + if (latestJobInfo.status === "failed") { + return reject(latestJobInfo); + } + + await delay(1000); + } + + return res(latestJobInfo); + })(); + }); + }; + + postWorkspaceBuild = async ( + workspaceId: string, + data: TypesGen.CreateWorkspaceBuildRequest, + ): Promise => { + const response = await this.axios.post( + `/api/v2/workspaces/${workspaceId}/builds`, + data, + ); + return response.data; + }; + + getTemplateVersionPresets = async ( + templateVersionId: string, + ): Promise => { + const response = await this.axios.get( + `/api/v2/templateversions/${templateVersionId}/presets`, + ); + return response.data; + }; + + startWorkspace = ( + workspaceId: string, + templateVersionId: string, + logLevel?: TypesGen.ProvisionerLogLevel, + buildParameters?: TypesGen.WorkspaceBuildParameter[], + ) => { + return this.postWorkspaceBuild(workspaceId, { + transition: "start", + template_version_id: templateVersionId, + log_level: logLevel, + rich_parameter_values: buildParameters, + reason: "dashboard", + }); + }; + + stopWorkspace = ( + workspaceId: string, + logLevel?: TypesGen.ProvisionerLogLevel, + ) => { + return this.postWorkspaceBuild(workspaceId, { + transition: "stop", + log_level: logLevel, + }); + }; + + deleteWorkspace = (workspaceId: string, options?: DeleteWorkspaceOptions) => { + return this.postWorkspaceBuild(workspaceId, { + transition: "delete", + ...options, + }); + }; + + cancelWorkspaceBuild = async ( + workspaceBuildId: TypesGen.WorkspaceBuild["id"], + params?: TypesGen.CancelWorkspaceBuildParams, + ): Promise => { + const response = await this.axios.patch( + `/api/v2/workspacebuilds/${workspaceBuildId}/cancel`, + null, + { params }, + ); + + return response.data; + }; + + updateWorkspaceDormancy = async ( + workspaceId: string, + dormant: boolean, + ): Promise => { + const data: TypesGen.UpdateWorkspaceDormancy = { dormant }; + const response = await this.axios.put( + `/api/v2/workspaces/${workspaceId}/dormant`, + data, + ); + + return response.data; + }; + + updateWorkspaceAutomaticUpdates = async ( + workspaceId: string, + automaticUpdates: TypesGen.AutomaticUpdates, + ): Promise => { + const req: TypesGen.UpdateWorkspaceAutomaticUpdatesRequest = { + automatic_updates: automaticUpdates, + }; + + const response = await this.axios.put( + `/api/v2/workspaces/${workspaceId}/autoupdates`, + req, + ); + + return response.data; + }; + + restartWorkspace = async ({ + workspace, + buildParameters, + }: RestartWorkspaceParameters): Promise => { + const stopBuild = await this.stopWorkspace(workspace.id); + const awaitedStopBuild = await this.waitForBuild(stopBuild); + + // If the restart is canceled halfway through, make sure we bail + if (awaitedStopBuild?.status === "canceled") { + return; + } + + const startBuild = await this.startWorkspace( + workspace.id, + workspace.latest_build.template_version_id, + undefined, + buildParameters, + ); + + await this.waitForBuild(startBuild); + }; + + /** + * Starts a workspace, but if the last build was a failed start, + * stops it first to give it a clean slate and the best chance + * of success. + */ + retryWorkspace = async ( + workspace: TypesGen.Workspace, + templateVersionId: string, + logLevel?: TypesGen.ProvisionerLogLevel, + buildParameters?: TypesGen.WorkspaceBuildParameter[], + ): Promise => { + if ( + workspace.latest_build.status === "failed" && + workspace.latest_build.transition === "start" + ) { + const stopBuild = await this.stopWorkspace(workspace.id, logLevel); + const awaitedStop = await this.waitForBuild(stopBuild); + if (awaitedStop?.status === "canceled") { + throw new Error("Cleanup stop was canceled"); + } + } + return this.startWorkspace( + workspace.id, + templateVersionId, + logLevel, + buildParameters, + ); + }; + + cancelTemplateVersionBuild = async ( + templateVersionId: string, + ): Promise => { + const response = await this.axios.patch( + `/api/v2/templateversions/${templateVersionId}/cancel`, + ); + + return response.data; + }; + + cancelTemplateVersionDryRun = async ( + templateVersionId: string, + jobId: string, + ): Promise => { + const response = await this.axios.patch( + `/api/v2/templateversions/${templateVersionId}/dry-run/${jobId}/cancel`, + ); + + return response.data; + }; + + createUser = async ( + user: TypesGen.CreateUserRequestWithOrgs, + ): Promise => { + const response = await this.axios.post( + "/api/v2/users", + user, + ); + + return response.data; + }; + + createWorkspace = async ( + userId: string, + workspace: TypesGen.CreateWorkspaceRequest, + ): Promise => { + const response = await this.axios.post( + `/api/v2/users/${userId}/workspaces`, + workspace, + ); + + return response.data; + }; + + patchWorkspace = async ( + workspaceId: string, + data: TypesGen.UpdateWorkspaceRequest, + ): Promise => { + await this.axios.patch(`/api/v2/workspaces/${workspaceId}`, data); + }; + + getBuildInfo = async (): Promise => { + const response = await this.axios.get("/api/v2/buildinfo"); + return response.data; + }; + + getUpdateCheck = async (): Promise => { + const response = await this.axios.get("/api/v2/updatecheck"); + return response.data; + }; + + putWorkspaceAutostart = async ( + workspaceID: string, + autostart: TypesGen.UpdateWorkspaceAutostartRequest, + ): Promise => { + const payload = JSON.stringify(autostart); + await this.axios.put( + `/api/v2/workspaces/${workspaceID}/autostart`, + payload, + { headers: { ...BASE_CONTENT_TYPE_JSON } }, + ); + }; + + putWorkspaceAutostop = async ( + workspaceID: string, + ttl: TypesGen.UpdateWorkspaceTTLRequest, + ): Promise => { + const payload = JSON.stringify(ttl); + await this.axios.put(`/api/v2/workspaces/${workspaceID}/ttl`, payload, { + headers: { ...BASE_CONTENT_TYPE_JSON }, + }); + }; + + updateProfile = async ( + userId: string, + data: TypesGen.UpdateUserProfileRequest, + ): Promise => { + const response = await this.axios.put( + `/api/v2/users/${userId}/profile`, + data, + ); + return response.data; + }; + + getAppearanceSettings = + async (): Promise => { + const response = await this.axios.get("/api/v2/users/me/appearance"); + return response.data; + }; + + updateAppearanceSettings = async ( + data: TypesGen.UpdateUserAppearanceSettingsRequest, + ): Promise => { + const response = await this.axios.put("/api/v2/users/me/appearance", data); + return response.data; + }; + + getUserPreferenceSettings = + async (): Promise => { + const response = await this.axios.get("/api/v2/users/me/preferences"); + return response.data; + }; + + updateUserPreferenceSettings = async ( + req: TypesGen.UpdateUserPreferenceSettingsRequest, + ): Promise => { + const response = await this.axios.put("/api/v2/users/me/preferences", req); + return response.data; + }; + + getUserQuietHoursSchedule = async ( + userId: TypesGen.User["id"], + ): Promise => { + const response = await this.axios.get( + `/api/v2/users/${userId}/quiet-hours`, + ); + return response.data; + }; + + updateUserQuietHoursSchedule = async ( + userId: TypesGen.User["id"], + data: TypesGen.UpdateUserQuietHoursScheduleRequest, + ): Promise => { + const response = await this.axios.put( + `/api/v2/users/${userId}/quiet-hours`, + data, + ); + + return response.data; + }; + + getUserAIBudgetOverride = async ( + userId: TypesGen.User["id"], + ): Promise => { + const response = await this.axios.get( + `/api/v2/users/${encodeURIComponent(userId)}/ai/budget/override`, + ); + + return response.data; + }; + + upsertUserAIBudgetOverride = async ( + userId: TypesGen.User["id"], + data: TypesGen.UpsertUserAIBudgetOverrideRequest, + ): Promise => { + const response = await this.axios.put( + `/api/v2/users/${encodeURIComponent(userId)}/ai/budget/override`, + data, + ); + + return response.data; + }; + + deleteUserAIBudgetOverride = async ( + userId: TypesGen.User["id"], + ): Promise => { + await this.axios.delete( + `/api/v2/users/${encodeURIComponent(userId)}/ai/budget/override`, + ); + }; + + activateUser = async ( + userId: TypesGen.User["id"], + ): Promise => { + const response = await this.axios.put( + `/api/v2/users/${userId}/status/activate`, + ); + return response.data; + }; + + suspendUser = async (userId: TypesGen.User["id"]): Promise => { + const response = await this.axios.put( + `/api/v2/users/${userId}/status/suspend`, + ); + + return response.data; + }; + + deleteUser = async (userId: TypesGen.User["id"]): Promise => { + await this.axios.delete(`/api/v2/users/${userId}`); + }; + + // API definition: + // https://github.com/coder/coder/blob/db665e7261f3c24a272ccec48233a3e276878239/coderd/users.go#L33-L53 + hasFirstUser = async (): Promise => { + try { + // If it is success, it is true + await this.axios.get("/api/v2/users/first"); + return true; + } catch (error) { + // If it returns a 404, it is false + if (isAxiosError(error) && error.response?.status === 404) { + return false; + } + + throw error; + } + }; + + createFirstUser = async ( + req: TypesGen.CreateFirstUserRequest, + ): Promise => { + const response = await this.axios.post("/api/v2/users/first", req); + return response.data; + }; + + updateUserPassword = async ( + userId: TypesGen.User["id"], + updatePassword: TypesGen.UpdateUserPasswordRequest, + ): Promise => { + await this.axios.put(`/api/v2/users/${userId}/password`, updatePassword); + }; + + validateUserPassword = async ( + password: string, + ): Promise => { + const response = await this.axios.post("/api/v2/users/validate-password", { + password, + }); + return response.data; + }; + + getRoles = async (): Promise> => { + const response = await this.axios.get( + "/api/v2/users/roles", + ); + + return response.data; + }; + + updateUserRoles = async ( + roles: TypesGen.SlimRole["name"][], + userId: TypesGen.User["id"], + ): Promise => { + const response = await this.axios.put( + `/api/v2/users/${userId}/roles`, + { roles }, + ); + + return response.data; + }; + + getUserSSHKey = async (userId = "me"): Promise => { + const response = await this.axios.get( + `/api/v2/users/${userId}/gitsshkey`, + ); + + return response.data; + }; + + regenerateUserSSHKey = async (userId = "me"): Promise => { + const response = await this.axios.put( + `/api/v2/users/${userId}/gitsshkey`, + ); + + return response.data; + }; + + getUserSecrets = async (userId: string): Promise => { + const response = await this.axios.get( + `/api/v2/users/${encodeURIComponent(userId)}/secrets`, + ); + + return response.data; + }; + + getUserSecret = async ( + userId: string, + name: string, + ): Promise => { + const response = await this.axios.get( + `/api/v2/users/${encodeURIComponent(userId)}/secrets/${encodeURIComponent(name)}`, + ); + + return response.data; + }; + + createUserSecret = async ( + userId: string, + request: TypesGen.CreateUserSecretRequest, + ): Promise => { + const response = await this.axios.post( + `/api/v2/users/${encodeURIComponent(userId)}/secrets`, + request, + ); + + return response.data; + }; + + updateUserSecret = async ( + userId: string, + name: string, + request: TypesGen.UpdateUserSecretRequest, + ): Promise => { + const response = await this.axios.patch( + `/api/v2/users/${encodeURIComponent(userId)}/secrets/${encodeURIComponent(name)}`, + request, + ); + + return response.data; + }; + + deleteUserSecret = async (userId: string, name: string): Promise => { + await this.axios.delete( + `/api/v2/users/${encodeURIComponent(userId)}/secrets/${encodeURIComponent(name)}`, + ); + }; + + importUserSecrets = async ( + userId: string, + request: TypesGen.ImportUserSecretsRequest, + ): Promise => { + const response = await this.axios.post( + `/api/v2/users/${encodeURIComponent(userId)}/secrets/batch`, + request, + ); + + return response.data; + }; + + getWorkspaceBuilds = async ( + workspaceId: string, + req?: TypesGen.WorkspaceBuildsRequest, + ) => { + const response = await this.axios.get( + getURLWithSearchParams(`/api/v2/workspaces/${workspaceId}/builds`, req), + ); + + return response.data; + }; + + getWorkspaceBuildLogs = async ( + buildId: string, + ): Promise => { + const response = await this.axios.get( + `/api/v2/workspacebuilds/${buildId}/logs`, + ); + + return response.data; + }; + + getWorkspaceAgentLogs = async ( + agentID: string, + ): Promise => { + const response = await this.axios.get( + `/api/v2/workspaceagents/${agentID}/logs`, + ); + + return response.data; + }; + + putWorkspaceExtension = async ( + workspaceId: string, + newDeadline: dayjs.Dayjs, + ): Promise => { + await this.axios.put(`/api/v2/workspaces/${workspaceId}/extend`, { + deadline: newDeadline, + }); + }; + + refreshEntitlements = async (): Promise => { + await this.axios.post("/api/v2/licenses/refresh-entitlements"); + }; + + getEntitlements = async (): Promise => { + try { + const response = await this.axios.get( + "/api/v2/entitlements", + ); + + return response.data; + } catch (ex) { + if (isAxiosError(ex) && ex.response?.status === 404) { + return { + errors: [], + features: withDefaultFeatures({}), + has_license: false, + require_telemetry: false, + trial: false, + warnings: [], + refreshed_at: "", + }; + } + throw ex; + } + }; + + getExperiments = async (): Promise => { + try { + const response = await this.axios.get( + "/api/v2/experiments", + ); + + return response.data; + } catch (error) { + if (isAxiosError(error) && error.response?.status === 404) { + return []; + } + + throw error; + } + }; + + getAvailableExperiments = + async (): Promise => { + try { + const response = await this.axios.get("/api/v2/experiments/available"); + + return response.data; + } catch (error) { + if (isAxiosError(error) && error.response?.status === 404) { + return { safe: [] }; + } + throw error; + } + }; + + getExternalAuthProvider = async ( + provider: string, + ): Promise => { + const res = await this.axios.get(`/api/v2/external-auth/${provider}`); + return res.data; + }; + + getExternalAuthDevice = async ( + provider: string, + ): Promise => { + const resp = await this.axios.get( + `/api/v2/external-auth/${provider}/device`, + ); + return resp.data; + }; + + exchangeExternalAuthDevice = async ( + provider: string, + req: TypesGen.ExternalAuthDeviceExchange, + ): Promise => { + const resp = await this.axios.post( + `/api/v2/external-auth/${provider}/device`, + req, + ); + + return resp.data; + }; + + getUserExternalAuthProviders = + async (): Promise => { + const resp = await this.axios.get("/api/v2/external-auth"); + return resp.data; + }; + + unlinkExternalAuthProvider = async ( + provider: string, + ): Promise => { + const resp = await this.axios.delete(`/api/v2/external-auth/${provider}`); + return resp.data; + }; + + getOAuth2GitHubDeviceFlowCallback = async ( + code: string, + state: string, + ): Promise => { + const resp = await this.axios.get( + `/api/v2/users/oauth2/github/callback?code=${code}&state=${state}`, + ); + // sanity check + if ( + typeof resp.data !== "object" || + typeof resp.data.redirect_url !== "string" + ) { + console.error("Invalid response from OAuth2 GitHub callback", resp); + throw new Error("Invalid response from OAuth2 GitHub callback"); + } + return resp.data; + }; + + getOAuth2GitHubDevice = async (): Promise => { + const resp = await this.axios.get("/api/v2/users/oauth2/github/device"); + return resp.data; + }; + + getOAuth2ProviderApps = async ( + filter?: TypesGen.OAuth2ProviderAppFilter, + ): Promise => { + const params = filter?.user_id + ? new URLSearchParams({ user_id: filter.user_id }).toString() + : ""; + + const resp = await this.axios.get(`/api/v2/oauth2-provider/apps?${params}`); + return resp.data; + }; + + getOAuth2ProviderApp = async ( + id: string, + ): Promise => { + const resp = await this.axios.get(`/api/v2/oauth2-provider/apps/${id}`); + return resp.data; + }; + + postOAuth2ProviderApp = async ( + data: TypesGen.PostOAuth2ProviderAppRequest, + ): Promise => { + const response = await this.axios.post( + "/api/v2/oauth2-provider/apps", + data, + ); + return response.data; + }; + + putOAuth2ProviderApp = async ( + id: string, + data: TypesGen.PutOAuth2ProviderAppRequest, + ): Promise => { + const response = await this.axios.put( + `/api/v2/oauth2-provider/apps/${id}`, + data, + ); + return response.data; + }; + + deleteOAuth2ProviderApp = async (id: string): Promise => { + await this.axios.delete(`/api/v2/oauth2-provider/apps/${id}`); + }; + + getOAuth2ProviderAppSecrets = async ( + id: string, + ): Promise => { + const resp = await this.axios.get( + `/api/v2/oauth2-provider/apps/${id}/secrets`, + ); + return resp.data; + }; + + postOAuth2ProviderAppSecret = async ( + id: string, + ): Promise => { + const resp = await this.axios.post( + `/api/v2/oauth2-provider/apps/${id}/secrets`, + ); + return resp.data; + }; + + deleteOAuth2ProviderAppSecret = async ( + appId: string, + secretId: string, + ): Promise => { + await this.axios.delete( + `/api/v2/oauth2-provider/apps/${appId}/secrets/${secretId}`, + ); + }; + + revokeOAuth2ProviderApp = async (appId: string): Promise => { + await this.axios.delete(`/oauth2/tokens?client_id=${appId}`); + }; + + getOAuth2ProviderSettings = + async (): Promise => { + const resp = await this.axios.get( + "/api/v2/oauth2-provider/settings", + ); + return resp.data; + }; + + putOAuth2ProviderSettings = async ( + data: TypesGen.OAuth2ProviderSettings, + ): Promise => { + const resp = await this.axios.put( + "/api/v2/oauth2-provider/settings", + data, + ); + return resp.data; + }; + + getAuditLogs = async ( + options: TypesGen.AuditLogsRequest, + ): Promise => { + const url = getURLWithSearchParams("/api/v2/audit", options); + const response = await this.axios.get(url); + return response.data; + }; + + getConnectionLogs = async ( + options: TypesGen.ConnectionLogsRequest, + ): Promise => { + const url = getURLWithSearchParams("/api/v2/connectionlog", options); + const response = await this.axios.get(url); + return response.data; + }; + + getTemplateDAUs = async ( + templateId: string, + ): Promise => { + const response = await this.axios.get( + `/api/v2/templates/${templateId}/daus`, + ); + + return response.data; + }; + + getDeploymentDAUs = async ( + // Default to user's local timezone. + // As /api/v2/insights/daus only accepts whole-number values for tz_offset + // we truncate the tz offset down to the closest hour. + offset = Math.trunc(new Date().getTimezoneOffset() / 60), + ): Promise => { + const response = await this.axios.get( + `/api/v2/insights/daus?tz_offset=${offset}`, + ); + + return response.data; + }; + + getTemplateACLAvailable = async ( + templateId: string, + options: TypesGen.UsersRequest, + ): Promise => { + const url = getURLWithSearchParams( + `/api/v2/templates/${templateId}/acl/available`, + options, + ).toString(); + + const response = await this.axios.get(url); + return response.data; + }; + + getTemplateACL = async ( + templateId: string, + ): Promise => { + const response = await this.axios.get( + `/api/v2/templates/${templateId}/acl`, + ); + + return response.data; + }; + + updateTemplateACL = async ( + templateId: string, + data: TypesGen.UpdateTemplateACL, + ): Promise<{ message: string }> => { + const response = await this.axios.patch( + `/api/v2/templates/${templateId}/acl`, + data, + ); + + return response.data; + }; + + getWorkspaceACL = async ( + workspaceId: string, + ): Promise => { + const response = await this.axios.get( + `/api/v2/workspaces/${workspaceId}/acl`, + ); + + return response.data; + }; + + updateWorkspaceACL = async ( + workspaceId: string, + data: TypesGen.UpdateWorkspaceACL, + ): Promise => { + await this.axios.patch(`/api/v2/workspaces/${workspaceId}/acl`, data); + }; + + getApplicationsHost = async (): Promise => { + const response = await this.axios.get("/api/v2/applications/host"); + return response.data; + }; + + getGroups = async ( + options: { userId?: string; organization?: string } = {}, + ): Promise => { + const params: Record = {}; + if (options.userId !== undefined) { + params.has_member = options.userId; + } + if (options.organization !== undefined) { + params.organization = options.organization; + } + + const response = await this.axios.get("/api/v2/groups", { params }); + return response.data; + }; + + /** + * @param organization Can be the organization's ID or name + */ + getGroupsByOrganization = async ( + organization: string, + ): Promise => { + const response = await this.axios.get( + `/api/v2/organizations/${organization}/groups`, + ); + return response.data; + }; + + /** + * AI spend for the given groups in the active budget period. Fetched in + * batches of 100 (the backend cap) and merged. Requires at least one ID; + * the period window comes from the backend, so an empty request has no + * meaningful response. + * @param organization Can be the organization's ID or name + */ + getOrganizationGroupsAISpend = async ( + organization: string, + groupIds: readonly string[], + ): Promise => { + if (groupIds.length === 0) { + throw new Error("groupIds must not be empty"); + } + const responses = await fetchInBatches( + groupIds, + aiSpendBatchSize, + async (ids) => { + const url = getURLWithSearchParams( + `/api/v2/organizations/${organization}/groups/ai/spend`, + { group_ids: ids.join(",") }, + ); + const response = + await this.axios.get(url); + return response.data; + }, + ); + // Every batch reports the same active period window. + return { + ...responses[0], + groups: responses.flatMap((r) => r.groups), + }; + }; + + /** + * Per-member AI spend attributed to a group in the active budget period. + * Users not in the group, or whose spend the caller can't read, are + * omitted. Fetched in batches of 100 (the backend cap) and merged. + * Requires at least one ID. + */ + getGroupMembersAISpend = async ( + groupId: string, + userIds: readonly string[], + ): Promise => { + if (userIds.length === 0) { + throw new Error("userIds must not be empty"); + } + const responses = await fetchInBatches( + userIds, + aiSpendBatchSize, + async (ids) => { + const url = getURLWithSearchParams( + `/api/v2/groups/${groupId}/members/ai/spend`, + { user_ids: ids.join(",") }, + ); + const response = + await this.axios.get(url); + return response.data; + }, + ); + return { + ...responses[0], + members: responses.flatMap((r) => r.members), + }; + }; + + /** + * @param organization Can be the organization's ID or name + * @param options Pagination and search options + */ + getOrganizationPaginatedGroups = async ( + organization: string, + options?: TypesGen.PaginatedGroupsRequest, + ): Promise => { + const url = getURLWithSearchParams( + `/api/v2/organizations/${organization}/paginated-groups`, + options, + ); + const response = await this.axios.get(url); + return response.data; + }; + + /** + * @param organization Can be the organization's ID or name + */ + createGroup = async ( + organization: string, + data: TypesGen.CreateGroupRequest, + ): Promise => { + const response = await this.axios.post( + `/api/v2/organizations/${organization}/groups`, + data, + ); + return response.data; + }; + + getGroupById = async ( + groupId: string, + req: TypesGen.GroupRequest, + signal?: AbortSignal, + ): Promise => { + const url = getURLWithSearchParams(`/api/v2/groups/${groupId}`, req); + const response = await this.axios.get(url, { signal }); + return response.data; + }; + + /** + * @param organization Can be the organization's ID or name + */ + getGroup = async ( + organization: string, + groupName: string, + req: TypesGen.GroupRequest, + signal?: AbortSignal, + ): Promise => { + const url = getURLWithSearchParams( + `/api/v2/organizations/${organization}/groups/${groupName}`, + req, + ); + const response = await this.axios.get(url, { signal }); + return response.data; + }; + + getGroupMembers = async ( + organization: string, + groupName: string, + filter?: UsersRequest, + signal?: AbortSignal, + ): Promise => { + const url = getURLWithSearchParams( + `/api/v2/organizations/${organization}/groups/${groupName}/members`, + filter, + ); + const response = await this.axios.get( + url.toString(), + { signal }, + ); + return response.data; + }; + + patchGroup = async ( + groupId: string, + data: TypesGen.PatchGroupRequest, + ): Promise => { + const response = await this.axios.patch(`/api/v2/groups/${groupId}`, data); + return response.data; + }; + + addMembers = async (groupId: string, userIds: string[]) => { + return this.patchGroup(groupId, { + name: "", + add_users: userIds, + remove_users: [], + display_name: null, + avatar_url: null, + quota_allowance: null, + }); + }; + + addMember = async (groupId: string, userId: string) => { + return this.patchGroup(groupId, { + name: "", + add_users: [userId], + remove_users: [], + display_name: null, + avatar_url: null, + quota_allowance: null, + }); + }; + + removeMember = async (groupId: string, userId: string) => { + return this.patchGroup(groupId, { + name: "", + add_users: [], + remove_users: [userId], + display_name: null, + avatar_url: null, + quota_allowance: null, + }); + }; + + deleteGroup = async (groupId: string): Promise => { + await this.axios.delete(`/api/v2/groups/${groupId}`); + }; + + getGroupAIBudget = async ( + groupId: string, + ): Promise => { + const response = await this.axios.get( + `/api/v2/groups/${groupId}/ai/budget`, + ); + return response.data; + }; + + upsertGroupAIBudget = async ( + groupId: string, + data: TypesGen.UpsertGroupAIBudgetRequest, + ): Promise => { + const response = await this.axios.put( + `/api/v2/groups/${groupId}/ai/budget`, + data, + ); + return response.data; + }; + + deleteGroupAIBudget = async (groupId: string): Promise => { + await this.axios.delete(`/api/v2/groups/${groupId}/ai/budget`); + }; + + getWorkspaceQuota = async ( + organizationName: string, + username: string, + ): Promise => { + const response = await this.axios.get( + `/api/v2/organizations/${encodeURIComponent(organizationName)}/members/${encodeURIComponent(username)}/workspace-quota`, + ); + + return response.data; + }; + + getAgentListeningPorts = async ( + agentID: string, + ): Promise => { + const response = await this.axios.get( + `/api/v2/workspaceagents/${agentID}/listening-ports`, + ); + return response.data; + }; + + getWorkspaceAgentSharedPorts = async ( + workspaceID: string, + ): Promise => { + const response = await this.axios.get( + `/api/v2/workspaces/${workspaceID}/port-share`, + ); + return response.data; + }; + + getWorkspaceAgentCredentials = async ( + workspaceID: string, + agentName: string, + ): Promise => { + const response = await this.axios.get( + `/api/v2/workspaces/${workspaceID}/external-agent/${agentName}/credentials`, + ); + return response.data; + }; + + upsertWorkspaceAgentSharedPort = async ( + workspaceID: string, + req: TypesGen.UpsertWorkspaceAgentPortShareRequest, + ): Promise => { + const response = await this.axios.post( + `/api/v2/workspaces/${workspaceID}/port-share`, + req, + ); + return response.data; + }; + + deleteWorkspaceAgentSharedPort = async ( + workspaceID: string, + req: TypesGen.DeleteWorkspaceAgentPortShareRequest, + ): Promise => { + const response = await this.axios.delete( + `/api/v2/workspaces/${workspaceID}/port-share`, + { data: req }, + ); + + return response.data; + }; + + // getDeploymentSSHConfig is used by the VSCode-Extension. + getDeploymentSSHConfig = async (): Promise => { + const response = await this.axios.get("/api/v2/deployment/ssh"); + return response.data; + }; + + getDeploymentConfig = async (): Promise => { + const response = await this.axios.get("/api/v2/deployment/config"); + return response.data; + }; + + getDeploymentStats = async (): Promise => { + const response = await this.axios.get("/api/v2/deployment/stats"); + return response.data; + }; + + reportPremiumFunnelEvent = async ( + req: TypesGen.PremiumFunnelEventRequest, + ): Promise => { + await this.axios.post("/api/v2/deployment/premium-funnel-events", req); + }; + + getReplicas = async (): Promise => { + const response = await this.axios.get("/api/v2/replicas"); + return response.data; + }; + + getFile = async (fileId: string): Promise => { + const response = await this.axios.get( + `/api/v2/files/${fileId}`, + { responseType: "arraybuffer" }, + ); + + return response.data; + }; + + getWorkspaceProxyRegions = async (): Promise< + TypesGen.RegionsResponse + > => { + const response = + await this.axios.get>( + "/api/v2/regions", + ); + + return response.data; + }; + + getWorkspaceProxies = async (): Promise< + TypesGen.RegionsResponse + > => { + const response = await this.axios.get< + TypesGen.RegionsResponse + >("/api/v2/workspaceproxies"); + + return response.data; + }; + + createWorkspaceProxy = async ( + b: TypesGen.CreateWorkspaceProxyRequest, + ): Promise => { + const response = await this.axios.post("/api/v2/workspaceproxies", b); + return response.data; + }; + + getAppearance = async (): Promise => { + try { + const response = await this.axios.get("/api/v2/appearance"); + return response.data || {}; + } catch (ex) { + if (isAxiosError(ex) && ex.response?.status === 404) { + return { + application_name: "", + docs_url: "", + logo_url: "", + announcement_banners: [], + codernauts_enabled: true, + service_banner: { + enabled: false, + }, + }; + } + + throw ex; + } + }; + + updateAppearance = async ( + b: TypesGen.AppearanceConfig, + ): Promise => { + const response = await this.axios.put("/api/v2/appearance", b); + return response.data; + }; + + /** + * @param organization Can be the organization's ID or name + */ + getTemplateExamples = async (): Promise => { + const response = await this.axios.get("/api/v2/templates/examples"); + + return response.data; + }; + + getTemplateBuilderBases = + async (): Promise => { + const response = await this.axios.get("/api/v2/templatebuilder/bases"); + return response.data; + }; + + getTemplateBuilderModules = async ( + base?: string, + ): Promise => { + const params = base ? `?base=${encodeURIComponent(base)}` : ""; + const response = await this.axios.get( + `/api/v2/templatebuilder/modules${params}`, + ); + return response.data; + }; + + createTemplateFromBuilder = async ( + req: TypesGen.TemplateBuilderCreateTemplateRequest, + ): Promise => { + const response = await this.axios.post( + "/api/v2/templatebuilder/compose/template", + req, + ); + return response.data; + }; + + recordTemplateBuilderSession = async ( + req: TypesGen.TemplateBuilderSessionRequest, + ): Promise => { + await this.axios.post("/api/v2/templatebuilder/sessions", req); + }; + + uploadFile = async (file: File): Promise => { + const response = await this.axios.post("/api/v2/files", file, { + headers: { "Content-Type": file.type }, + }); + + return response.data; + }; + + getTemplateVersionLogs = async ( + versionId: string, + ): Promise => { + const response = await this.axios.get( + `/api/v2/templateversions/${versionId}/logs`, + ); + return response.data; + }; + + updateWorkspaceVersion = async ( + workspace: TypesGen.Workspace, + ): Promise => { + const template = await this.getTemplate(workspace.template_id); + return this.startWorkspace(workspace.id, template.active_version_id); + }; + + getWorkspaceBuildParameters = async ( + workspaceBuildId: TypesGen.WorkspaceBuild["id"], + ): Promise => { + const response = await this.axios.get( + `/api/v2/workspacebuilds/${workspaceBuildId}/parameters`, + ); + + return response.data; + }; + + getLicenses = async (): Promise => { + const response = await this.axios.get("/api/v2/licenses"); + return response.data; + }; + + createLicense = async ( + data: TypesGen.AddLicenseRequest, + ): Promise => { + const response = await this.axios.post("/api/v2/licenses", data); + return response.data; + }; + + createTrialLicense = async ( + data: TypesGen.CreateTrialLicenseRequest, + ): Promise => { + const response = await this.axios.post("/api/v2/licenses/trial", data); + return response.data; + }; + + removeLicense = async (licenseId: number): Promise => { + await this.axios.delete(`/api/v2/licenses/${licenseId}`); + }; + + getDynamicParameters = async ( + templateVersionId: string, + ownerId: string, + oldBuildParameters: TypesGen.WorkspaceBuildParameter[], + ) => { + const request: DynamicParametersRequest = { + id: 1, + owner_id: ownerId, + inputs: Object.fromEntries( + new Map(oldBuildParameters.map((param) => [param.name, param.value])), + ), + }; + + const dynamicParametersResponse = + await this.getTemplateVersionDynamicParameters( + templateVersionId, + request, + ); + + return dynamicParametersResponse.parameters.map((p) => ({ + ...p, + description_plaintext: p.description || "", + default_value: p.default_value?.valid ? p.default_value.value : "", + options: p.options + ? p.options.map((opt) => ({ + ...opt, + value: opt.value?.valid ? opt.value.value : "", + })) + : [], + })); + }; + + /** + * Stops a workspace if it is currently running and waits for the stop + * to complete. Throws if the stop build is canceled. + */ + private stopWorkspaceIfRunning = async ( + workspace: TypesGen.Workspace, + ): Promise => { + // Workspace is already in a state where it's "stopped". + if (workspace.latest_build.status !== "running") return; + + const stopBuild = await this.stopWorkspace(workspace.id); + const awaitedStopBuild = await this.waitForBuild(stopBuild); + + if (awaitedStopBuild?.status === "canceled") { + throw new Error("Workspace stop was canceled."); + } + }; + + changeWorkspaceVersion = async ( + workspace: TypesGen.Workspace, + templateVersionId: string, + newBuildParameters: TypesGen.WorkspaceBuildParameter[] = [], + ): Promise => { + await this.stopWorkspaceIfRunning(workspace); + + try { + return await this.postWorkspaceBuild(workspace.id, { + transition: "start", + template_version_id: templateVersionId, + rich_parameter_values: newBuildParameters, + }); + } catch (error) { + // If the build failed because of a parameter validation error, then we + // throw a special sentinel error that can be caught by the caller. + if ( + isApiError(error) && + error.response.status === 400 && + error.response.data.validations && + error.response.data.validations.length > 0 + ) { + throw new ParameterValidationError( + templateVersionId, + error.response.data.validations, + ); + } + throw error; + } + }; + + updateWorkspace = async ( + workspace: TypesGen.Workspace, + newBuildParameters: TypesGen.WorkspaceBuildParameter[] = [], + ): Promise => { + const template = await this.getTemplate(workspace.template_id); + const activeVersionId = template.active_version_id; + + return this.changeWorkspaceVersion( + workspace, + activeVersionId, + newBuildParameters, + ); + }; + + getWorkspaceResolveAutostart = async ( + workspaceId: string, + ): Promise => { + const response = await this.axios.get( + `/api/v2/workspaces/${workspaceId}/resolve-autostart`, + ); + return response.data; + }; + + issueReconnectingPTYSignedToken = async ( + params: TypesGen.IssueReconnectingPTYSignedTokenRequest, + ): Promise => { + const response = await this.axios.post( + "/api/v2/applications/reconnecting-pty-signed-token", + params, + ); + + return response.data; + }; + + getInsightsUserLatency = async ( + filters: InsightsParams, + ): Promise => { + const params = new URLSearchParams(filters); + const response = await this.axios.get( + `/api/v2/insights/user-latency?${params}`, + ); + + return response.data; + }; + + getInsightsUserActivity = async ( + filters: InsightsParams, + ): Promise => { + const params = new URLSearchParams(filters); + const response = await this.axios.get( + `/api/v2/insights/user-activity?${params}`, + ); + + return response.data; + }; + + // Intl.DateTimeFormat().resolvedOptions().timeZone returns an IANA timezone + // name (e.g. "America/New_York") per ECMA-402. Go's time.LoadLocation and + // PostgreSQL's timezone() both accept IANA names, so these are compatible. + getInsightsUserStatusCounts = async ( + timezone = Intl.DateTimeFormat().resolvedOptions().timeZone, + ): Promise => { + const searchParams = new URLSearchParams({ + timezone, + }); + const response = await this.axios.get( + `/api/v2/insights/user-status-counts?${searchParams}`, + ); + + return response.data; + }; + + getInsightsTemplate = async ( + params: InsightsTemplateParams, + ): Promise => { + const searchParams = new URLSearchParams(params); + const response = await this.axios.get( + `/api/v2/insights/templates?${searchParams}`, + ); + + return response.data; + }; + + getHealth = async (force = false) => { + const params = new URLSearchParams({ force: force.toString() }); + const response = await this.axios.get( + `/api/v2/debug/health?${params}`, + ); + return response.data; + }; + + getHealthSettings = async (): Promise => { + const res = await this.axios.get( + "/api/v2/debug/health/settings", + ); + + return res.data; + }; + + updateHealthSettings = async (data: TypesGen.UpdateHealthSettings) => { + const response = await this.axios.put( + "/api/v2/debug/health/settings", + data, + ); + + return response.data; + }; + + putFavoriteWorkspace = async (workspaceID: string) => { + await this.axios.put(`/api/v2/workspaces/${workspaceID}/favorite`); + }; + + deleteFavoriteWorkspace = async (workspaceID: string) => { + await this.axios.delete(`/api/v2/workspaces/${workspaceID}/favorite`); + }; + + postWorkspaceUsage = async ( + workspaceID: string, + options: PostWorkspaceUsageRequest, + ) => { + const response = await this.axios.post( + `/api/v2/workspaces/${workspaceID}/usage`, + options, + ); + + return response.data; + }; + + getUserNotificationPreferences = async (userId: string) => { + const res = await this.axios.get( + `/api/v2/users/${userId}/notifications/preferences`, + ); + return res.data ?? []; + }; + + putUserNotificationPreferences = async ( + userId: string, + req: TypesGen.UpdateUserNotificationPreferences, + ) => { + const res = await this.axios.put( + `/api/v2/users/${userId}/notifications/preferences`, + req, + ); + return res.data; + }; + + getSystemNotificationTemplates = async () => { + const res = await this.axios.get( + "/api/v2/notifications/templates/system", + ); + return res.data; + }; + + getCustomNotificationTemplates = async () => { + const res = await this.axios.get( + "/api/v2/notifications/templates/custom", + ); + return res.data; + }; + + getNotificationDispatchMethods = async () => { + const res = await this.axios.get( + "/api/v2/notifications/dispatch-methods", + ); + return res.data; + }; + + updateNotificationTemplateMethod = async ( + templateId: string, + req: TypesGen.UpdateNotificationTemplateMethod, + ) => { + const res = await this.axios.put( + `/api/v2/notifications/templates/${templateId}/method`, + req, + ); + return res.data; + }; + + postTestNotification = async () => { + await this.axios.post("/api/v2/notifications/test"); + }; + + createWebPushSubscription = async ( + userId: string, + req: TypesGen.WebpushSubscription, + ) => { + await this.axios.post( + `/api/v2/users/${userId}/webpush/subscription`, + req, + ); + }; + + deleteWebPushSubscription = async ( + userId: string, + req: TypesGen.DeleteWebpushSubscription, + ) => { + await this.axios.delete( + `/api/v2/users/${userId}/webpush/subscription`, + { + data: req, + }, + ); + }; + + requestOneTimePassword = async ( + req: TypesGen.RequestOneTimePasscodeRequest, + ) => { + await this.axios.post("/api/v2/users/otp/request", req); + }; + + changePasswordWithOTP = async ( + req: TypesGen.ChangePasswordWithOneTimePasscodeRequest, + ) => { + await this.axios.post("/api/v2/users/otp/change-password", req); + }; + + workspaceBuildTimings = async (workspaceBuildId: string) => { + const res = await this.axios.get( + `/api/v2/workspacebuilds/${workspaceBuildId}/timings`, + ); + return res.data; + }; + + getProvisionerJobs = async ( + orgId: string, + params: GetProvisionerJobsParams = {}, + ) => { + const res = await this.axios.get( + `/api/v2/organizations/${orgId}/provisionerjobs`, + { params }, + ); + return res.data; + }; + + cancelProvisionerJob = async (job: TypesGen.ProvisionerJob) => { + switch (job.type) { + case "workspace_build": + if (!job.input.workspace_build_id) { + throw new Error("Workspace build ID is required to cancel this job"); + } + return this.cancelWorkspaceBuild(job.input.workspace_build_id); + + case "template_version_import": + if (!job.input.template_version_id) { + throw new Error("Template version ID is required to cancel this job"); + } + return this.cancelTemplateVersionBuild(job.input.template_version_id); + + case "template_version_dry_run": + if (!job.input.template_version_id) { + throw new Error("Template version ID is required to cancel this job"); + } + return this.cancelTemplateVersionDryRun( + job.input.template_version_id, + job.id, + ); + } + }; + + deleteDevContainer = async ({ + parentAgentId, + devcontainerId, + }: { + parentAgentId: string; + devcontainerId: string; + }) => { + await this.axios.delete( + `/api/v2/workspaceagents/${parentAgentId}/containers/devcontainers/${devcontainerId}`, + ); + }; + + recreateDevContainer = async ({ + parentAgentId, + devcontainerId, + }: { + parentAgentId: string; + devcontainerId: string; + }) => { + const response = await this.axios.post( + `/api/v2/workspaceagents/${parentAgentId}/containers/devcontainers/${devcontainerId}/recreate`, + ); + return response.data; + }; + + getAgentContainers = async (agentId: string, labels?: string[]) => { + const params = new URLSearchParams( + labels?.map((label) => ["label", label]), + ); + const res = + await this.axios.get( + `/api/v2/workspaceagents/${agentId}/containers?${params.toString()}`, + ); + return res.data; + }; + + getInboxNotifications = async (startingBeforeId?: string) => { + const params = new URLSearchParams(); + if (startingBeforeId) { + params.append("starting_before", startingBeforeId); + } + const res = await this.axios.get( + `/api/v2/notifications/inbox?${params.toString()}`, + ); + return res.data; + }; + + updateInboxNotificationReadStatus = async ( + notificationId: string, + req: TypesGen.UpdateInboxNotificationReadStatusRequest, + ) => { + const res = + await this.axios.put( + `/api/v2/notifications/inbox/${notificationId}/read-status`, + req, + ); + return res.data; + }; + + markAllInboxNotificationsAsRead = async () => { + await this.axios.put("/api/v2/notifications/inbox/mark-all-as-read"); + }; + + createTask = async ( + user: string, + req: TypesGen.CreateTaskRequest, + ): Promise => { + const response = await this.axios.post( + `/api/v2/tasks/${user}`, + req, + ); + + return response.data; + }; + + getTasks = async ( + filter: TypesGen.TasksFilter, + ): Promise => { + const query: string[] = []; + if (filter.owner) { + query.push(`owner:${filter.owner}`); + } + if (filter.status) { + query.push(`status:${filter.status}`); + } + + const res = await this.axios.get( + "/api/v2/tasks", + { + params: { + q: query.join(", "), + }, + }, + ); + + return res.data.tasks; + }; + + getTask = async (user: string, id: string): Promise => { + const response = await this.axios.get( + `/api/v2/tasks/${user}/${id}`, + ); + + return response.data; + }; + + deleteTask = async (user: string, id: string): Promise => { + await this.axios.delete(`/api/v2/tasks/${user}/${id}`); + }; + + updateTaskInput = async ( + user: string, + id: string, + input: string, + ): Promise => { + await this.axios.patch(`/api/v2/tasks/${user}/${id}/input`, { + input, + } satisfies TypesGen.UpdateTaskInputRequest); + }; + + getTaskLogs = async ( + user: string, + id: string, + ): Promise => { + const response = await this.axios.get( + `/api/v2/tasks/${user}/${id}/logs`, + ); + return response.data; + }; + + pauseTask = async ( + user: string, + id: string, + ): Promise => { + const response = await this.axios.post( + `/api/v2/tasks/${user}/${id}/pause`, + ); + return response.data; + }; + + resumeTask = async ( + user: string, + id: string, + ): Promise => { + const response = await this.axios.post( + `/api/v2/tasks/${user}/${id}/resume`, + ); + return response.data; + }; + + sendTaskInput = async ( + user: string, + id: string, + input: string, + ): Promise => { + await this.axios.post(`/api/v2/tasks/${user}/${id}/send`, { + input, + } satisfies TypesGen.TaskSendRequest); + }; + + createTaskFeedback = async ( + _taskId: string, + _req: CreateTaskFeedbackRequest, + ) => { + return new Promise((res) => { + setTimeout(() => res(), 500); + }); + }; + + getAIBridgeModels = async (options: SearchParamOptions) => { + const url = getURLWithSearchParams(`${aiGatewayPath}/models`, options); + + const response = await this.axios.get(url); + return response.data; + }; + + getAIBridgeClients = async (options: SearchParamOptions) => { + const url = getURLWithSearchParams(`${aiGatewayPath}/clients`, options); + + const response = await this.axios.get(url); + return response.data; + }; + + getAIBridgeSessionList = async (options: SearchParamOptions) => { + const url = getURLWithSearchParams(`${aiGatewayPath}/sessions`, options); + const response = + await this.axios.get(url); + return response.data; + }; + + getAIBridgeSessionThreads = async ( + sessionId: string, + options?: { after_id?: string; before_id?: string; limit?: number }, + ) => { + const url = getURLWithSearchParams( + `${aiGatewayPath}/sessions/${sessionId}`, + options, + ); + const response = + await this.axios.get(url); + return response.data; + }; + + getAIProviders = async (): Promise => { + const response = await this.axios.get( + "/api/v2/ai/providers", + ); + return response.data; + }; + + getAIProvider = async (idOrName: string): Promise => { + const response = await this.axios.get( + `/api/v2/ai/providers/${encodeURIComponent(idOrName)}`, + ); + return response.data; + }; + + createAIProvider = async ( + req: TypesGen.CreateAIProviderRequest, + ): Promise => { + const response = await this.axios.post( + "/api/v2/ai/providers", + req, + ); + return response.data; + }; + + updateAIProvider = async ( + idOrName: string, + req: TypesGen.UpdateAIProviderRequest, + ): Promise => { + const response = await this.axios.patch( + `/api/v2/ai/providers/${encodeURIComponent(idOrName)}`, + req, + ); + return response.data; + }; + + deleteAIProvider = async (idOrName: string): Promise => { + await this.axios.delete( + `/api/v2/ai/providers/${encodeURIComponent(idOrName)}`, + ); + }; + + getAIGatewayKeys = async (): Promise => { + const response = await this.axios.get( + `${aiGatewayPath}/keys`, + ); + return response.data; + }; + + createAIGatewayKey = async ( + req: TypesGen.CreateAIGatewayKeyRequest, + ): Promise => { + const response = await this.axios.post( + `${aiGatewayPath}/keys`, + req, + ); + return response.data; + }; + + deleteAIGatewayKey = async (id: string): Promise => { + await this.axios.delete(`${aiGatewayPath}/keys/${encodeURIComponent(id)}`); + }; +} + +export type TaskFeedbackRating = "good" | "okay" | "bad"; + +export type CreateTaskFeedbackRequest = { + rate: TaskFeedbackRating; + comment?: string; +}; + +export type ChatPlanModeOrClear = TypesGen.ChatPlanMode | ""; + +export type CreateChatMessageRequestWithClearablePlanMode = Omit< + TypesGen.CreateChatMessageRequest, + "plan_mode" +> & { + readonly plan_mode?: ChatPlanModeOrClear; +}; + +type UpdateChatRequestWithClearablePlanMode = Omit< + TypesGen.UpdateChatRequest, + "plan_mode" +> & { + readonly plan_mode?: ChatPlanModeOrClear; +}; + +// These API methods span stable and experimental endpoints. Routes that remain +// experimental are not stable and may change or be removed at any time. +// +// All methods must be defined with arrow function syntax. See the docstring +// above the ApiMethods class for a full explanation. +class ExperimentalApiMethods { + constructor(protected readonly axios: AxiosInstance) {} + + getChatsByWorkspace = async ( + workspaceIds: readonly string[], + ): Promise> => { + const res = await this.axios.get("/api/v2/chats/by-workspace", { + params: { workspace_ids: workspaceIds.join(",") }, + }); + return res.data; + }; + + uploadChatFile = async ( + file: File, + organizationId: string, + ): Promise => { + const response = await this.axios.post( + `/api/v2/chats/files?organization=${organizationId}`, + file, + { + headers: { + "Content-Type": file.type || "application/octet-stream", + // Use RFC 5987 encoding for the filename to support + // non-ASCII characters. Placing the raw name directly in + // the header causes XMLHttpRequest to throw because HTTP + // headers only allow ISO-8859-1 code points. + "Content-Disposition": `attachment; filename="file"; filename*=UTF-8''${encodeURIComponent(file.name)}`, + }, + }, + ); + return response.data; + }; + + getChatFileText = async (fileId: string): Promise => { + const response = await this.axios.get(`/api/v2/chats/files/${fileId}`, { + responseType: "text", + }); + return response.data as string; + }; + + // Chat API methods + getChatACL = async (chatId: string): Promise => { + const response = await this.axios.get( + `/api/v2/chats/${chatId}/acl`, + ); + return response.data; + }; + + updateChatACL = async ( + chatId: string, + req: TypesGen.UpdateChatACL, + ): Promise => { + await this.axios.patch(`/api/v2/chats/${chatId}/acl`, req); + }; + + getChats = async (req?: { + after_id?: string; + limit?: number; + offset?: number; + q?: string; + }): Promise => { + const response = await this.axios.get( + getURLWithSearchParams("/api/v2/chats", req), + ); + return response.data; + }; + getChat = async (chatId: string): Promise => { + const response = await this.axios.get( + `/api/v2/chats/${chatId}`, + ); + return response.data; + }; + getChatCost = async (chatId: string): Promise => { + const response = await this.axios.get( + `/api/v2/chats/${chatId}/cost`, + ); + return response.data; + }; + getChatMessages = async ( + chatId: string, + opts?: { before_id?: number; after_id?: number; limit?: number }, + ): Promise => { + const params = new URLSearchParams(); + if (opts?.before_id) { + params.set("before_id", opts.before_id.toString()); + } + if (opts?.after_id) { + params.set("after_id", opts.after_id.toString()); + } + if (opts?.limit) { + params.set("limit", opts.limit.toString()); + } + const query = params.toString(); + const url = `/api/v2/chats/${chatId}/messages${query ? `?${query}` : ""}`; + const response = await this.axios.get(url); + return response.data; + }; + + /** + * Lists the user-authored prompts in a chat, newest first. + * Powers the composer's up/down arrow prompt-history cycle. + */ + getChatPrompts = async ( + chatId: string, + opts?: { limit?: number }, + ): Promise => { + const url = getURLWithSearchParams(`/api/v2/chats/${chatId}/prompts`, opts); + const response = await this.axios.get(url); + return response.data; + }; + + createChat = async ( + req: TypesGen.CreateChatRequest, + ): Promise => { + const response = await this.axios.post("/api/v2/chats", req); + return response.data; + }; + + updateChat = async ( + chatId: string, + req: UpdateChatRequestWithClearablePlanMode, + ): Promise => { + await this.axios.patch(`/api/v2/chats/${chatId}`, req); + }; + + proposeChatTitle = async (chatId: string): Promise<{ title: string }> => { + const response = await this.axios.post<{ title: string }>( + `/api/v2/chats/${chatId}/title/propose`, + ); + return response.data; + }; + + createChatMessage = async ( + chatId: string, + req: CreateChatMessageRequestWithClearablePlanMode, + ): Promise => { + const response = await this.axios.post( + `/api/v2/chats/${chatId}/messages`, + req, + ); + return response.data; + }; + + editChatMessage = async ( + chatId: string, + messageId: number, + req: TypesGen.EditChatMessageRequest, + ): Promise => { + const response = await this.axios.patch( + `/api/v2/chats/${chatId}/messages/${messageId}`, + req, + ); + return response.data; + }; + interruptChat = async (chatId: string): Promise => { + const response = await this.axios.post( + `/api/v2/chats/${chatId}/interrupt`, + ); + return response.data; + }; + + /** + * Requests a manual context compaction on an idle or errored chat, + * clearing any stored error. The compaction runs asynchronously + * through the chat worker and bypasses the automatic usage threshold. + */ + compactChat = async (chatId: string): Promise => { + const response = await this.axios.post( + `/api/v2/chats/${chatId}/compact`, + ); + return response.data; + }; + + /** + * Re-pins the chat to its agent's latest context snapshot and clears + * the dirty marker. Returns the updated chat. + */ + refreshChatContext = async (chatId: string): Promise => { + const response = await this.axios.put( + `/api/v2/chats/${chatId}/context`, + ); + return response.data; + }; + + deleteChatQueuedMessage = async ( + chatId: string, + queuedMessageId: number, + ): Promise => { + await this.axios.delete(`/api/v2/chats/${chatId}/queue/${queuedMessageId}`); + }; + + promoteChatQueuedMessage = async ( + chatId: string, + queuedMessageId: number, + ): Promise => { + await this.axios.post( + `/api/v2/chats/${chatId}/queue/${queuedMessageId}/promote`, + ); + }; + + getChatDiffContents = async ( + chatId: string, + ): Promise => { + const response = await this.axios.get( + `/api/v2/chats/${chatId}/diff`, + ); + return response.data; + }; + + getAIModelPrices = async (filter: { + provider?: string; + model?: string; + }): Promise => { + const response = await this.axios.get( + "/api/experimental/ai/model-prices", + { params: filter }, + ); + return response.data; + }; + + listAIProviders = async (): Promise => { + const response = await this.axios.get( + aiProviderConfigsPath, + ); + return response.data; + }; + + createAIProvider = async ( + req: TypesGen.CreateAIProviderRequest, + ): Promise => { + const response = await this.axios.post( + aiProviderConfigsPath, + req, + ); + return response.data; + }; + + updateAIProvider = async ( + providerId: string, + req: TypesGen.UpdateAIProviderRequest, + ): Promise => { + const response = await this.axios.patch( + `${aiProviderConfigsPath}/${providerId}`, + req, + ); + return response.data; + }; + + deleteAIProvider = async (providerId: string): Promise => { + await this.axios.delete(`${aiProviderConfigsPath}/${providerId}`); + }; + + getUserAIProviderKeyConfigs = async ( + user = "me", + ): Promise => { + const response = await this.axios.get( + userAIProviderKeysPath(user), + ); + return response.data; + }; + + upsertUserAIProviderKey = async ( + providerId: string, + req: TypesGen.CreateUserAIProviderKeyRequest, + user = "me", + ): Promise => { + const response = await this.axios.put( + `${userAIProviderKeysPath(user)}/${providerId}`, + req, + ); + return response.data; + }; + + deleteUserAIProviderKey = async ( + providerId: string, + user = "me", + ): Promise => { + await this.axios.delete(`${userAIProviderKeysPath(user)}/${providerId}`); + }; + + getChatSystemPrompt = + async (): Promise => { + const response = await this.axios.get( + "/api/v2/chats/config/system-prompt", + ); + return response.data; + }; + + updateChatSystemPrompt = async ( + req: TypesGen.UpdateChatSystemPromptRequest, + ): Promise => { + await this.axios.put("/api/v2/chats/config/system-prompt", req); + }; + + getChatPlanModeInstructions = + async (): Promise => { + const response = + await this.axios.get( + "/api/v2/chats/config/plan-mode-instructions", + ); + return response.data; + }; + + updateChatPlanModeInstructions = async ( + req: TypesGen.UpdateChatPlanModeInstructionsRequest, + ): Promise => { + await this.axios.put("/api/v2/chats/config/plan-mode-instructions", req); + }; + + getOrganizationChatModelOverrides = async ( + organizationId: string, + ): Promise => { + const response = await this.axios.get( + `/api/v2/organizations/${encodeURIComponent(organizationId)}/chats/model-overrides`, + ); + return response.data; + }; + + updateOrganizationChatModelOverride = async ( + organizationId: string, + context: TypesGen.ChatModelOverrideContext, + req: TypesGen.UpdateChatModelOverrideRequest, + ): Promise => { + const response = await this.axios.put( + `/api/v2/organizations/${encodeURIComponent(organizationId)}/chats/model-overrides/${encodeURIComponent(context)}`, + req, + ); + return response.data; + }; + + getChatPersonalModelOverridesAdminSettings = + async (): Promise => { + const response = + await this.axios.get( + "/api/v2/chats/config/personal-model-overrides", + ); + return response.data; + }; + + updateChatPersonalModelOverridesAdminSettings = async ( + req: TypesGen.UpdateChatPersonalModelOverridesAdminSettingsRequest, + ): Promise => { + await this.axios.put("/api/v2/chats/config/personal-model-overrides", req); + }; + + getChatDebugLogging = + async (): Promise => { + const response = + await this.axios.get( + "/api/v2/chats/config/debug-logging", + ); + return response.data; + }; + + updateChatDebugLogging = async ( + req: TypesGen.UpdateChatDebugLoggingAllowUsersRequest, + ): Promise => { + await this.axios.put("/api/v2/chats/config/debug-logging", req); + }; + + getUserChatDebugLogging = + async (): Promise => { + const response = + await this.axios.get( + "/api/v2/chats/config/user-debug-logging", + ); + return response.data; + }; + + updateUserChatDebugLogging = async ( + req: TypesGen.UpdateUserChatDebugLoggingRequest, + ): Promise => { + await this.axios.put("/api/v2/chats/config/user-debug-logging", req); + }; + + getUserChatPersonalModelOverrides = async ( + organizationId: string, + user: string, + ): Promise => { + const response = + await this.axios.get( + `/api/v2/organizations/${encodeURIComponent(organizationId)}/members/${encodeURIComponent(user)}/chats/model-overrides`, + ); + return response.data; + }; + + updateUserChatPersonalModelOverride = async ( + organizationId: string, + user: string, + context: TypesGen.ChatPersonalModelOverrideContext, + req: TypesGen.UpdateUserChatPersonalModelOverrideRequest, + ): Promise => { + await this.axios.put( + `/api/v2/organizations/${encodeURIComponent(organizationId)}/members/${encodeURIComponent(user)}/chats/model-overrides/${encodeURIComponent(context)}`, + req, + ); + }; + + getChatDebugRuns = async ( + chatId: string, + ): Promise => { + const response = await this.axios.get( + `/api/experimental/chats/${chatId}/debug/runs`, + ); + return response.data; + }; + + getChatDebugRun = async ( + chatId: string, + runId: string, + ): Promise => { + const response = await this.axios.get( + `/api/experimental/chats/${chatId}/debug/runs/${runId}`, + ); + return response.data; + }; + + getChatAdvisorConfig = async (): Promise => { + const response = await this.axios.get( + "/api/experimental/chats/config/advisor", + ); + return response.data; + }; + + updateChatAdvisorConfig = async ( + req: UpdateAdvisorConfigRequest, + ): Promise => { + await this.axios.put("/api/experimental/chats/config/advisor", req); + }; + + getChatComputerUseProvider = + async (): Promise => { + const response = + await this.axios.get( + "/api/experimental/chats/config/computer-use-provider", + ); + return response.data; + }; + + updateChatComputerUseProvider = async ( + req: TypesGen.UpdateChatComputerUseProviderRequest, + ): Promise => { + await this.axios.put( + "/api/experimental/chats/config/computer-use-provider", + req, + ); + }; + + getChatWorkspaceTTL = + async (): Promise => { + const response = await this.axios.get( + "/api/v2/chats/config/workspace-ttl", + ); + return response.data; + }; + + updateChatWorkspaceTTL = async ( + req: TypesGen.UpdateChatWorkspaceTTLRequest, + ): Promise => { + await this.axios.put("/api/v2/chats/config/workspace-ttl", req); + }; + + getChatRetentionDays = + async (): Promise => { + const response = await this.axios.get( + "/api/v2/chats/config/retention-days", + ); + return response.data; + }; + + updateChatRetentionDays = async ( + req: TypesGen.UpdateChatRetentionDaysRequest, + ): Promise => { + await this.axios.put("/api/v2/chats/config/retention-days", req); + }; + + getChatDebugRetentionDays = + async (): Promise => { + const response = + await this.axios.get( + "/api/v2/chats/config/debug-retention-days", + ); + return response.data; + }; + + updateChatDebugRetentionDays = async ( + req: TypesGen.UpdateChatDebugRetentionDaysRequest, + ): Promise => { + await this.axios.put("/api/v2/chats/config/debug-retention-days", req); + }; + + getChatAutoArchiveDays = + async (): Promise => { + const response = + await this.axios.get( + "/api/v2/chats/config/auto-archive-days", + ); + return response.data; + }; + + updateChatAutoArchiveDays = async ( + req: TypesGen.UpdateChatAutoArchiveDaysRequest, + ): Promise => { + await this.axios.put("/api/v2/chats/config/auto-archive-days", req); + }; + + getUserChatCustomPrompt = + async (): Promise => { + const response = await this.axios.get( + "/api/v2/chats/config/user-prompt", + ); + return response.data; + }; + updateUserChatCustomPrompt = async ( + req: TypesGen.UserChatCustomPrompt, + ): Promise => { + const response = await this.axios.put( + "/api/v2/chats/config/user-prompt", + req, + ); + return response.data; + }; + + createUserSkill = async ( + user: string, + req: TypesGen.CreateUserSkillRequest, + ): Promise => { + const response = await this.axios.post( + userSkillsPath(user), + req, + ); + return response.data; + }; + + getUserSkills = async ( + user: string, + ): Promise => { + const response = await this.axios.get( + userSkillsPath(user), + ); + return response.data; + }; + + getUserSkillByName = async ( + user: string, + name: string, + ): Promise => { + const response = await this.axios.get( + userSkillPath(user, name), + ); + return response.data; + }; + + updateUserSkill = async ( + user: string, + name: string, + req: TypesGen.UpdateUserSkillRequest, + ): Promise => { + const response = await this.axios.patch( + userSkillPath(user, name), + req, + ); + return response.data; + }; + + deleteUserSkill = async (user: string, name: string): Promise => { + await this.axios.delete(userSkillPath(user, name)); + }; + + getUserChatCompactionThresholds = + async (): Promise => { + const response = + await this.axios.get( + "/api/v2/chats/config/user-compaction-thresholds", + ); + return response.data; + }; + updateUserChatCompactionThreshold = async ( + modelId: string, + req: TypesGen.UpdateUserChatCompactionThresholdRequest, + ): Promise => { + const response = await this.axios.put( + `/api/v2/chats/config/user-compaction-thresholds/${encodeURIComponent(modelId)}`, + req, + ); + return response.data; + }; + deleteUserChatCompactionThreshold = async ( + modelId: string, + ): Promise => { + await this.axios.delete( + `/api/v2/chats/config/user-compaction-thresholds/${encodeURIComponent(modelId)}`, + ); + }; + + getChatModels = async ( + organizationId: string, + ): Promise => { + const response = + await this.axios.get( + chatModelsPath(organizationId), + ); + if (!Array.isArray(response.data?.models)) { + throw new Error("Invalid chat models response: models must be an array."); + } + return response.data; + }; + + getChatModel = async ( + organizationId: string, + modelId: string, + ): Promise => { + const response = await this.axios.get( + chatModelPath(organizationId, modelId), + ); + return response.data; + }; + + createChatModel = async ( + organizationId: string, + req: TypesGen.CreateChatModelRequest, + ): Promise => { + const response = await this.axios.post( + chatModelsPath(organizationId), + req, + ); + return response.data; + }; + + updateChatModel = async ( + organizationId: string, + modelId: string, + req: TypesGen.UpdateChatModelRequest, + ): Promise => { + const response = await this.axios.patch( + chatModelPath(organizationId, modelId), + req, + ); + return response.data; + }; + + getChatModelACL = async ( + organizationId: string, + modelId: string, + ): Promise => { + const response = await this.axios.get( + chatModelACLPath(organizationId, modelId), + ); + return response.data; + }; + + getChatModelACLAvailable = async ( + organizationId: string, + modelId: string, + options: TypesGen.UsersRequest, + ): Promise => { + const url = getURLWithSearchParams( + `${chatModelACLPath(organizationId, modelId)}/available`, + options, + ); + const response = await this.axios.get(url); + return response.data; + }; + + updateChatModelACL = async ( + organizationId: string, + modelId: string, + req: TypesGen.UpdateChatModelACLRequest, + ): Promise => { + await this.axios.patch(chatModelACLPath(organizationId, modelId), req); + }; + + deleteChatModel = async ( + organizationId: string, + modelId: string, + ): Promise => { + await this.axios.delete(chatModelPath(organizationId, modelId)); + }; + + getMCPServerConfigs = async ( + organization: string, + ): Promise => { + const response = await this.axios.get( + mcpServerConfigsPath(organization), + ); + return response.data; + }; + + getMCPServerConfig = async ( + organization: string, + id: string, + ): Promise => { + const response = await this.axios.get( + mcpServerConfigPath(organization, id), + ); + return response.data; + }; + + getMCPServerConfigACL = async ( + organization: string, + id: string, + ): Promise => { + const response = await this.axios.get( + mcpServerConfigACLPath(organization, id), + ); + return response.data; + }; + + getMCPServerConfigACLAvailable = async ( + organization: string, + id: string, + options: TypesGen.UsersRequest, + ): Promise => { + const response = await this.axios.get( + getURLWithSearchParams( + mcpServerConfigACLAvailablePath(organization, id), + options, + ), + ); + return response.data; + }; + + updateMCPServerConfigACL = async ( + organization: string, + id: string, + req: TypesGen.UpdateMCPServerConfigACLRequest, + ): Promise => { + await this.axios.patch(mcpServerConfigACLPath(organization, id), req); + }; + + createMCPServerConfig = async ( + organization: string, + req: TypesGen.CreateMCPServerConfigRequest, + ): Promise => { + const response = await this.axios.post( + mcpServerConfigsPath(organization), + req, + ); + return response.data; + }; + + updateMCPServerConfig = async ( + organization: string, + id: string, + req: TypesGen.UpdateMCPServerConfigRequest, + ): Promise => { + const response = await this.axios.patch( + mcpServerConfigPath(organization, id), + req, + ); + return response.data; + }; + + deleteMCPServerConfig = async ( + organization: string, + id: string, + ): Promise => { + await this.axios.delete(mcpServerConfigPath(organization, id)); + }; + + disconnectMCPServerOAuth2 = async ( + id: string, + ): Promise => { + const response = + await this.axios.delete( + mcpServerOAuth2DisconnectPath(id), + ); + return response.data; + }; +} + +// This is a hard coded CSRF token/cookie pair for local development. In prod, +// the GoLang webserver generates a random cookie with a new token for each +// document request. For local development, we don't use the Go webserver for +// static files, so this is the 'hack' to make local development work with +// remote apis. The CSRF cookie for this token is "JXm9hOUdZctWt0ZZGAy9xiS/gxMKYOThdxjjMnMUyn4=" +const csrfToken = + "KNKvagCBEHZK7ihe2t7fj6VeJ0UyTDco1yVUJE8N06oNqxLu5Zx1vRxZbgfC0mJJgeGkVjgs08mgPbcWPBkZ1A=="; + +// Always attach CSRF token to all requests. In puppeteer the document is +// undefined. In those cases, just do nothing. +const tokenMetadataElement = + typeof document !== "undefined" + ? document.head.querySelector('meta[property="csrf-token"]') + : null; + +function getConfiguredAxiosInstance(): AxiosInstance { + const instance = globalAxios.create(); + + // Adds 304 for the default axios validateStatus function + // https://github.com/axios/axios#handling-errors Check status here + // https://httpstatusdogs.com/ + instance.defaults.validateStatus = (status) => { + return (status >= 200 && status < 300) || status === 304; + }; + + const metadataIsAvailable = + tokenMetadataElement !== null && + tokenMetadataElement.getAttribute("content") !== null; + + if (metadataIsAvailable) { + if (process.env.NODE_ENV === "development") { + // Development mode uses a hard-coded CSRF token + instance.defaults.headers.common["X-CSRF-TOKEN"] = csrfToken; + tokenMetadataElement.setAttribute("content", csrfToken); + } else { + instance.defaults.headers.common["X-CSRF-TOKEN"] = + tokenMetadataElement.getAttribute("content") ?? ""; + } + } else { + // Do not write error logs if we are in a FE unit test or if there is no document (e.g., Electron) + if ( + typeof document !== "undefined" && + !process.env.JEST_WORKER_ID && + !process.env.VITEST + ) { + console.error("CSRF token not found"); + } + } + + return instance; +} + +/** + * Utility function to help create a WebSocket connection with Coder's API. + */ +function createWebSocket( + path: string, + params: URLSearchParams = new URLSearchParams(), +) { + // When running in an embedded context (e.g. VS Code webview), + // the session token is set via the API header but browsers + // cannot attach custom headers to WebSocket connections. + // Pass it as a query parameter instead. + const token = API.getSessionToken(); + if (token) { + params.set(SessionTokenCookie, token); + } + const protocol = location.protocol === "https:" ? "wss:" : "ws:"; + const socket = new WebSocket( + `${protocol}//${location.host}${path}?${params}`, + ); + socket.binaryType = "blob"; + return socket; +} + +// Other non-API methods defined here to make it a little easier to find them. +interface ClientApi extends ApiMethods { + getCsrfToken: () => string; + setSessionToken: (token: string) => void; + getSessionToken: () => string | undefined; + setHost: (host: string | undefined) => void; + getAxiosInstance: () => AxiosInstance; +} + +/** @public Exported for use by external consumers (e.g., VS Code extension). */ +export class Api extends ApiMethods implements ClientApi { + constructor() { + const scopedAxiosInstance = getConfiguredAxiosInstance(); + super(scopedAxiosInstance); + } + + // As with ApiMethods, all public methods should be defined with arrow + // function syntax to ensure they can be passed around the React UI without + // losing/detaching their `this` context! + + getCsrfToken = (): string => { + return csrfToken; + }; + + setSessionToken = (token: string): void => { + this.axios.defaults.headers.common["Coder-Session-Token"] = token; + }; + + getSessionToken = (): string | undefined => { + return this.axios.defaults.headers.common["Coder-Session-Token"] as + | string + | undefined; + }; + + setHost = (host: string | undefined): void => { + this.axios.defaults.baseURL = host; + }; + + getAxiosInstance = (): AxiosInstance => { + return this.axios; + }; +} + +export const API = new Api(); diff --git a/vendor/coder/site/src/api/errors.ts b/vendor/coder/site/src/api/errors.ts new file mode 100644 index 0000000000..69b41d3492 --- /dev/null +++ b/vendor/coder/site/src/api/errors.ts @@ -0,0 +1,154 @@ +import { type AxiosError, type AxiosResponse, isAxiosError } from "axios"; + +export interface FieldError { + field: string; + detail: string; +} + +type FieldErrors = Record; + +export interface ApiErrorResponse { + message: string; + detail?: string; + validations?: FieldError[]; +} + +export type ApiError = AxiosError & { + response: AxiosResponse; +}; + +export const isApiError = (err: unknown): err is ApiError => { + return ( + isAxiosError(err) && + err.response !== undefined && + isApiErrorResponse(err.response.data) + ); +}; + +/** @public Exported for use by external consumers (e.g., VS Code extension). */ +export const isApiErrorResponse = (err: unknown): err is ApiErrorResponse => { + return ( + typeof err === "object" && + err !== null && + "message" in err && + typeof err.message === "string" && + (!("detail" in err) || + err.detail === undefined || + typeof err.detail === "string") && + (!("validations" in err) || + err.validations === undefined || + Array.isArray(err.validations)) + ); +}; + +export const hasApiFieldErrors = (error: ApiError): boolean => + Array.isArray(error.response.data.validations); + +export const isApiValidationError = (error: unknown): error is ApiError => { + return isApiError(error) && hasApiFieldErrors(error); +}; + +export const hasError = (error: unknown) => + error !== undefined && error !== null; + +export const mapApiErrorToFieldErrors = ( + apiErrorResponse: ApiErrorResponse, +): FieldErrors => { + const result: FieldErrors = {}; + + if (apiErrorResponse.validations) { + for (const error of apiErrorResponse.validations) { + result[error.field] = error.detail || "Invalid value"; + } + } + + return result; +}; + +/** + * + * @param error + * @param defaultMessage + * @returns error's message if ApiError or Error, else defaultMessage + */ +export const getErrorMessage = ( + error: unknown, + defaultMessage: string, +): string => { + // if error is API error + // 404s result in the default message being returned + if (isApiError(error) && error.response.data.message) { + return error.response.data.message; + } + if (isApiErrorResponse(error)) { + return error.message; + } + // if error is a non-empty string + if (error && typeof error === "string") { + return error; + } + return defaultMessage; +}; + +/** + * + * @param error + * @returns a combined validation error message if the error is an ApiError + * and contains validation messages for different form fields. + */ +export const getValidationErrorMessage = (error: unknown): string => { + const validationErrors = + isApiError(error) && error.response.data.validations + ? error.response.data.validations + : []; + return validationErrors.map((error) => error.detail).join("\n"); +}; + +export const getErrorDetail = (error: unknown): string | undefined => { + if (error instanceof DetailedError) { + return error.detail; + } + + // APIErrors that are empty still benefit from checking the developer + // console if no detail is provided. So only use the detail field if + // it is not empty. + if (isApiError(error) && error.response.data.detail) { + return error.response.data.detail; + } + + if (isApiErrorResponse(error) && error.detail) { + return error.detail; + } + + if ( + isApiValidationError(error) && + // Ensure that the validations array is not `[]` (empty array). + Array.isArray(error.response.data.validations) && + error.response.data.validations.length > 0 + ) { + return getValidationErrorMessage(error); + } + + if (error instanceof Error) { + return "Please check the developer console for more details."; + } + + return undefined; +}; + +export const getErrorStatus = (error: unknown): number | undefined => { + if (isApiError(error)) { + return error.status; + } + + return undefined; +}; + +export class DetailedError extends Error { + constructor( + message: string, + public detail?: string, + ) { + super(message); + } +} diff --git a/vendor/coder/site/src/api/typesGenerated.ts b/vendor/coder/site/src/api/typesGenerated.ts new file mode 100644 index 0000000000..c12dc3b4bf --- /dev/null +++ b/vendor/coder/site/src/api/typesGenerated.ts @@ -0,0 +1,11840 @@ +// Code generated by 'guts'. DO NOT EDIT. + +// From codersdk/templates.go +/** + * ACLAvailable is a list of users and groups that can be added to a resource + * ACL. + */ +export interface ACLAvailable { + readonly users: readonly ReducedUser[]; + readonly groups: readonly Group[]; +} + +// From codersdk/aibridge.go +/** + * AIBridgeAgenticAction represents a tool call with associated + * thinking blocks and token usage from one or more interceptions. + */ +export interface AIBridgeAgenticAction { + readonly model: string; + readonly token_usage: AIBridgeSessionThreadsTokenUsage; + readonly thinking: readonly AIBridgeModelThought[]; + readonly tool_calls: readonly AIBridgeToolCall[]; +} + +// From codersdk/deployment.go +export interface AIBridgeAnthropicConfig { + readonly base_url: string; + readonly key: string; +} + +// From codersdk/deployment.go +export interface AIBridgeBedrockConfig { + readonly base_url: string; + readonly region: string; + readonly access_key: string; + readonly access_key_secret: string; + readonly model: string; + readonly small_fast_model: string; +} + +// From codersdk/deployment.go +export interface AIBridgeConfig { + readonly enabled: boolean; + /** + * @deprecated Use Providers with indexed `CODER_AI_GATEWAY_PROVIDER__*` env vars instead. + */ + readonly openai: AIBridgeOpenAIConfig; + /** + * @deprecated Use Providers with indexed `CODER_AI_GATEWAY_PROVIDER__*` env vars instead. + */ + readonly anthropic: AIBridgeAnthropicConfig; + /** + * @deprecated Use Providers with indexed `CODER_AI_GATEWAY_PROVIDER__*` env vars instead. + */ + readonly bedrock: AIBridgeBedrockConfig; + /** + * Providers holds provider instances populated from `CODER_AI_GATEWAY_PROVIDER__` + * env vars and/or the deprecated LegacyOpenAI/LegacyAnthropic/LegacyBedrock fields above. + */ + readonly providers?: readonly AIProviderConfig[]; + /** + * @deprecated Injected MCP in AI Bridge is deprecated and will be removed in a future release. + */ + readonly inject_coder_mcp_tools: boolean; + readonly retention: number; + readonly max_concurrency: number; + readonly rate_limit: number; + readonly structured_logging: boolean; + readonly send_actor_headers: boolean; + readonly allow_byok: boolean; + /** + * Budget settings for AI Governance cost controls. + */ + readonly budget_policy?: string; + readonly budget_period?: string; + /** + * Circuit breaker protects against cascading failures from upstream AI + * provider overload (503, 529). + */ + readonly circuit_breaker_enabled: boolean; + readonly circuit_breaker_failure_threshold: number; + readonly circuit_breaker_interval: number; + readonly circuit_breaker_timeout: number; + readonly circuit_breaker_max_requests: number; + /** + * APIDumpDir is the base directory under which each provider's + * request/response dumps are written, in a subdirectory named after + * the provider. Empty disables dumping. + */ + readonly api_dump_dir: string; +} + +// From codersdk/aibridge.go +export interface AIBridgeListSessionsResponse { + readonly count: number; + readonly sessions: readonly AIBridgeSession[]; +} + +// From codersdk/aibridge.go +/** + * AIBridgeModelThought represents a single thinking block from + * the model. + */ +export interface AIBridgeModelThought { + readonly text: string; +} + +// From codersdk/deployment.go +export interface AIBridgeOpenAIConfig { + readonly base_url: string; + readonly key: string; +} + +// From codersdk/deployment.go +export interface AIBridgeProxyConfig { + readonly enabled: boolean; + readonly listen_addr: string; + readonly target: string; + readonly tls_cert_file: string; + readonly tls_key_file: string; + readonly cert_file: string; + readonly key_file: string; + readonly domain_allowlist: string; + readonly upstream_proxy: string; + readonly upstream_proxy_ca: string; + readonly allowed_private_cidrs: string; + readonly api_dump_dir: string; +} + +// From codersdk/aibridge.go +export interface AIBridgeSession { + readonly id: string; + readonly initiator: MinimalUser; + readonly providers: readonly string[]; + readonly models: readonly string[]; + readonly client: string | null; + // empty interface{} type, falling back to unknown + readonly metadata: Record; + readonly started_at: string; + readonly ended_at?: string; + readonly threads: number; + readonly token_usage_summary: AIBridgeSessionTokenUsageSummary; + /** + * NetworkCalls summarizes the Agent Firewall network requests made during the + * session. A nil value means the session did not pass through Agent + * Firewall, so network call monitoring was not active, which the UI + * surfaces as "Disabled". + */ + readonly network_calls?: AIBridgeSessionNetworkCallSummary; + readonly last_prompt?: string; + readonly last_active_at: string; +} + +// From codersdk/aibridge.go +/** + * AIBridgeSessionNetworkCallSummary aggregates the Agent Firewall network + * calls made during a session. Blocked counts calls denied by the firewall + * allow-list. + */ +export interface AIBridgeSessionNetworkCallSummary { + readonly total: number; + readonly blocked: number; +} + +// From codersdk/aibridge.go +/** + * AIBridgeSessionNetworkDomain is one destination host contacted during a + * session, with the number of network calls made to it. + */ +export interface AIBridgeSessionNetworkDomain { + readonly domain: string; + readonly count: number; +} + +// From codersdk/aibridge.go +/** + * AIBridgeSessionThreadsResponse is the response for GET + * /api/v2/ai-gateway/sessions/{session_id} which returns a single + * session with fully expanded threads. + */ +export interface AIBridgeSessionThreadsResponse { + readonly id: string; + readonly initiator: MinimalUser; + readonly providers: readonly string[]; + readonly models: readonly string[]; + readonly client?: string; + // empty interface{} type, falling back to unknown + readonly metadata: Record; + readonly page_started_at?: string; + readonly page_ended_at?: string; + readonly started_at: string; + readonly ended_at?: string; + readonly token_usage_summary: AIBridgeSessionThreadsTokenUsage; + /** + * NetworkCalls summarizes the Agent Firewall network calls made during the + * session. A nil value means the session did not pass through Agent + * Firewall, so network call monitoring was not active, which the UI + * surfaces as "Disabled". + */ + readonly network_calls?: AIBridgeSessionNetworkCallSummary; + /** + * NetworkTopDomains lists the most contacted destination hosts, ordered by + * call count descending. NetworkDomainCount is the total number of distinct + * domains, used to render a "+N more" overflow beyond the listed domains. + */ + readonly network_top_domains?: readonly AIBridgeSessionNetworkDomain[]; + readonly network_domain_count?: number; + /** + * NetworkCallLogs is the chronological list of individual network calls made + * during the session, holding the earliest calls up to a server-side cap. + * NetworkCalls remains authoritative for whole-session totals, so a shorter + * list than NetworkCalls.Total means the list was truncated. Empty when the + * session did not pass through Agent Firewall. + */ + readonly network_call_logs?: readonly AgentFirewallLog[]; + readonly threads: readonly AIBridgeThread[]; +} + +// From codersdk/aibridge.go +/** + * AIBridgeSessionThreadsTokenUsage represents aggregated token usage + * with metadata containing provider-specific fields. + */ +export interface AIBridgeSessionThreadsTokenUsage { + readonly input_tokens: number; + readonly output_tokens: number; + readonly cache_read_input_tokens: number; + readonly cache_write_input_tokens: number; + // empty interface{} type, falling back to unknown + readonly metadata: Record; +} + +// From codersdk/aibridge.go +export interface AIBridgeSessionTokenUsageSummary { + readonly input_tokens: number; + readonly output_tokens: number; + readonly cache_read_input_tokens: number; + readonly cache_write_input_tokens: number; +} + +// From codersdk/aibridge.go +/** + * AIBridgeThread represents a single thread within a session. + * A thread groups interceptions by their thread_root_id. + */ +export interface AIBridgeThread { + readonly id: string; + readonly prompt?: string; + readonly model: string; + readonly provider: string; + readonly credential_kind: string; + readonly credential_hint: string; + readonly started_at: string; + readonly ended_at?: string; + readonly token_usage: AIBridgeSessionThreadsTokenUsage; + readonly agentic_actions: readonly AIBridgeAgenticAction[]; + /** + * ErrorType is the categorized terminal upstream error from the root + * interception, or nil when the interception succeeded. See the + * aibridge_interception_error_type enum for possible values. + */ + readonly error_type?: string; + /** + * ErrorMessage is the raw terminal upstream error message from the root + * interception. Nil when the interception succeeded. + */ + readonly error_message?: string; + /** + * AgentFirewallSessionID links this thread to an agent firewall + * confinement session. Nil when the request did not pass through + * the agent firewall. + */ + readonly agent_firewall_session_id?: string; + /** + * AgentFirewallSequenceNumber is the firewall sequence number from + * the root interception. Used to determine the position of this + * LLM request in the firewall event stream. Nil when the request + * did not pass through the agent firewall. + */ + readonly agent_firewall_sequence_number?: number; +} + +// From codersdk/aibridge.go +/** + * AIBridgeToolCall represents a tool call recorded during an + * interception. + */ +export interface AIBridgeToolCall { + readonly id: string; + readonly interception_id: string; + readonly provider_response_id: string; + readonly server_url: string; + readonly tool: string; + readonly injected: boolean; + readonly input: string; + // empty interface{} type, falling back to unknown + readonly metadata: Record; + readonly created_at: string; +} + +// From codersdk/aibridge.go +/** + * AIBudgetLimit is an AI spend limit and the tier that produced it. Both + * fields are always populated together. + */ +export interface AIBudgetLimit { + readonly spend_limit_micros: number; + readonly limit_source: AIBudgetLimitSource; +} + +// From codersdk/aibridge.go +export type AIBudgetLimitSource = "group" | "user_override"; + +export const AIBudgetLimitSources: AIBudgetLimitSource[] = [ + "group", + "user_override", +]; + +// From codersdk/deployment.go +export type AIBudgetPeriod = "month"; + +export const AIBudgetPeriods: AIBudgetPeriod[] = ["month"]; + +export const AIBudgetPolicies: AIBudgetPolicy[] = ["highest"]; + +// From codersdk/deployment.go +export type AIBudgetPolicy = "highest"; + +// From codersdk/deployment.go +export interface AIConfig { + readonly bridge?: AIBridgeConfig; + readonly aibridge_proxy?: AIBridgeProxyConfig; + readonly chat?: ChatConfig; +} + +// From codersdk/aigatewaykeys.go +/** + * AIGatewayKey is a shared secret used by a standalone AI Gateway + * to authenticate into coderd. + */ +export interface AIGatewayKey { + readonly id: string; + readonly name: string; + readonly key_prefix: string; + readonly created_at: string; + readonly last_heartbeat_at?: string; +} + +// From codersdk/client.go +/** + * AIGatewayKeyHeader contains the authentication key for a standalone AI Gateway replica. + */ +export const AIGatewayKeyHeader = "X-Coder-AI-Governance-Gateway-Key"; + +// From codersdk/aimodelprices.go +/** + * AIModelPrice is a per-model token price used by AI Gateway to compute the + * cost of an interception. + * + * Prices are integer micro-units per million tokens, so 1000000 is $1.00 per + * million tokens. A nil price means the price is not known, which the cost + * calculation treats the same as zero. Distinguish that from an explicit 0, + * which declares the model free of charge. + */ +export interface AIModelPrice { + readonly provider: string; + readonly model: string; + readonly input_price: number | null; + readonly output_price: number | null; + readonly cache_read_price: number | null; + readonly cache_write_price: number | null; + readonly source: AIModelPriceSource; + readonly created_at: string; + readonly updated_at: string; +} + +// From codersdk/aimodelprices.go +export type AIModelPriceSource = "custom" | "default"; + +export const AIModelPriceSources: AIModelPriceSource[] = ["custom", "default"]; + +// From codersdk/aimodelprices.go +/** + * AIModelPriceUpsert is one model's prices in an upsert request. It carries + * only the writable fields of AIModelPrice. + */ +export interface AIModelPriceUpsert { + readonly provider: string; + readonly model: string; + readonly input_price: number | null; + readonly output_price: number | null; + readonly cache_read_price: number | null; + readonly cache_write_price: number | null; +} + +// From codersdk/aiproviders.go +/** + * AIProvider represents an AI provider configuration row as returned + * by the API. Each APIKey entry carries the row's ID so callers can + * reference it in an UpdateAIProviderRequest; the plaintext value is + * never echoed back (see AIProviderKey.Masked). Secret fields on + * Settings are never included in responses. + */ +export interface AIProvider { + readonly id: string; + readonly type: AIProviderType; + readonly name: string; + readonly display_name: string; + readonly icon: string; + readonly enabled: boolean; + readonly base_url: string; + readonly api_keys: readonly AIProviderKey[]; + readonly settings: AIProviderSettings; + readonly created_at: string; + readonly updated_at: string; + /** + * Status carries runtime routing status; nil when empty. + */ + readonly status?: AIProviderStatus; +} + +// From codersdk/aiproviders_bedrock.go +export type AIProviderBedrockProtocol = "invoke-model" | "mantle"; + +export const AIProviderBedrockProtocols: AIProviderBedrockProtocol[] = [ + "invoke-model", + "mantle", +]; + +// From codersdk/aiproviders_bedrock.go +/** + * AIProviderBedrockSettings configures providers that authenticate + * against AWS Bedrock. AccessKey and AccessKeySecret are write-only: + * servers strip them from GET and list responses. Both secret fields + * use a pointer so a PATCH can distinguish "leave untouched" (omitted) + * from "explicitly clear" (empty string), e.g. when migrating to + * IAM role-based authentication. + */ +export interface AIProviderBedrockSettings { + /** + * Region is the AWS region used to construct the Bedrock endpoint + * URL when BaseURL is not set on the parent provider. + */ + readonly region?: string; + /** + * Model is the AWS Bedrock model identifier used for primary + * requests. + */ + readonly model?: string; + /** + * SmallFastModel is the AWS Bedrock model identifier used for + * background tasks (e.g. Claude Code's haiku-class model). + */ + readonly small_fast_model?: string; + /** + * AccessKey is the AWS access key ID used to authenticate against + * Bedrock. Write-only. + */ + readonly access_key?: string; + /** + * AccessKeySecret is the AWS secret access key paired with + * AccessKey. Write-only. + */ + readonly access_key_secret?: string; + /** + * RoleARN, when set, is the IAM role assumed via STS before calling + * Bedrock. The base identity (static keys or the AWS environment, e.g. + * IRSA / EKS Pod Identity / EC2 Instance Profile) signs the AssumeRole + * call, and the resulting temporary credentials sign Bedrock requests. + */ + readonly role_arn?: string; + /** + * ExternalID is the STS external ID sent on the AssumeRole call when + * RoleARN is set. The server generates and owns it: create and update + * reject any client-supplied value that differs from the stored one (an + * update may echo the stored value back). + */ + readonly external_id?: string; + /** + * Protocol selects the Bedrock wire protocol. An empty value resolves to + * AIProviderBedrockProtocolInvokeModel, so existing rows keep the legacy + * behavior. + */ + readonly protocol?: AIProviderBedrockProtocol; +} + +// From codersdk/aiproviders_bedrock.go +/** + * AIProviderBedrockSettingsVersion is the current schema version of + * AIProviderBedrockSettings. + */ +export const AIProviderBedrockSettingsVersion = 1; + +// From codersdk/deployment.go +/** + * AIProviderConfig represents a single AI provider instance, + * parsed from CODER_AI_GATEWAY_PROVIDER__ environment variables. + * CODER_AIBRIDGE_PROVIDER__ is also accepted as a deprecated alias. + * This follows the same indexed pattern as ExternalAuthConfig. + */ +export interface AIProviderConfig { + /** + * Type is the provider type. Valid values are: "openai", + * "anthropic", "azure", "bedrock", "google", "openai-compat", + * "openrouter", "vercel", "copilot". + */ + readonly type: string; + /** + * Name is the unique instance identifier used for routing. + * Defaults to Type if not provided. + */ + readonly name: string; + /** + * BaseURL is the base URL of the upstream provider API. + */ + readonly base_url: string; + readonly bedrock_region?: string; + readonly bedrock_model?: string; + readonly bedrock_small_fast_model?: string; +} + +// From codersdk/aiproviders.go +/** + * AIProviderKey is a single API key registered on a provider. The + * plaintext is never returned; Masked is a one-way rendering safe for + * display (see aibridge utils MaskSecret). ID lets clients reference + * the row in an UpdateAIProviderRequest without re-sending plaintext. + */ +export interface AIProviderKey { + readonly id: string; + readonly masked: string; + readonly created_at: string; +} + +// From codersdk/aiproviders.go +/** + * AIProviderKeyMutation describes the intended state of a single key + * in an UpdateAIProviderRequest. Exactly one of ID or APIKey must be + * set: + * + * - ID set, APIKey nil: keep this existing key (matched by ID). + * - ID nil, APIKey set: insert this new plaintext as a new key. + * + * Any existing key whose ID is absent from the request is deleted. + */ +export interface AIProviderKeyMutation { + readonly id?: string; + readonly api_key?: string; +} + +// From codersdk/aiproviders.go +/** + * AIProviderSettings is the discriminated container for type-specific + * provider settings stored in ai_providers.settings. Providers that + * need no type-specific configuration (current OpenAI and standard + * Anthropic flows) leave every field nil; the wire form for those + * providers is JSON null. + * + * On the wire, settings serialize as a JSON object that always carries + * _type and _version discriminator keys alongside the type-specific + * fields. The custom (Un)MarshalJSON implementations on this type + * handle the routing automatically; callers should never marshal the + * concrete settings struct directly. + */ +export interface AIProviderSettings {} + +// From codersdk/aiproviders_bedrock.go +/** + * AIProviderSettingsTypeBedrock is the _type discriminator value for + * AIProviderBedrockSettings. + */ +export const AIProviderSettingsTypeBedrock = "bedrock"; + +// From codersdk/aiproviders.go +/** + * AIProviderStatus carries non-fatal routing warnings. Direct + * routing remains available for the provider. + */ +export interface AIProviderStatus { + readonly warnings?: readonly string[]; +} + +// From codersdk/chats.go +/** + * AIProviderSummary is provider metadata embedded in other API responses. + */ +export interface AIProviderSummary { + readonly id: string; + readonly type: AIProviderType; + readonly name: string; + readonly display_name: string; + readonly icon: string; + readonly enabled: boolean; + readonly deleted: boolean; +} + +// From codersdk/aiproviders.go +export type AIProviderType = + | "anthropic" + | "azure" + | "bedrock" + | "copilot" + | "google" + | "openai" + | "openai-compat" + | "openrouter" + | "vercel"; + +export const AIProviderTypes: AIProviderType[] = [ + "anthropic", + "azure", + "bedrock", + "copilot", + "google", + "openai", + "openai-compat", + "openrouter", + "vercel", +]; + +// From codersdk/aibridge.go +/** + * AISpendPeriodWindow is the [Start, End) window over which AI spend is + * aggregated. + */ +export interface AISpendPeriodWindow { + /** + * PeriodStart is the inclusive lower bound of the current budget + * period. + */ + readonly period_start: string; + /** + * PeriodEnd is the exclusive upper bound of the current budget + * period. + */ + readonly period_end: string; +} + +// From codersdk/allowlist.go +/** + * APIAllowListTarget represents a single allow-list entry using the canonical + * string form ":". The wildcard symbol "*" is treated as a + * permissive match for either side. + */ +export interface APIAllowListTarget { + readonly type: RBACResource; + readonly id: string; +} + +// From codersdk/apikey.go +/** + * APIKey: do not ever return the HashedSecret + */ +export interface APIKey { + readonly id: string; + readonly user_id: string; + readonly last_used: string; + readonly expires_at: string; + readonly created_at: string; + readonly updated_at: string; + readonly login_type: LoginType; + readonly scope: APIKeyScope; // Deprecated: use Scopes instead. + readonly scopes: readonly APIKeyScope[]; + readonly token_name: string; + readonly lifetime_seconds: number; + readonly allow_list: readonly APIAllowListTarget[]; +} + +// From codersdk/apikey.go +export type APIKeyScope = + | "ai_gateway_key:*" + | "ai_gateway_key:create" + | "ai_gateway_key:delete" + | "ai_gateway_key:read" + | "ai_gateway_key:update" + | "ai_model_price:*" + | "ai_model_price:read" + | "ai_model_price:update" + | "ai_provider:*" + | "ai_provider:create" + | "ai_provider:delete" + | "ai_provider:read" + | "ai_provider:update" + | "ai_seat:*" + | "ai_seat:create" + | "ai_seat:read" + | "aibridge_interception:*" + | "aibridge_interception:create" + | "aibridge_interception:read" + | "aibridge_interception:update" + | "all" + | "api_key:*" + | "api_key:create" + | "api_key:delete" + | "api_key:read" + | "api_key:update" + | "application_connect" + | "assign_org_role:*" + | "assign_org_role:assign" + | "assign_org_role:create" + | "assign_org_role:delete" + | "assign_org_role:read" + | "assign_org_role:unassign" + | "assign_org_role:update" + | "assign_role:*" + | "assign_role:assign" + | "assign_role:read" + | "assign_role:unassign" + | "audit_log:*" + | "audit_log:create" + | "audit_log:read" + | "boundary_log:*" + | "boundary_log:create" + | "boundary_log:delete" + | "boundary_log:read" + | "boundary_usage:*" + | "boundary_usage:delete" + | "boundary_usage:read" + | "boundary_usage:update" + | "chat:*" + | "chat:create" + | "chat:delete" + | "chat_model_config:*" + | "chat_model_config:create" + | "chat_model_config:delete" + | "chat_model_config:read" + | "chat_model_config:share" + | "chat_model_config:update" + | "chat:read" + | "chat:share" + | "chat:update" + | "coder:all" + | "coder:apikeys.manage_self" + | "coder:application_connect" + | "coder:templates.author" + | "coder:templates.build" + | "coder:workspaces.access" + | "coder:workspaces.create" + | "coder:workspaces.delete" + | "coder:workspaces.operate" + | "connection_log:*" + | "connection_log:read" + | "connection_log:update" + | "crypto_key:*" + | "crypto_key:create" + | "crypto_key:delete" + | "crypto_key:read" + | "crypto_key:update" + | "debug_info:*" + | "debug_info:read" + | "deployment_config:*" + | "deployment_config:read" + | "deployment_config:update" + | "deployment_stats:*" + | "deployment_stats:read" + | "file:*" + | "file:create" + | "file:read" + | "group:*" + | "group:create" + | "group:delete" + | "group_member:*" + | "group_member:read" + | "group:read" + | "group:update" + | "idpsync_settings:*" + | "idpsync_settings:read" + | "idpsync_settings:update" + | "inbox_notification:*" + | "inbox_notification:create" + | "inbox_notification:read" + | "inbox_notification:update" + | "license:*" + | "license:create" + | "license:delete" + | "license:read" + | "mcp_server_config:*" + | "mcp_server_config:create" + | "mcp_server_config:delete" + | "mcp_server_config:read" + | "mcp_server_config:share" + | "mcp_server_config:update" + | "notification_message:*" + | "notification_message:create" + | "notification_message:delete" + | "notification_message:read" + | "notification_message:update" + | "notification_preference:*" + | "notification_preference:read" + | "notification_preference:update" + | "notification_template:*" + | "notification_template:read" + | "notification_template:update" + | "oauth2_app:*" + | "oauth2_app_code_token:*" + | "oauth2_app_code_token:create" + | "oauth2_app_code_token:delete" + | "oauth2_app_code_token:read" + | "oauth2_app:create" + | "oauth2_app:delete" + | "oauth2_app:read" + | "oauth2_app_secret:*" + | "oauth2_app_secret:create" + | "oauth2_app_secret:delete" + | "oauth2_app_secret:read" + | "oauth2_app_secret:update" + | "oauth2_app:update" + | "organization:*" + | "organization:create" + | "organization:delete" + | "organization_member:*" + | "organization_member:create" + | "organization_member:delete" + | "organization_member:read" + | "organization_member:update" + | "organization:read" + | "organization:update" + | "prebuilt_workspace:*" + | "prebuilt_workspace:delete" + | "prebuilt_workspace:update" + | "provisioner_daemon:*" + | "provisioner_daemon:create" + | "provisioner_daemon:delete" + | "provisioner_daemon:read" + | "provisioner_daemon:update" + | "provisioner_jobs:*" + | "provisioner_jobs:create" + | "provisioner_jobs:read" + | "provisioner_jobs:update" + | "replicas:*" + | "replicas:read" + | "system:*" + | "system:create" + | "system:delete" + | "system:read" + | "system:update" + | "tailnet_coordinator:*" + | "tailnet_coordinator:create" + | "tailnet_coordinator:delete" + | "tailnet_coordinator:read" + | "tailnet_coordinator:update" + | "task:*" + | "task:create" + | "task:delete" + | "task:read" + | "task:update" + | "template:*" + | "template:create" + | "template:delete" + | "template:read" + | "template:update" + | "template:use" + | "template:view_insights" + | "usage_event:*" + | "usage_event:create" + | "usage_event:read" + | "usage_event:update" + | "user:*" + | "user:create" + | "user:delete" + | "user:read" + | "user:read_personal" + | "user_secret:*" + | "user_secret:create" + | "user_secret:delete" + | "user_secret:read" + | "user_secret:update" + | "user_skill:*" + | "user_skill:create" + | "user_skill:delete" + | "user_skill:read" + | "user_skill:update" + | "user:update" + | "user:update_personal" + | "webpush_subscription:*" + | "webpush_subscription:create" + | "webpush_subscription:delete" + | "webpush_subscription:read" + | "workspace_agent_devcontainers:*" + | "workspace_agent_devcontainers:create" + | "workspace_agent_resource_monitor:*" + | "workspace_agent_resource_monitor:create" + | "workspace_agent_resource_monitor:read" + | "workspace_agent_resource_monitor:update" + | "workspace:*" + | "workspace:application_connect" + | "workspace_build_orchestration:*" + | "workspace_build_orchestration:create" + | "workspace_build_orchestration:delete" + | "workspace_build_orchestration:read" + | "workspace_build_orchestration:update" + | "workspace:create" + | "workspace:create_agent" + | "workspace:delete" + | "workspace:delete_agent" + | "workspace_dormant:*" + | "workspace_dormant:application_connect" + | "workspace_dormant:create" + | "workspace_dormant:create_agent" + | "workspace_dormant:delete" + | "workspace_dormant:delete_agent" + | "workspace_dormant:read" + | "workspace_dormant:share" + | "workspace_dormant:ssh" + | "workspace_dormant:start" + | "workspace_dormant:stop" + | "workspace_dormant:update" + | "workspace_dormant:update_agent" + | "workspace_proxy:*" + | "workspace_proxy:create" + | "workspace_proxy:delete" + | "workspace_proxy:read" + | "workspace_proxy:update" + | "workspace:read" + | "workspace:share" + | "workspace:ssh" + | "workspace:start" + | "workspace:stop" + | "workspace:update" + | "workspace:update_agent"; + +export const APIKeyScopes: APIKeyScope[] = [ + "ai_gateway_key:*", + "ai_gateway_key:create", + "ai_gateway_key:delete", + "ai_gateway_key:read", + "ai_gateway_key:update", + "ai_model_price:*", + "ai_model_price:read", + "ai_model_price:update", + "ai_provider:*", + "ai_provider:create", + "ai_provider:delete", + "ai_provider:read", + "ai_provider:update", + "ai_seat:*", + "ai_seat:create", + "ai_seat:read", + "aibridge_interception:*", + "aibridge_interception:create", + "aibridge_interception:read", + "aibridge_interception:update", + "all", + "api_key:*", + "api_key:create", + "api_key:delete", + "api_key:read", + "api_key:update", + "application_connect", + "assign_org_role:*", + "assign_org_role:assign", + "assign_org_role:create", + "assign_org_role:delete", + "assign_org_role:read", + "assign_org_role:unassign", + "assign_org_role:update", + "assign_role:*", + "assign_role:assign", + "assign_role:read", + "assign_role:unassign", + "audit_log:*", + "audit_log:create", + "audit_log:read", + "boundary_log:*", + "boundary_log:create", + "boundary_log:delete", + "boundary_log:read", + "boundary_usage:*", + "boundary_usage:delete", + "boundary_usage:read", + "boundary_usage:update", + "chat:*", + "chat:create", + "chat:delete", + "chat_model_config:*", + "chat_model_config:create", + "chat_model_config:delete", + "chat_model_config:read", + "chat_model_config:share", + "chat_model_config:update", + "chat:read", + "chat:share", + "chat:update", + "coder:all", + "coder:apikeys.manage_self", + "coder:application_connect", + "coder:templates.author", + "coder:templates.build", + "coder:workspaces.access", + "coder:workspaces.create", + "coder:workspaces.delete", + "coder:workspaces.operate", + "connection_log:*", + "connection_log:read", + "connection_log:update", + "crypto_key:*", + "crypto_key:create", + "crypto_key:delete", + "crypto_key:read", + "crypto_key:update", + "debug_info:*", + "debug_info:read", + "deployment_config:*", + "deployment_config:read", + "deployment_config:update", + "deployment_stats:*", + "deployment_stats:read", + "file:*", + "file:create", + "file:read", + "group:*", + "group:create", + "group:delete", + "group_member:*", + "group_member:read", + "group:read", + "group:update", + "idpsync_settings:*", + "idpsync_settings:read", + "idpsync_settings:update", + "inbox_notification:*", + "inbox_notification:create", + "inbox_notification:read", + "inbox_notification:update", + "license:*", + "license:create", + "license:delete", + "license:read", + "mcp_server_config:*", + "mcp_server_config:create", + "mcp_server_config:delete", + "mcp_server_config:read", + "mcp_server_config:share", + "mcp_server_config:update", + "notification_message:*", + "notification_message:create", + "notification_message:delete", + "notification_message:read", + "notification_message:update", + "notification_preference:*", + "notification_preference:read", + "notification_preference:update", + "notification_template:*", + "notification_template:read", + "notification_template:update", + "oauth2_app:*", + "oauth2_app_code_token:*", + "oauth2_app_code_token:create", + "oauth2_app_code_token:delete", + "oauth2_app_code_token:read", + "oauth2_app:create", + "oauth2_app:delete", + "oauth2_app:read", + "oauth2_app_secret:*", + "oauth2_app_secret:create", + "oauth2_app_secret:delete", + "oauth2_app_secret:read", + "oauth2_app_secret:update", + "oauth2_app:update", + "organization:*", + "organization:create", + "organization:delete", + "organization_member:*", + "organization_member:create", + "organization_member:delete", + "organization_member:read", + "organization_member:update", + "organization:read", + "organization:update", + "prebuilt_workspace:*", + "prebuilt_workspace:delete", + "prebuilt_workspace:update", + "provisioner_daemon:*", + "provisioner_daemon:create", + "provisioner_daemon:delete", + "provisioner_daemon:read", + "provisioner_daemon:update", + "provisioner_jobs:*", + "provisioner_jobs:create", + "provisioner_jobs:read", + "provisioner_jobs:update", + "replicas:*", + "replicas:read", + "system:*", + "system:create", + "system:delete", + "system:read", + "system:update", + "tailnet_coordinator:*", + "tailnet_coordinator:create", + "tailnet_coordinator:delete", + "tailnet_coordinator:read", + "tailnet_coordinator:update", + "task:*", + "task:create", + "task:delete", + "task:read", + "task:update", + "template:*", + "template:create", + "template:delete", + "template:read", + "template:update", + "template:use", + "template:view_insights", + "usage_event:*", + "usage_event:create", + "usage_event:read", + "usage_event:update", + "user:*", + "user:create", + "user:delete", + "user:read", + "user:read_personal", + "user_secret:*", + "user_secret:create", + "user_secret:delete", + "user_secret:read", + "user_secret:update", + "user_skill:*", + "user_skill:create", + "user_skill:delete", + "user_skill:read", + "user_skill:update", + "user:update", + "user:update_personal", + "webpush_subscription:*", + "webpush_subscription:create", + "webpush_subscription:delete", + "webpush_subscription:read", + "workspace_agent_devcontainers:*", + "workspace_agent_devcontainers:create", + "workspace_agent_resource_monitor:*", + "workspace_agent_resource_monitor:create", + "workspace_agent_resource_monitor:read", + "workspace_agent_resource_monitor:update", + "workspace:*", + "workspace:application_connect", + "workspace_build_orchestration:*", + "workspace_build_orchestration:create", + "workspace_build_orchestration:delete", + "workspace_build_orchestration:read", + "workspace_build_orchestration:update", + "workspace:create", + "workspace:create_agent", + "workspace:delete", + "workspace:delete_agent", + "workspace_dormant:*", + "workspace_dormant:application_connect", + "workspace_dormant:create", + "workspace_dormant:create_agent", + "workspace_dormant:delete", + "workspace_dormant:delete_agent", + "workspace_dormant:read", + "workspace_dormant:share", + "workspace_dormant:ssh", + "workspace_dormant:start", + "workspace_dormant:stop", + "workspace_dormant:update", + "workspace_dormant:update_agent", + "workspace_proxy:*", + "workspace_proxy:create", + "workspace_proxy:delete", + "workspace_proxy:read", + "workspace_proxy:update", + "workspace:read", + "workspace:share", + "workspace:ssh", + "workspace:start", + "workspace:stop", + "workspace:update", + "workspace:update_agent", +]; + +// From codersdk/apikey.go +export interface APIKeyWithOwner extends APIKey { + readonly username: string; +} + +// From healthsdk/healthsdk.go +/** + * AccessURLReport shows the results of performing a HTTP_GET to the /healthz endpoint through the configured access URL. + */ +export interface AccessURLReport extends BaseReport { + /** + * Healthy is deprecated and left for backward compatibility purposes, use `Severity` instead. + */ + readonly healthy: boolean; + readonly access_url: string; + readonly reachable: boolean; + readonly status_code: number; + readonly healthz_response: string; +} + +// From codersdk/licenses.go +export interface AddLicenseRequest { + readonly license: string; +} + +// From codersdk/deployment.go +export type Addon = "ai_governance"; + +export const Addons: Addon[] = ["ai_governance"]; + +// From codersdk/chats.go +/** + * AdvisorConfig is the deployment-wide runtime configuration for the + * experimental chat advisor. + * + * EXPERIMENTAL: this type is experimental and is subject to change. + */ +export interface AdvisorConfig { + /** + * Enabled reflects whether the chat-advisor experiment is active. + * The experiment flag is the sole gate; this field is read-only and + * always matches the experiment state regardless of the stored DB value. + */ + readonly enabled: boolean; + /** + * MaxUsesPerRun caps how many times the advisor can be invoked per + * chat run. 0 means unlimited. + */ + readonly max_uses_per_run: number; + /** + * MaxOutputTokens caps the advisor model response tokens. 0 means + * use the runtime default. + */ + readonly max_output_tokens: number; +} + +// From codersdk/users.go +export type AgentChatSendShortcut = "enter" | "modifier_enter"; + +export const AgentChatSendShortcuts: AgentChatSendShortcut[] = [ + "enter", + "modifier_enter", +]; + +// From codersdk/workspacebuilds.go +export interface AgentConnectionTiming { + readonly started_at: string; + readonly ended_at: string; + readonly stage: TimingStage; + readonly workspace_agent_id: string; + readonly workspace_agent_name: string; +} + +// From codersdk/users.go +export type AgentDisplayMode = "always_collapsed" | "always_expanded" | "auto"; + +export const AgentDisplayModes: AgentDisplayMode[] = [ + "always_collapsed", + "always_expanded", + "auto", +]; + +// From codersdk/agentfirewall.go +/** + * AgentFirewallLog represents a single audit event from an agent firewall proxy. + */ +export interface AgentFirewallLog { + readonly id: string; + readonly session_id: string; + readonly sequence_number: number; + readonly allowed: boolean; + readonly created_at: string; + readonly proto: string; + readonly method: string; + readonly detail: string; + readonly matched_rule: string | null; + readonly captured_at?: string; +} + +// From codersdk/agentfirewall.go +/** + * AgentFirewallSession represents a firewall session for a workspace agent. + */ +export interface AgentFirewallSession { + readonly id: string; + readonly workspace_id: string; + readonly owner_id: string; + readonly confined_process: string; + readonly started_at: string; +} + +// From codersdk/agentfirewall.go +/** + * AgentFirewallSessionLogsParams are query parameters for listing + * agent firewall session logs. + */ +export interface AgentFirewallSessionLogsParams { + /** + * SeqAfter is an inclusive lower bound on sequence_number. + * Only logs with sequence_number >= SeqAfter are returned. + */ + readonly seq_after?: number; + /** + * SeqBefore is an exclusive upper bound on sequence_number. + * Only logs with sequence_number < SeqBefore are returned. + */ + readonly seq_before?: number; + /** + * Limit caps the number of returned rows. Defaults to 100. + */ + readonly limit?: number; +} + +// From codersdk/agentfirewall.go +/** + * AgentFirewallSessionLogsResponse is the response for + * GET /api/v2/agent-firewall/sessions/{id}/logs. + */ +export interface AgentFirewallSessionLogsResponse { + readonly results: readonly AgentFirewallLog[]; +} + +// From agenthooks/types.go +/** + * ChatRef identifies the chat a lifecycle hook event refers to. + */ +export interface AgentHookChatRef { + readonly chat_id: string; + readonly owner_id: string; + readonly workspace_id?: string; + readonly turn_id?: string; + readonly parent_chat_id?: string; + /** + * RootChatID identifies the user-facing root of the chat tree. + */ + readonly root_chat_id?: string; +} + +// From agenthooks/types.go +/** + * Claims describes the JWT minted by coderd for a lifecycle hook dispatch. + */ +export interface AgentHookClaims { + readonly iss: string; + readonly sub: string; + readonly aud: string; + readonly iat: number; + readonly nbf: number; + readonly exp: number; + readonly jti: string; + readonly type: AgentHookEventType; + readonly body_sha256: string; +} + +// From agenthooks/types.go +export type AgentHookEventType = + | "post_compact" + | "post_tool_use" + | "pre_compact" + | "pre_tool_use" + | "session_start" + | "stop" + | "user_prompt_submit"; + +export const AgentHookEventTypes: AgentHookEventType[] = [ + "post_compact", + "post_tool_use", + "pre_compact", + "pre_tool_use", + "session_start", + "stop", + "user_prompt_submit", +]; + +// From agenthooks/http.go +/** + * Hooks lets a consumer implement only the lifecycle events it uses. + */ +export interface AgentHookHooks { + // Function type detected, and unsupported. Leaving the type as unknown + readonly SessionStart: unknown; + // Function type detected, and unsupported. Leaving the type as unknown + readonly UserPromptSubmit: unknown; + // Function type detected, and unsupported. Leaving the type as unknown + readonly PreToolUse: unknown; + // Function type detected, and unsupported. Leaving the type as unknown + readonly PostToolUse: unknown; + // Function type detected, and unsupported. Leaving the type as unknown + readonly PreCompact: unknown; + // Function type detected, and unsupported. Leaving the type as unknown + readonly PostCompact: unknown; + // Function type detected, and unsupported. Leaving the type as unknown + readonly Stop: unknown; +} + +// From agenthooks/http.go +/** + * MaxRequestBodyBytes limits memory used to verify hook requests. + */ +export const AgentHookMaxRequestBodyBytes = 10485760; // 10 MiB + +// From agenthooks/types.go +/** + * Meta identifies a hook dispatch and its chat. + */ +export interface AgentHookMeta extends AgentHookChatRef { + readonly dispatch_id: string; + readonly schema_version: number; +} + +// From agenthooks/jwt.go +/** + * MinSecretLen is the minimum HS256 secret length in bytes. go-jose + * accepts shorter keys, so signing and verification enforce it to fail + * closed on missing or weak secrets. + */ +export const AgentHookMinSecretLen = 32; + +// From agenthooks/types.go +/** + * Permission controls whether mutable hook input may proceed. + */ +export interface AgentHookPermission { + readonly decision: AgentHookPermissionDecision; + readonly reason?: string; + readonly input_override?: unknown; +} + +// From agenthooks/types.go +export type AgentHookPermissionDecision = "allow" | "deny"; + +export const AgentHookPermissionDecisions: AgentHookPermissionDecision[] = [ + "allow", + "deny", +]; + +// From agenthooks/types.go +/** + * PostCompactData is empty; Meta identifies the compacted chat. + */ +export interface AgentHookPostCompactData {} + +// From agenthooks/types.go +/** + * PostToolUseData describes a completed tool call, carrying either + * ToolResponse or ToolError. + */ +export interface AgentHookPostToolUseData { + readonly tool_use_id: string; + readonly tool_name: string; + readonly tool_response?: unknown; + readonly tool_error?: string; +} + +// From agenthooks/types.go +/** + * PreCompactData is empty; Meta identifies the chat being compacted. + */ +export interface AgentHookPreCompactData {} + +// From agenthooks/types.go +/** + * PreToolUseData describes a tool call before execution. + */ +export interface AgentHookPreToolUseData { + readonly tool_use_id: string; + readonly tool_name: string; + readonly tool_input: unknown; +} + +// From agenthooks/types.go +/** + * Request is the body coderd posts to the configured lifecycle hook URL. + */ +export interface AgentHookRequest { + readonly type: AgentHookEventType; + readonly meta: AgentHookMeta; + readonly data: unknown; +} + +// From agenthooks/types.go +/** + * Response carries a consumer's decision and optional injected content. + * Permission is honored for user_prompt_submit and pre_tool_use only. + * user_prompt_submit folds injected content into the submitted message. + * A denied pre_tool_use yields a synthetic tool result carrying only the + * policy text and any Reason; ModelContext persists separately as + * model-only transcript content that never reaches clients. + */ +export interface AgentHookResponse { + readonly permission?: AgentHookPermission; + readonly model_context?: string; + readonly user_message?: string; +} + +// From agenthooks/types.go +/** + * SchemaVersion is the current lifecycle hook request schema version. + */ +export const AgentHookSchemaVersion = 1; + +// From agenthooks/types.go +/** + * SessionStartData reports why a chat session started. Source is + * "startup", "resume", or "clear". + */ +export interface AgentHookSessionStartData { + readonly source: string; +} + +// From agenthooks/types.go +/** + * StopData is empty; Meta identifies the chat that stopped. + */ +export interface AgentHookStopData {} + +// From agenthooks/types.go +/** + * UserPromptSubmitData includes concatenated text and persisted parts. + * Inspect Parts when structure matters. + */ +export interface AgentHookUserPromptSubmitData { + readonly prompt: string; + readonly parts?: unknown; +} + +// From codersdk/workspacebuilds.go +export interface AgentScriptTiming { + readonly started_at: string; + readonly ended_at: string; + readonly exit_code: number; + readonly stage: TimingStage; + readonly status: string; + readonly display_name: string; + readonly workspace_agent_id: string; + readonly workspace_agent_name: string; +} + +// From codersdk/templates.go +/** + * AgentStatsReportResponse is returned for each report + * request by the agent. + */ +export interface AgentStatsReportResponse { + readonly num_comms: number; + /** + * RxBytes is the number of received bytes. + */ + readonly rx_bytes: number; + /** + * TxBytes is the number of transmitted bytes. + */ + readonly tx_bytes: number; +} + +// From codersdk/workspaceagents.go +export type AgentSubsystem = "envbox" | "envbuilder" | "exectrace"; + +export const AgentSubsystems: AgentSubsystem[] = [ + "envbox", + "envbuilder", + "exectrace", +]; + +// From codersdk/aiproviders.go +export type AgentsUnsupportedProviderType = "copilot"; + +export const AgentsUnsupportedProviderTypes: AgentsUnsupportedProviderType[] = [ + "copilot", +]; + +// From codersdk/chats.go +/** + * AnthropicInlineImageCapBytes is Anthropic's documented per-image + * wire limit; the same cap applies to Bedrock-hosted Claude. Other + * providers have no documented per-image cap. + */ +export const AnthropicInlineImageCapBytes = 5242880; + +// From codersdk/deployment.go +export interface AppHostResponse { + /** + * Host is the externally accessible URL for the Coder instance. + */ + readonly host: string; +} + +// From codersdk/deployment.go +export interface AppearanceConfig { + readonly application_name: string; + readonly logo_url: string; + readonly docs_url: string; + /** + * @deprecated ServiceBanner has been replaced by AnnouncementBanners. + */ + readonly service_banner: BannerConfig; + readonly announcement_banners: readonly BannerConfig[]; + readonly support_links?: readonly LinkConfig[]; + readonly codernauts_enabled: boolean; +} + +// From codersdk/templates.go +export interface ArchiveTemplateVersionsRequest { + /** + * By default, only failed versions are archived. Set this to true + * to archive all unused versions regardless of job status. + */ + readonly all: boolean; +} + +// From codersdk/templates.go +export interface ArchiveTemplateVersionsResponse { + readonly template_id: string; + readonly archived_ids: readonly string[]; +} + +// From codersdk/roles.go +export interface AssignableRoles extends Role { + readonly assignable: boolean; + /** + * BuiltIn roles are immutable + */ + readonly built_in: boolean; +} + +// From codersdk/audit.go +export type AuditAction = + | "close" + | "connect" + | "create" + | "delete" + | "disconnect" + | "login" + | "logout" + | "open" + | "register" + | "request_password_reset" + | "start" + | "stop" + | "write"; + +export const AuditActions: AuditAction[] = [ + "close", + "connect", + "create", + "delete", + "disconnect", + "login", + "logout", + "open", + "register", + "request_password_reset", + "start", + "stop", + "write", +]; + +// From codersdk/audit.go +export type AuditDiff = Record; + +// From codersdk/audit.go +export interface AuditDiffField { + // empty interface{} type, falling back to unknown + readonly old?: unknown; + // empty interface{} type, falling back to unknown + readonly new?: unknown; + readonly secret: boolean; +} + +// From codersdk/audit.go +export interface AuditLog { + readonly id: string; + readonly request_id: string; + readonly time: string; + readonly ip: string; + readonly user_agent: string; + readonly resource_type: ResourceType; + readonly resource_id: string; + /** + * ResourceTarget is the name of the resource. + */ + readonly resource_target: string; + readonly resource_icon: string; + readonly action: AuditAction; + readonly diff: AuditDiff; + readonly status_code: number; + readonly additional_fields: Record; + readonly description: string; + readonly resource_link: string; + readonly is_deleted: boolean; + /** + * @deprecated Use 'organization.id' instead. + */ + readonly organization_id: string; + readonly organization?: MinimalOrganization; + readonly user: User | null; +} + +// From codersdk/audit.go +export interface AuditLogResponse { + readonly audit_logs: readonly AuditLog[]; + readonly count: number; + readonly count_cap: number; +} + +// From codersdk/audit.go +export interface AuditLogsRequest extends Pagination { + readonly q?: string; +} + +// From codersdk/users.go +export interface AuthMethod { + readonly enabled: boolean; +} + +// From codersdk/users.go +/** + * AuthMethods contains authentication method information like whether they are enabled or not or custom text, etc. + */ +export interface AuthMethods { + readonly terms_of_service_url?: string; + readonly password: AuthMethod; + readonly github: GithubAuthMethod; + readonly oidc: OIDCAuthMethod; +} + +// From codersdk/authorization.go +/** + * AuthorizationCheck is used to check if the currently authenticated user (or the specified user) can do a given action to a given set of objects. + * + * @Description AuthorizationCheck is used to check if the currently authenticated user (or the specified user) can do a given action to a given set of objects. + */ +export interface AuthorizationCheck { + /** + * Object can represent a "set" of objects, such as: all workspaces in an organization, all workspaces owned by me, and all workspaces across the entire product. + * When defining an object, use the most specific language when possible to + * produce the smallest set. Meaning to set as many fields on 'Object' as + * you can. Example, if you want to check if you can update all workspaces + * owned by 'me', try to also add an 'OrganizationID' to the settings. + * Omitting the 'OrganizationID' could produce the incorrect value, as + * workspaces have both `user` and `organization` owners. + */ + readonly object: AuthorizationObject; + readonly action: RBACAction; +} + +// From codersdk/authorization.go +/** + * AuthorizationObject can represent a "set" of objects, such as: all workspaces in an organization, all workspaces owned by me, + * all workspaces across the entire product. + * + * @Description AuthorizationObject can represent a "set" of objects, such as: all workspaces in an organization, all workspaces owned by me, + * @Description all workspaces across the entire product. + */ +export interface AuthorizationObject { + /** + * ResourceType is the name of the resource. + * `./coderd/rbac/object.go` has the list of valid resource types. + */ + readonly resource_type: RBACResource; + /** + * OwnerID (optional) adds the set constraint to all resources owned by a given user. + */ + readonly owner_id?: string; + /** + * OrganizationID (optional) adds the set constraint to all resources owned by a given organization. + */ + readonly organization_id?: string; + /** + * ResourceID (optional) reduces the set to a singular resource. This assigns + * a resource ID to the resource type, eg: a single workspace. + * The rbac library will not fetch the resource from the database, so if you + * are using this option, you should also set the owner ID and organization ID + * if possible. Be as specific as possible using all the fields relevant. + */ + readonly resource_id?: string; + /** + * AnyOrgOwner (optional) will disregard the org_owner when checking for permissions. + * This cannot be set to true if the OrganizationID is set. + */ + readonly any_org?: boolean; +} + +// From codersdk/authorization.go +/** + * AuthorizationRequest is a structure instead of a map because + * go-playground/validate can only validate structs. If you attempt to pass + * a map into `httpapi.Read`, you will get an invalid type error. + */ +export interface AuthorizationRequest { + /** + * Checks is a map keyed with an arbitrary string to a permission check. + * The key can be any string that is helpful to the caller, and allows + * multiple permission checks to be run in a single request. + * The key ensures that each permission check has the same key in the + * response. + */ + readonly checks: Record; +} + +// From codersdk/authorization.go +export type AuthorizationResponse = Record; + +// From codersdk/workspaces.go +export type AutomaticUpdates = "always" | "never"; + +export const AutomaticUpdateses: AutomaticUpdates[] = ["always", "never"]; + +// From codersdk/deployment.go +/** + * AvailableExperiments is an expandable type that returns all safe experiments + * available to be used with a deployment. + */ +export interface AvailableExperiments { + readonly safe: readonly Experiment[]; +} + +// From codersdk/deployment.go +export interface BannerConfig { + readonly enabled: boolean; + readonly message?: string; + readonly background_color?: string; +} + +// From healthsdk/healthsdk.go +/** + * BaseReport holds fields common to various health reports. + */ +export interface BaseReport { + readonly error?: string; + readonly severity: HealthSeverity; + readonly warnings: readonly HealthMessage[]; + readonly dismissed: boolean; +} + +// From codersdk/deployment.go +/** + * BuildInfoResponse contains build information for this instance of Coder. + */ +export interface BuildInfoResponse { + /** + * ExternalURL references the current Coder version. + * For production builds, this will link directly to a release. For development builds, this will link to a commit. + */ + readonly external_url: string; + /** + * Version returns the semantic version of the build. + */ + readonly version: string; + /** + * DashboardURL is the URL to hit the deployment's dashboard. + * For external workspace proxies, this is the coderd they are connected + * to. + */ + readonly dashboard_url: string; + /** + * Telemetry is a boolean that indicates whether telemetry is enabled. + */ + readonly telemetry: boolean; + readonly workspace_proxy: boolean; + /** + * AgentAPIVersion is the current version of the Agent API (back versions + * MAY still be supported). + */ + readonly agent_api_version: string; + /** + * ProvisionerAPIVersion is the current version of the Provisioner API + */ + readonly provisioner_api_version: string; + /** + * UpgradeMessage is the message displayed to users when an outdated client + * is detected. + */ + readonly upgrade_message: string; + /** + * DeploymentID is the unique identifier for this deployment. + */ + readonly deployment_id: string; + /** + * WebPushPublicKey is the public key for push notifications via Web Push. + */ + readonly webpush_public_key?: string; +} + +// From codersdk/workspacebuilds.go +export type BuildReason = + | "autostart" + | "autostop" + | "cli" + | "dashboard" + | "dormancy" + | "initiator" + | "jetbrains_connection" + | "ssh_connection" + | "task_auto_pause" + | "task_manual_pause" + | "task_resume" + | "vscode_connection"; + +export const BuildReasons: BuildReason[] = [ + "autostart", + "autostop", + "cli", + "dashboard", + "dormancy", + "initiator", + "jetbrains_connection", + "ssh_connection", + "task_auto_pause", + "task_manual_pause", + "task_resume", + "vscode_connection", +]; + +// From codersdk/client.go +/** + * BuildVersionHeader contains build information of Coder. + */ +export const BuildVersionHeader = "X-Coder-Build-Version"; + +// From codersdk/client.go +/** + * BypassRatelimitHeader is the custom header to use to bypass ratelimits. + * Only owners can bypass rate limits. This is typically used for scale testing. + * nolint: gosec + */ +export const BypassRatelimitHeader = "X-Coder-Bypass-Ratelimit"; + +// From codersdk/client.go +/** + * CLITelemetryHeader contains a base64-encoded representation of the CLI + * command that was invoked to produce the request. It is for internal use + * only. + */ +export const CLITelemetryHeader = "Coder-CLI-Telemetry"; + +// From codersdk/cors_behavior.go +export type CORSBehavior = "passthru" | "simple"; + +export const CORSBehaviors: CORSBehavior[] = ["passthru", "simple"]; + +// From codersdk/workspacebuilds.go +export interface CancelWorkspaceBuildParams { + /** + * ExpectStatus ensures the build is in the expected status before canceling. + */ + readonly expect_status?: CancelWorkspaceBuildStatus; +} + +// From codersdk/workspacebuilds.go +export type CancelWorkspaceBuildStatus = "pending" | "running"; + +export const CancelWorkspaceBuildStatuses: CancelWorkspaceBuildStatus[] = [ + "pending", + "running", +]; + +// From codersdk/users.go +/** + * ChangePasswordWithOneTimePasscodeRequest enables callers to change their password when they've forgotten it. + */ +export interface ChangePasswordWithOneTimePasscodeRequest { + readonly email: string; + readonly password: string; + readonly one_time_passcode: string; +} + +// From codersdk/chats.go +/** + * Chat represents a chat session with an AI agent. + */ +export interface Chat { + readonly id: string; + readonly organization_id: string; + readonly owner_id: string; + readonly owner_username?: string; + readonly owner_name?: string; + readonly workspace_id?: string; + readonly build_id?: string; + readonly agent_id?: string; + readonly parent_chat_id?: string; + readonly root_chat_id?: string; + readonly last_model_config_id: string; + readonly last_reasoning_effort?: string; + readonly title: string; + readonly status: ChatStatus; + readonly plan_mode?: ChatPlanMode; + readonly last_error?: ChatError; + readonly last_turn_summary: string | null; + /** + * Summary is the persisted whole-chat summary, generated in the background. + * It is nil until the first summary has been produced. + */ + readonly summary: string | null; + readonly diff_status?: ChatDiffStatus; + readonly created_at: string; + readonly updated_at: string; + readonly archived: boolean; + /** + * Shared is true when this chat's root chat has explicit user or group ACL entries. + */ + readonly shared: boolean; + readonly pin_order: number; + readonly mcp_server_ids: readonly string[]; + readonly labels: Record; + readonly files?: readonly ChatFileMetadata[]; + /** + * HasUnread is true when assistant messages exist beyond + * the owner's read cursor, which updates on stream + * connect and disconnect. + */ + readonly has_unread: boolean; + /** + * Context reports the chat's pinned workspace-context state and + * whether it has drifted from the agent's latest pushed snapshot. + * Nil when the chat has no pinned context yet. + */ + readonly context?: ChatContext; + /** + * QueuedForCapacity reports that the chat is waiting for a concurrent + * agent slot. Single-chat reads derive it; list responses leave it false. + */ + readonly queued_for_capacity?: boolean; + readonly warnings?: readonly string[]; + readonly client_type: ChatClientType; + /** + * Children holds child (subagent) chats nested under this root + * chat. Always initialized to an empty slice so the JSON field + * is present as []. Child chats cannot create their own + * subagents, so nesting depth is capped at 1 and this slice is + * always empty for child chats. + */ + readonly children: readonly Chat[]; +} + +// From codersdk/chats.go +export interface ChatACL { + readonly users: readonly ChatUser[]; + readonly groups: readonly ChatGroup[]; +} + +// From codersdk/chats.go +export type ChatAttachmentMediaType = + | "application/json" + | "application/pdf" + | "image/gif" + | "image/jpeg" + | "image/png" + | "image/webp" + | "text/csv" + | "text/markdown" + | "text/plain"; + +export const ChatAttachmentMediaTypes: ChatAttachmentMediaType[] = [ + "application/json", + "application/pdf", + "image/gif", + "image/jpeg", + "image/png", + "image/webp", + "text/csv", + "text/markdown", + "text/plain", +]; + +// From codersdk/chats.go +/** + * ChatAutoArchiveDaysResponse contains the current chat auto-archive setting. + */ +export interface ChatAutoArchiveDaysResponse { + readonly auto_archive_days: number; +} + +// From codersdk/chats.go +export type ChatBusyBehavior = "interrupt" | "queue"; + +export const ChatBusyBehaviors: ChatBusyBehavior[] = ["interrupt", "queue"]; + +// From codersdk/chats.go +export type ChatClientType = "api" | "ui"; + +export const ChatClientTypes: ChatClientType[] = ["api", "ui"]; + +// From codersdk/chats.go +/** + * ChatCompactionThresholdKeyPrefix scopes per-model chat compaction + * threshold settings. + */ +export const ChatCompactionThresholdKeyPrefix = + "chat_compaction_threshold_pct:"; + +// From codersdk/chats.go +export type ChatComputerUseProvider = "anthropic" | "openai"; + +// From codersdk/chats.go +/** + * ChatComputerUseProviderResponse is the response for getting the computer use + * provider setting. + */ +export interface ChatComputerUseProviderResponse { + readonly provider: ChatComputerUseProvider; +} + +export const ChatComputerUseProviders: ChatComputerUseProvider[] = [ + "anthropic", + "openai", +]; + +// From codersdk/deployment.go +export interface ChatConfig { + readonly acquire_batch_size: number; + readonly debug_logging_enabled: boolean; + readonly hook_url: string; + readonly hook_secret: string; + readonly hook_timeout: number; + readonly hook_enabled: boolean; + readonly hook_allow_insecure: boolean; + /** + * @deprecated AI Gateway routing is now the only routing path. Setting this + * value has no effect. This option will be removed in a future release. + */ + readonly ai_gateway_routing_enabled: boolean; +} + +// From codersdk/chats.go +/** + * ChatContext reports a chat's pinned workspace context and whether it has + * drifted from the agent's latest pushed snapshot. The chat stays usable + * when dirty; refreshing re-pins it to the latest snapshot. + */ +export interface ChatContext { + /** + * Dirty is true when the agent's latest snapshot hash differs from the + * chat's pinned hash. + */ + readonly dirty: boolean; + /** + * DirtySince is when drift was first detected; nil when not dirty. + */ + readonly dirty_since?: string; + /** + * Error is the snapshot-level error copied from the pinned snapshot + * (empty when healthy). + */ + readonly error?: string; + /** + * Resources is the chat's pinned context (instruction files and + * skills) the prompt is built from, metadata only (no bodies). It is + * populated only on the single-chat GET response; list and watch + * payloads leave it nil to stay lightweight. + */ + readonly resources?: readonly ChatContextResource[]; +} + +// From codersdk/chats.go +export interface ChatContextFilePart { + readonly type: "context-file"; + /** + * ContextFilePath is the absolute path of a file loaded into + * the LLM context (e.g. an AGENTS.md instruction file). + */ + readonly context_file_path: string; + /** + * ContextFileTruncated indicates the file exceeded the 64KiB + * instruction file limit and was truncated. + */ + readonly context_file_truncated?: boolean; + /** + * ContextFileAgentID is the workspace agent that provided + * this context file. Used to detect when the agent changes + * (e.g. workspace rebuilt) so instruction files can be + * re-persisted with fresh content. + */ + readonly context_file_agent_id?: string; +} + +// From codersdk/chats.go +/** + * ChatContextResource is one pinned workspace-context resource the chat's + * prompt is built from. It is metadata only; bodies are omitted. Reported + * only on the single-chat GET response. + */ +export interface ChatContextResource { + /** + * Source is the resource locator: the canonical file path for an + * instruction file, the skill directory for a skill, the file path for + * an MCP config, or the server name for an MCP server. + */ + readonly source: string; + readonly kind: ChatContextResourceKind; + /** + * SizeBytes is the original payload size in bytes. + */ + readonly size_bytes: number; + /** + * SkillName and SkillDescription are populated only for skill kinds. + */ + readonly skill_name?: string; + readonly skill_description?: string; + /** + * Tools lists the tools exposed by an MCP server. Populated only for the + * mcp_server kind; nil otherwise. + */ + readonly tools?: readonly ChatContextTool[]; + /** + * Status is the resource's health. Non-ok resources (invalid, unreadable, + * oversize, excluded) are still reported so the UI can surface why a + * resource was dropped from the prompt instead of silently omitting it; + * their body-specific fields (skill name, tools) are empty. + */ + readonly status: ChatContextResourceStatus; + /** + * Error explains a non-ok Status; empty when healthy. May also carry a + * non-fatal warning when Status is ok. + */ + readonly error?: string; +} + +// From codersdk/chats.go +export type ChatContextResourceKind = + | "instruction_file" + | "mcp_config" + | "mcp_server" + | "skill"; + +export const ChatContextResourceKinds: ChatContextResourceKind[] = [ + "instruction_file", + "mcp_config", + "mcp_server", + "skill", +]; + +// From codersdk/chats.go +export type ChatContextResourceStatus = + | "excluded" + | "invalid" + | "ok" + | "oversize" + | "unreadable"; + +export const ChatContextResourceStatuses: ChatContextResourceStatus[] = [ + "excluded", + "invalid", + "ok", + "oversize", + "unreadable", +]; + +// From codersdk/chats.go +/** + * ChatContextTool is one tool exposed by a pinned MCP server, reported on the + * single-chat GET response. Metadata only; the input schema is omitted. + */ +export interface ChatContextTool { + /** + * Name is the tool name with the `__` prefix the agent adds + * stripped, so it reads as the server exposes it. + */ + readonly name: string; + /** + * Description is the tool's human-readable summary; may be empty. + */ + readonly description?: string; +} + +// From codersdk/chats.go +/** + * ChatCost is the AI Gateway cost for the requested chat's whole tree. + * Root and subagent chats report the same total. + * RequestCount counts every finished request in the tree, including ones that + * recorded no billable usage at all, such as a request that failed upstream. + * UnpricedRequestCount counts requests with at least one usage record whose + * model had no recorded price; RequestCount includes them and + * TotalCostMicros omits only their unpriced usage. + */ +export interface ChatCost { + readonly chat_id: string; + readonly total_cost_micros: number; + readonly request_count: number; + readonly unpriced_request_count: number; +} + +// From codersdk/chats.go +/** + * ChatDebugLoggingAdminSettings describes the runtime admin setting + * that allows users to opt into chat debug logging. + */ +export interface ChatDebugLoggingAdminSettings { + readonly allow_users: boolean; + readonly forced_by_deployment: boolean; +} + +// From codersdk/chats.go +/** + * ChatDebugRetentionDaysResponse contains the current chat debug run + * retention setting. + */ +export interface ChatDebugRetentionDaysResponse { + readonly debug_retention_days: number; +} + +// From codersdk/chats.go +/** + * ChatDebugRun is the detailed run response returned by the run-detail + * endpoint. It includes the same summary fields as ChatDebugRunSummary + * along with the full step history for the run. + */ +export interface ChatDebugRun { + readonly id: string; + readonly chat_id: string; + readonly root_chat_id?: string; + readonly parent_chat_id?: string; + readonly model_config_id?: string; + readonly trigger_message_id?: number; + readonly history_tip_message_id?: number; + readonly kind: ChatDebugRunKind; + readonly status: ChatDebugStatus; + readonly provider?: string; + readonly model?: string; + // empty interface{} type, falling back to unknown + readonly summary: Record; + readonly started_at: string; + readonly updated_at: string; + readonly finished_at?: string; + readonly steps: readonly ChatDebugStep[]; +} + +// From codersdk/chats.go +export type ChatDebugRunKind = + | "chat_turn" + | "compaction" + | "quickgen" + | "title_generation"; + +export const ChatDebugRunKinds: ChatDebugRunKind[] = [ + "chat_turn", + "compaction", + "quickgen", + "title_generation", +]; + +// From codersdk/chats.go +/** + * ChatDebugRunSummary is a lightweight run entry for list endpoints. + */ +export interface ChatDebugRunSummary { + readonly id: string; + readonly chat_id: string; + readonly kind: ChatDebugRunKind; + readonly status: ChatDebugStatus; + readonly provider?: string; + readonly model?: string; + // empty interface{} type, falling back to unknown + readonly summary: Record; + readonly started_at: string; + readonly updated_at: string; + readonly finished_at?: string; +} + +// From codersdk/chats.go +export type ChatDebugStatus = + | "completed" + | "error" + | "in_progress" + | "interrupted"; + +export const ChatDebugStatuses: ChatDebugStatus[] = [ + "completed", + "error", + "in_progress", + "interrupted", +]; + +// From codersdk/chats.go +/** + * ChatDebugStep is a single step within a debug run. + */ +export interface ChatDebugStep { + readonly id: string; + readonly run_id: string; + readonly chat_id: string; + readonly step_number: number; + readonly operation: ChatDebugStepOperation; + readonly status: ChatDebugStatus; + readonly history_tip_message_id?: number; + readonly assistant_message_id?: number; + // empty interface{} type, falling back to unknown + readonly normalized_request: Record; + // empty interface{} type, falling back to unknown + readonly normalized_response?: Record; + // empty interface{} type, falling back to unknown + readonly usage?: Record; + // empty interface{} type, falling back to unknown + readonly attempts: readonly Record[]; + // empty interface{} type, falling back to unknown + readonly error?: Record; + // empty interface{} type, falling back to unknown + readonly metadata: Record; + readonly started_at: string; + readonly updated_at: string; + readonly finished_at?: string; +} + +// From codersdk/chats.go +export type ChatDebugStepOperation = "generate" | "stream"; + +export const ChatDebugStepOperations: ChatDebugStepOperation[] = [ + "generate", + "stream", +]; + +// From codersdk/chats.go +/** + * ChatDiffContents represents the resolved diff text for a chat. + */ +export interface ChatDiffContents { + readonly chat_id: string; + readonly provider?: string; + readonly remote_origin?: string; + readonly branch?: string; + readonly pull_request_url?: string; + readonly diff?: string; +} + +// From codersdk/chats.go +/** + * ChatDiffStatus represents cached diff status for a chat. The URL + * may point to a pull request or a branch page depending on whether + * a PR has been opened. + */ +export interface ChatDiffStatus { + readonly chat_id: string; + readonly url?: string; + readonly pull_request_state?: string; + readonly pull_request_title: string; + readonly pull_request_draft: boolean; + readonly changes_requested: boolean; + readonly additions: number; + readonly deletions: number; + readonly changed_files: number; + readonly author_login?: string; + readonly author_avatar_url?: string; + readonly base_branch?: string; + readonly head_branch?: string; + readonly pr_number?: number; + readonly commits?: number; + readonly approved?: boolean; + readonly reviewer_count?: number; + readonly refreshed_at?: string; + readonly stale_at?: string; +} + +// From codersdk/chats.go +/** + * ChatError represents a terminal chat error in persisted chat state or the + * live stream. + */ +export interface ChatError { + /** + * Message is the normalized, user-facing error message. + */ + readonly message: string; + /** + * Detail is optional provider-specific context shown alongside the + * normalized error message when available. + */ + readonly detail?: string; + /** + * Kind classifies the error for consistent client rendering. + */ + readonly kind?: ChatErrorKind; + /** + * Provider identifies the upstream model provider when known. + */ + readonly provider?: string; + /** + * Retryable reports whether the underlying error is transient. + */ + readonly retryable: boolean; + /** + * StatusCode is the best-effort upstream HTTP status code. + */ + readonly status_code?: number; +} + +// From codersdk/chats.go +export type ChatErrorKind = + | "auth" + | "config" + | "content_filter" + | "generic" + | "hook_denied" + | "hook_dispatch_failed" + | "missing_key" + | "overloaded" + | "provider_disabled" + | "rate_limit" + | "stream_silence_timeout" + | "timeout" + | "usage_limit"; + +export const ChatErrorKinds: ChatErrorKind[] = [ + "auth", + "config", + "content_filter", + "generic", + "hook_denied", + "hook_dispatch_failed", + "missing_key", + "overloaded", + "provider_disabled", + "rate_limit", + "stream_silence_timeout", + "timeout", + "usage_limit", +]; + +// From codersdk/chats.go +/** + * ChatFileDownloadURLResponse contains a short-lived URL for downloading a chat file. + */ +export interface ChatFileDownloadURLResponse { + readonly url: string; + readonly expires_at: string; + readonly sha256: string; + readonly size_bytes: number; + readonly name: string; + readonly mime_type: string; +} + +// From codersdk/chats.go +/** + * ChatFileMetadata contains lightweight metadata about a file + * associated with a chat, excluding the file content itself. + */ +export interface ChatFileMetadata { + readonly id: string; + readonly owner_id: string; + readonly organization_id: string; + readonly name: string; + readonly mime_type: string; + readonly size_bytes: number; + readonly created_at: string; +} + +// From codersdk/chats.go +export interface ChatFilePart { + readonly type: "file"; + readonly media_type: string; + readonly name?: string; + readonly data?: string; + readonly file_id?: string; +} + +// From codersdk/chats.go +export interface ChatFileReferencePart { + readonly type: "file-reference"; + readonly file_name: string; + readonly start_line: number; + readonly end_line: number; + /** + * The code content from the diff that was commented on. + */ + readonly content: string; +} + +// From codersdk/chats.go +/** + * ChatGitChange represents a git file change detected during a chat session. + */ +export interface ChatGitChange { + readonly id: string; + readonly chat_id: string; + readonly file_path: string; + readonly change_type: string; // added, modified, deleted, renamed + readonly old_path?: string; + readonly diff_summary?: string; + readonly detected_at: string; +} + +// From codersdk/chats.go +/** + * Chat git watch error messages. These are the user-visible messages + * the server returns in 400 responses from + * /api/v2/chats/{id}/stream/git when the chat cannot be + * observed through a workspace agent. They are exported so the CLI + * (and any future consumer) can match them structurally via + * IsChatGitWatchFallbackMessage instead of coupling to exact wording. + * Keep these in sync with coderd/exp_chats.go. + * ChatGitWatchAgentStatePrefix is the common prefix of the + * message produced by ChatGitWatchAgentStateMessage. The CLI + * uses it as a mechanical fingerprint for the "agent not yet + * connected" case without depending on the formatted values. + */ +export const ChatGitWatchAgentStatePrefix = "Agent state is "; + +// From codersdk/chats.go +/** + * Chat git watch error messages. These are the user-visible messages + * the server returns in 400 responses from + * /api/v2/chats/{id}/stream/git when the chat cannot be + * observed through a workspace agent. They are exported so the CLI + * (and any future consumer) can match them structurally via + * IsChatGitWatchFallbackMessage instead of coupling to exact wording. + * Keep these in sync with coderd/exp_chats.go. + */ +export const ChatGitWatchNoEligibleAgentMessage = + "No eligible agent found for chat workspace."; + +// From codersdk/chats.go +/** + * Chat git watch error messages. These are the user-visible messages + * the server returns in 400 responses from + * /api/v2/chats/{id}/stream/git when the chat cannot be + * observed through a workspace agent. They are exported so the CLI + * (and any future consumer) can match them structurally via + * IsChatGitWatchFallbackMessage instead of coupling to exact wording. + * Keep these in sync with coderd/exp_chats.go. + */ +export const ChatGitWatchNoWorkspaceMessage = "Chat has no workspace to watch."; + +// From codersdk/chats.go +/** + * Chat git watch error messages. These are the user-visible messages + * the server returns in 400 responses from + * /api/v2/chats/{id}/stream/git when the chat cannot be + * observed through a workspace agent. They are exported so the CLI + * (and any future consumer) can match them structurally via + * IsChatGitWatchFallbackMessage instead of coupling to exact wording. + * Keep these in sync with coderd/exp_chats.go. + */ +export const ChatGitWatchWorkspaceNotFoundMessage = "Chat workspace not found."; + +// From codersdk/chats.go +export interface ChatGroup extends Group { + readonly role: ChatRole; +} + +// From codersdk/chats.go +/** + * ChatHookDeniedResponse is the error body returned when a lifecycle hook + * denies a synchronous chat operation. Kind lets clients classify the denial + * without parsing message text. + */ +export interface ChatHookDeniedResponse extends Response { + readonly kind: ChatErrorKind; +} + +// From codersdk/chats.go +/** + * ChatHookDispatchFailedResponse is the error body returned when a + * lifecycle hook dispatch fails during a synchronous chat operation. + * Kind lets clients classify the failure without parsing message text. + */ +export interface ChatHookDispatchFailedResponse extends Response { + readonly kind: ChatErrorKind; +} + +// From codersdk/chats.go +export interface ChatHookNoticePart { + readonly type: "hook-notice"; + readonly text: string; +} + +// From codersdk/chats.go +/** + * ChatInputPart is a single user input part for creating a chat. + */ +export interface ChatInputPart { + readonly type: ChatInputPartType; + readonly text?: string; + readonly file_id?: string; + /** + * The following fields are only set when Type is + * ChatInputPartTypeFileReference. + */ + readonly file_name?: string; + readonly start_line?: number; + readonly end_line?: number; + /** + * The code content from the diff that was commented on. + */ + readonly content?: string; +} + +// From codersdk/chats.go +export type ChatInputPartType = "file" | "file-reference" | "text"; + +export const ChatInputPartTypes: ChatInputPartType[] = [ + "file", + "file-reference", + "text", +]; + +// From codersdk/chats.go +export type ChatListSource = "created_by_me" | "shared_with_me"; + +export const ChatListSources: ChatListSource[] = [ + "created_by_me", + "shared_with_me", +]; + +// From codersdk/chats.go +/** + * ChatMessage represents a single message in a chat. + */ +export interface ChatMessage { + readonly id: number; + readonly chat_id: string; + readonly created_by?: string; + readonly model_config_id?: string; + readonly created_at: string; + readonly role: ChatMessageRole; + readonly content?: readonly ChatMessagePart[]; + readonly usage?: ChatMessageUsage; +} + +// From codersdk/chats.go +/** + * ChatMessagePart is a structured chunk of a chat message. + * + * WARNING: This type is both an API wire type and a database + * persistence format. Its JSON layout is stored in the + * chat_messages.content column. Field additions, renames, type + * changes, and omitempty behavior all affect backward-compatible + * deserialization of stored rows. Treat changes to this struct + * with the same care as a database migration. + * + * The variants struct tag declares which discriminated-union + * variants include each field in the generated TypeScript. Bare + * name = required, ? suffix = optional. Fields without a variants + * tag are excluded from the generated union. See + * scripts/apitypings/main.go for the codegen that reads these. + * + * omitempty rules (enforced by TestChatMessagePartVariantTags): + * - If a field is required (no ? suffix) in ANY variant, it + * must NOT use omitempty. Go would silently drop zero values + * that TypeScript expects to always be present. + * - If a field is optional (? suffix) in ALL of its variants, + * it MUST use omitempty. Sending zero values for fields that + * the frontend does not expect adds noise to the wire format + * and wastes space in persisted chat_messages rows. + */ +export type ChatMessagePart = + | ChatTextPart + | ChatReasoningPart + | ChatToolCallPart + | ChatToolResultPart + | ChatSourcePart + | ChatFilePart + | ChatFileReferencePart + | ChatContextFilePart + | ChatSkillPart + | ChatHookNoticePart; + +// From codersdk/chats.go +export type ChatMessagePartType = + | "context-file" + | "file" + | "file-reference" + | "hook-context" + | "hook-notice" + | "reasoning" + | "skill" + | "source" + | "text" + | "tool-call" + | "tool-result"; + +export const ChatMessagePartTypes: ChatMessagePartType[] = [ + "context-file", + "file", + "file-reference", + "hook-context", + "hook-notice", + "reasoning", + "skill", + "source", + "text", + "tool-call", + "tool-result", +]; + +// From codersdk/chats.go +export type ChatMessageRole = "assistant" | "system" | "tool" | "user"; + +export const ChatMessageRoles: ChatMessageRole[] = [ + "assistant", + "system", + "tool", + "user", +]; + +// From codersdk/chats.go +/** + * ChatMessageUsage contains token usage information for a chat message. + */ +export interface ChatMessageUsage { + readonly input_tokens?: number; + readonly output_tokens?: number; + readonly total_tokens?: number; + readonly reasoning_tokens?: number; + readonly cache_creation_tokens?: number; + readonly cache_read_tokens?: number; + readonly context_limit?: number; +} + +// From codersdk/chats.go +/** + * GetChatMessages returns the messages and queued messages for a chat. + * ChatMessagesPaginationOptions are optional pagination params for + * GetChatMessages. + */ +export interface ChatMessagesPaginationOptions { + readonly BeforeID: number; + /** + * AfterID, when > 0, restricts results to messages with id strictly + * greater than AfterID. When set without BeforeID, results come back + * in ASCENDING id order so a polling caller can advance its cursor + * to max(returned_ids) without gaps. When combined with BeforeID, + * results come back in DESC order over the open range + * (AfterID, BeforeID). + */ + readonly AfterID: number; + readonly Limit: number; +} + +// From codersdk/chats.go +/** + * ChatMessagesResponse contains the messages and queued messages for a chat. + */ +export interface ChatMessagesResponse { + readonly messages: readonly ChatMessage[]; + readonly queued_messages: readonly ChatQueuedMessage[]; + readonly has_more: boolean; +} + +// From codersdk/chats.go +/** + * ChatModel is an org-scoped model configuration. + */ +export interface ChatModel { + readonly id: string; + readonly organization_id: string; + readonly ai_provider_id: string; + readonly model: string; + readonly display_name: string; + readonly enabled: boolean; + readonly is_default: boolean; + readonly context_limit: number; + readonly compression_threshold: number; + readonly model_config?: ChatModelCallConfig; + /** + * ReasoningEfforts lists selectable reasoning effort values through + * the model's configured maximum. + */ + readonly reasoning_efforts?: readonly string[]; + readonly created_at: string; + readonly updated_at: string; +} + +// From codersdk/chats.go +/** + * ChatModelACL is the access control list for an organization-scoped chat + * model. Each principal includes the identity details needed to display and + * manage the ACL without separate directory lookups. + */ +export interface ChatModelACL { + readonly users: readonly ChatUser[]; + readonly groups: readonly ChatGroup[]; +} + +// From codersdk/chats.go +/** + * ChatModelAnthropicProviderOptions configures Anthropic provider behavior. + */ +export interface ChatModelAnthropicProviderOptions { + readonly send_reasoning?: boolean; + readonly thinking?: ChatModelAnthropicThinkingOptions; + readonly thinking_display?: string; + readonly disable_parallel_tool_use?: boolean; + readonly web_search_enabled?: boolean; + readonly allowed_domains?: readonly string[]; + readonly blocked_domains?: readonly string[]; + readonly context_1m_enabled?: boolean; +} + +// From codersdk/chats.go +/** + * ChatModelAnthropicThinkingOptions configures Anthropic thinking budget. + */ +export interface ChatModelAnthropicThinkingOptions { + readonly budget_tokens?: number; +} + +// From codersdk/chats.go +/** + * ChatModelCallConfig configures per-call model behavior defaults. + */ +export interface ChatModelCallConfig { + readonly max_output_tokens?: number; + readonly temperature?: number; + readonly top_p?: number; + readonly top_k?: number; + readonly presence_penalty?: number; + readonly frequency_penalty?: number; + readonly reasoning_effort?: ChatModelReasoningEffortConfig; + readonly openai_config?: ChatModelOpenAIConfig; + readonly provider_options?: ChatModelProviderOptions; +} + +// From codersdk/chats.go +/** + * ChatModelGoogleProviderOptions configures Google provider behavior. + */ +export interface ChatModelGoogleProviderOptions { + readonly thinking_config?: ChatModelGoogleThinkingConfig; + readonly cached_content?: string; + readonly safety_settings?: readonly ChatModelGoogleSafetySetting[]; + readonly threshold?: string; + readonly web_search_enabled?: boolean; +} + +// From codersdk/chats.go +/** + * ChatModelGoogleSafetySetting configures Google safety filtering. + */ +export interface ChatModelGoogleSafetySetting { + readonly category?: string; + readonly threshold?: string; +} + +// From codersdk/chats.go +/** + * ChatModelGoogleThinkingConfig configures Google thinking behavior. + */ +export interface ChatModelGoogleThinkingConfig { + readonly thinking_budget?: number; + readonly thinking_level?: string; + readonly include_thoughts?: boolean; +} + +// From codersdk/chats.go +/** + * ChatModelOpenAICompatProviderOptions configures OpenAI-compatible behavior. + */ +export interface ChatModelOpenAICompatProviderOptions { + readonly user?: string; +} + +// From codersdk/chats.go +/** + * ChatModelOpenAIConfig holds settings applied once when the OpenAI client + * is built, not per request. + */ +export interface ChatModelOpenAIConfig { + readonly use_responses_api?: boolean; +} + +// From codersdk/chats.go +/** + * ChatModelOpenAIProviderOptions configures OpenAI provider behavior. + */ +export interface ChatModelOpenAIProviderOptions { + readonly include?: readonly string[]; + readonly instructions?: string; + readonly logit_bias?: Record; + readonly log_probs?: boolean; + readonly top_log_probs?: number; + readonly max_tool_calls?: number; + readonly parallel_tool_calls?: boolean; + readonly user?: string; + readonly reasoning_summary?: string; + readonly max_completion_tokens?: number; + readonly text_verbosity?: string; + // empty interface{} type, falling back to unknown + readonly prediction?: Record; + readonly store?: boolean; + // empty interface{} type, falling back to unknown + readonly metadata?: Record; + readonly prompt_cache_key?: string; + readonly safety_identifier?: string; + readonly service_tier?: string; + readonly structured_outputs?: boolean; + readonly strict_json_schema?: boolean; + readonly web_search_enabled?: boolean; + readonly search_context_size?: string; + readonly allowed_domains?: readonly string[]; +} + +// From codersdk/chats.go +/** + * ChatModelOpenRouterProvider configures OpenRouter routing preferences. + */ +export interface ChatModelOpenRouterProvider { + readonly order?: readonly string[]; + readonly allow_fallbacks?: boolean; + readonly require_parameters?: boolean; + readonly data_collection?: string; + readonly only?: readonly string[]; + readonly ignore?: readonly string[]; + readonly quantizations?: readonly string[]; + readonly sort?: string; +} + +// From codersdk/chats.go +/** + * ChatModelOpenRouterProviderOptions configures OpenRouter provider behavior. + */ +export interface ChatModelOpenRouterProviderOptions { + readonly reasoning?: ChatModelReasoningOptions; + // empty interface{} type, falling back to unknown + readonly extra_body?: Record; + readonly include_usage?: boolean; + readonly logit_bias?: Record; + readonly log_probs?: boolean; + readonly parallel_tool_calls?: boolean; + readonly user?: string; + readonly provider?: ChatModelOpenRouterProvider; +} + +// From codersdk/chats.go +export type ChatModelOverrideContext = + | "advisor" + | "compaction" + | "explore" + | "general" + | "title_generation"; + +export const ChatModelOverrideContexts: ChatModelOverrideContext[] = [ + "advisor", + "compaction", + "explore", + "general", + "title_generation", +]; + +// From codersdk/chats.go +/** + * ChatModelOverrideResponse is the response body for one chat model override. + */ +export interface ChatModelOverrideResponse { + readonly context: ChatModelOverrideContext; + readonly model_config_id: string; + readonly reasoning_effort?: string; +} + +// From codersdk/chats.go +/** + * ChatModelOverridesResponse is the response body for organization chat model overrides. + */ +export interface ChatModelOverridesResponse { + readonly overrides: readonly ChatModelOverrideResponse[]; +} + +// From codersdk/chats.go +/** + * ChatModelProviderDescriptor is the redacted view of an AI provider carried + * on the org model collection response. It carries only the capability + * metadata the Models UI needs; key material, base URLs, and headers are + * never exposed. The fields mirror the provider descriptors returned by the + * organization-scoped chat models collection. + */ +export interface ChatModelProviderDescriptor { + readonly id: string; + readonly type: string; + readonly display_name: string; + readonly icon: string; + readonly enabled: boolean; + readonly has_api_key: boolean; + readonly has_user_api_key: boolean; + readonly has_effective_api_key: boolean; + readonly allow_user_api_key: boolean; + readonly available: boolean; + readonly unavailable_reason?: ChatModelProviderUnavailableReason; +} + +// From codersdk/chats.go +/** + * ChatModelProviderOptions contains typed provider-specific options. + * + * Note: Azure models use the `openai` options shape. + * Note: Bedrock models use the `anthropic` options shape. + */ +export interface ChatModelProviderOptions { + readonly openai?: ChatModelOpenAIProviderOptions; + readonly anthropic?: ChatModelAnthropicProviderOptions; + readonly google?: ChatModelGoogleProviderOptions; + readonly openaicompat?: ChatModelOpenAICompatProviderOptions; + readonly openrouter?: ChatModelOpenRouterProviderOptions; + readonly vercel?: ChatModelVercelProviderOptions; +} + +// From codersdk/chats.go +export type ChatModelProviderUnavailableReason = + | "fetch_failed" + | "missing_api_key" + | "user_api_key_required"; + +export const ChatModelProviderUnavailableReasons: ChatModelProviderUnavailableReason[] = + ["fetch_failed", "missing_api_key", "user_api_key_required"]; + +// From codersdk/chats.go +/** + * ChatModelReasoningEffortConfig configures per-model reasoning effort + * bounds. When configured, Default and Max must both be provided before + * storing. + */ +export interface ChatModelReasoningEffortConfig { + readonly default?: string; + readonly max?: string; +} + +// From codersdk/chats.go +/** + * Reasoning effort levels, ordered low to high for clamping and comparison. + */ +export const ChatModelReasoningEffortHigh = "high"; + +// From codersdk/chats.go +/** + * Reasoning effort levels, ordered low to high for clamping and comparison. + */ +export const ChatModelReasoningEffortLow = "low"; + +// From codersdk/chats.go +/** + * Reasoning effort levels, ordered low to high for clamping and comparison. + */ +export const ChatModelReasoningEffortMax = "max"; + +// From codersdk/chats.go +/** + * Reasoning effort levels, ordered low to high for clamping and comparison. + */ +export const ChatModelReasoningEffortMedium = "medium"; + +// From codersdk/chats.go +/** + * Reasoning effort levels, ordered low to high for clamping and comparison. + */ +export const ChatModelReasoningEffortMinimal = "minimal"; + +// From codersdk/chats.go +/** + * Reasoning effort levels, ordered low to high for clamping and comparison. + */ +export const ChatModelReasoningEffortNone = "none"; + +// From codersdk/chats.go +/** + * Reasoning effort levels, ordered low to high for clamping and comparison. + */ +export const ChatModelReasoningEffortXHigh = "xhigh"; + +// From codersdk/chats.go +/** + * ChatModelReasoningOptions configures reasoning behavior for model + * providers that support it. + */ +export interface ChatModelReasoningOptions { + readonly enabled?: boolean; + readonly exclude?: boolean; + readonly max_tokens?: number; +} + +// From codersdk/chats.go +/** + * ChatModelVercelGatewayProviderOptions configures Vercel routing behavior. + */ +export interface ChatModelVercelGatewayProviderOptions { + readonly order?: readonly string[]; + readonly models?: readonly string[]; +} + +// From codersdk/chats.go +/** + * ChatModelVercelProviderOptions configures Vercel provider behavior. + */ +export interface ChatModelVercelProviderOptions { + readonly reasoning?: ChatModelReasoningOptions; + readonly providerOptions?: ChatModelVercelGatewayProviderOptions; + readonly user?: string; + readonly logit_bias?: Record; + readonly logprobs?: boolean; + readonly top_logprobs?: number; + readonly parallel_tool_calls?: boolean; + // empty interface{} type, falling back to unknown + readonly extra_body?: Record; +} + +// From codersdk/chats.go +/** + * ChatPersonalModelOverride is a resolved user personal model override. + */ +export interface ChatPersonalModelOverride { + readonly context: ChatPersonalModelOverrideContext; + readonly mode: ChatPersonalModelOverrideMode; + readonly model_config_id: string; + readonly reasoning_effort?: string; + readonly is_set: boolean; +} + +// From codersdk/chats.go +export type ChatPersonalModelOverrideContext = "explore" | "general" | "root"; + +export const ChatPersonalModelOverrideContexts: ChatPersonalModelOverrideContext[] = + ["explore", "general", "root"]; + +// From codersdk/chats.go +/** + * ChatPersonalModelOverrideDeploymentDefaults describes the deployment-level + * defaults used when a personal override selects deployment_default. + */ +export interface ChatPersonalModelOverrideDeploymentDefaults { + readonly general: ChatModelOverrideResponse; + readonly explore: ChatModelOverrideResponse; +} + +// From codersdk/chats.go +export type ChatPersonalModelOverrideMode = + | "chat_default" + | "deployment_default" + | "model"; + +export const ChatPersonalModelOverrideModes: ChatPersonalModelOverrideMode[] = [ + "chat_default", + "deployment_default", + "model", +]; + +// From codersdk/chats.go +/** + * ChatPersonalModelOverridesAdminSettings describes whether users may manage + * personal model override settings. + */ +export interface ChatPersonalModelOverridesAdminSettings { + readonly allow_users: boolean; +} + +// From codersdk/chats.go +export type ChatPlanMode = "plan"; + +// From codersdk/chats.go +/** + * ChatPlanModeInstructionsResponse is the response body for the + * plan mode instructions configuration endpoint. + */ +export interface ChatPlanModeInstructionsResponse { + readonly plan_mode_instructions: string; +} + +export const ChatPlanModes: ChatPlanMode[] = ["plan"]; + +// From codersdk/chats.go +/** + * ChatPrompt is a single user-authored prompt in a chat, returned by + * GET /api/v2/chats/{chat}/prompts. The text field contains + * the concatenated text payload of the underlying chat message; non-text + * parts (tool calls, files, attachments) are omitted by the server. + */ +export interface ChatPrompt { + readonly id: number; + readonly text: string; +} + +// From codersdk/chats.go +/** + * ChatPromptsOptions are optional query parameters for GetChatPrompts. + */ +export interface ChatPromptsOptions { + /** + * Limit caps the number of prompts returned. The server enforces a + * minimum of 1 and a maximum of 2000; passing 0 (or negative) + * applies the server-side default of 500. + */ + readonly Limit: number; +} + +// From codersdk/chats.go +/** + * ChatPromptsResponse is the payload of + * GET /api/v2/chats/{chat}/prompts. Prompts are returned + * newest first so the client can index directly into the slice for + * up/down arrow history cycling. + */ +export interface ChatPromptsResponse { + readonly prompts: readonly ChatPrompt[]; +} + +// From codersdk/chats.go +/** + * ChatProviderConfig is an admin-managed provider configuration. + */ +export interface ChatProviderConfig { + readonly id: string; + readonly provider: string; + readonly display_name: string; + readonly icon: string; + readonly enabled: boolean; + readonly has_api_key: boolean; + readonly central_api_key_enabled: boolean; + readonly allow_user_api_key: boolean; + readonly allow_central_api_key_fallback: boolean; + readonly base_url?: string; + readonly source: ChatProviderConfigSource; + readonly created_at?: string; + readonly updated_at?: string; +} + +// From codersdk/chats.go +export type ChatProviderConfigSource = "database" | "env_preset" | "supported"; + +export const ChatProviderConfigSources: ChatProviderConfigSource[] = [ + "database", + "env_preset", + "supported", +]; + +// From codersdk/chats.go +/** + * ChatQueuedMessage represents a queued message waiting to be processed. + */ +export interface ChatQueuedMessage { + readonly id: number; + readonly chat_id: string; + readonly model_config_id?: string; + readonly content: readonly ChatMessagePart[]; + readonly created_at: string; +} + +// From codersdk/chats.go +export interface ChatReasoningPart { + readonly type: "reasoning"; + readonly text: string; + /** + * CreatedAt is the timestamp this part carries. The semantics + * depend on the part type: for tool-call and tool-result parts + * it is the time the call was emitted or the result was + * produced (tool duration is the result's created_at minus the + * call's created_at); for reasoning parts it is the time + * reasoning started streaming. + */ + readonly created_at?: string; + /** + * CompletedAt is the time a reasoning part finished streaming, + * so reasoning duration can be computed as completed_at minus + * created_at. For interrupted reasoning, this is the + * interruption time. Absent when reasoning timestamp data was + * not recorded (e.g. messages persisted before this feature + * was added). + */ + readonly completed_at?: string; +} + +// From codersdk/chats.go +/** + * ChatRetentionDaysResponse contains the current chat retention setting. + */ +export interface ChatRetentionDaysResponse { + readonly retention_days: number; +} + +// From codersdk/chats.go +export type ChatRole = "" | "read"; + +export const ChatRoles: ChatRole[] = ["", "read"]; + +// From codersdk/chats.go +export interface ChatSkillPart { + readonly type: "skill"; + /** + * SkillName is the kebab-case name of a discovered skill + * from the workspace's .agents/skills/ directory. + */ + readonly skill_name: string; + /** + * SkillDescription is the short description from the skill's + * SKILL.md frontmatter. + */ + readonly skill_description?: string; +} + +// From codersdk/chats.go +export interface ChatSourcePart { + readonly type: "source"; + readonly url: string; + readonly source_id?: string; + readonly title?: string; +} + +// From codersdk/chats.go +export type ChatStatus = + | "error" + | "interrupting" + | "requires_action" + | "running" + | "waiting"; + +export const ChatStatuses: ChatStatus[] = [ + "error", + "interrupting", + "requires_action", + "running", + "waiting", +]; + +// From codersdk/chats.go +/** + * ChatStreamActionRequired is the payload of an action_required stream event. + */ +export interface ChatStreamActionRequired { + readonly tool_calls: readonly ChatStreamToolCall[]; +} + +// From codersdk/chats.go +/** + * ChatStreamEvent represents a real-time update for chat streaming. + */ +export interface ChatStreamEvent { + readonly type: ChatStreamEventType; + readonly chat_id: string; + readonly message?: ChatMessage; + readonly message_part?: ChatStreamMessagePart; + readonly status?: ChatStreamStatus; + readonly error?: ChatError; + readonly retry?: ChatStreamRetry; + readonly queued_messages?: readonly ChatQueuedMessage[]; + readonly action_required?: ChatStreamActionRequired; +} + +// From codersdk/chats.go +export type ChatStreamEventType = + | "action_required" + | "error" + | "history_reset" + | "message" + | "message_part" + | "preview_reset" + | "queue_update" + | "retry" + | "status"; + +export const ChatStreamEventTypes: ChatStreamEventType[] = [ + "action_required", + "error", + "history_reset", + "message", + "message_part", + "preview_reset", + "queue_update", + "retry", + "status", +]; + +// From codersdk/chats.go +/** + * ChatStreamMessagePart is a streamed message part update. + */ +export interface ChatStreamMessagePart { + readonly role?: ChatMessageRole; + readonly part: ChatMessagePart; + readonly history_version?: number; + readonly generation_attempt?: number; + readonly seq?: number; +} + +// From codersdk/chats.go +/** + * ChatStreamRetry represents an auto-retry status event in the stream. + * Published when the server automatically retries a failed LLM call. + */ +export interface ChatStreamRetry { + /** + * Attempt is the 1-indexed retry attempt number. + */ + readonly attempt: number; + /** + * DelayMs is the backoff delay in milliseconds before the retry. + */ + readonly delay_ms: number; + /** + * Error is the normalized error message from the failed attempt. + */ + readonly error: string; + /** + * Kind classifies the retry reason for consistent client rendering. + */ + readonly kind?: ChatErrorKind; + /** + * Provider identifies the upstream model provider when known. + */ + readonly provider?: string; + /** + * StatusCode is the best-effort upstream HTTP status code. + */ + readonly status_code?: number; + /** + * RetryingAt is the timestamp when the retry will be attempted. + */ + readonly retrying_at: string; +} + +// From codersdk/chats.go +/** + * ChatStreamStatus represents an updated chat status. + */ +export interface ChatStreamStatus { + readonly status: ChatStatus; +} + +// From codersdk/chats.go +/** + * ChatStreamToolCall describes a pending dynamic tool call that the client + * must execute. + */ +export interface ChatStreamToolCall { + readonly tool_call_id: string; + readonly tool_name: string; + readonly args: string; +} + +// From codersdk/chats.go +/** + * ChatSystemPromptResponse is the response body for the chat system prompt + * configuration endpoint. + */ +export interface ChatSystemPromptResponse { + readonly system_prompt: string; + readonly include_default_system_prompt: boolean; + readonly default_system_prompt: string; +} + +// From codersdk/chats.go +export interface ChatTextPart { + readonly type: "text"; + readonly text: string; +} + +// From codersdk/chats.go +export interface ChatToolCallPart { + readonly type: "tool-call"; + readonly tool_call_id?: string; + readonly tool_name?: string; + readonly mcp_server_config_id?: string; + readonly args?: Record; + readonly args_delta?: string; + /** + * ParsedCommands holds parsed programs from an execute tool call's + * shell command, one entry per simple command in source order. Each + * entry is [program] or [program, arg] where arg is the first non-flag + * positional argument. Program names are normalized to their base + * name (e.g. /usr/bin/go becomes go). Only populated when ToolName + * is "execute" and the command parses successfully; nil otherwise. + */ + readonly parsed_commands?: readonly string[][]; + /** + * ProviderExecuted indicates the tool call was executed by + * the provider (e.g. Anthropic computer use). + */ + readonly provider_executed?: boolean; + /** + * HookRewritten indicates that a lifecycle hook replaced model-proposed tool input. + */ + readonly hook_rewritten?: boolean; + /** + * CreatedAt is the timestamp this part carries. The semantics + * depend on the part type: for tool-call and tool-result parts + * it is the time the call was emitted or the result was + * produced (tool duration is the result's created_at minus the + * call's created_at); for reasoning parts it is the time + * reasoning started streaming. + */ + readonly created_at?: string; +} + +// From codersdk/chats.go +export interface ChatToolResultPart { + readonly type: "tool-result"; + readonly tool_call_id?: string; + readonly tool_name?: string; + readonly mcp_server_config_id?: string; + readonly result?: Record; + readonly result_delta?: string; + readonly result_reset?: boolean; + readonly is_error?: boolean; + readonly is_media?: boolean; + /** + * ProviderExecuted indicates the tool call was executed by + * the provider (e.g. Anthropic computer use). + */ + readonly provider_executed?: boolean; + /** + * CreatedAt is the timestamp this part carries. The semantics + * depend on the part type: for tool-call and tool-result parts + * it is the time the call was emitted or the result was + * produced (tool duration is the result's created_at minus the + * call's created_at); for reasoning parts it is the time + * reasoning started streaming. + */ + readonly created_at?: string; +} + +// From codersdk/chats.go +/** + * ChatUnsupportedProvider is a configured provider the Agents harness cannot + * use. + */ +export interface ChatUnsupportedProvider { + /** + * Provider is the provider type, e.g. "copilot". + */ + readonly provider: string; + readonly display_name: string; +} + +// From codersdk/chats.go +export interface ChatUser extends MinimalUser { + readonly role: ChatRole; +} + +// From codersdk/chats.go +/** + * ChatWatchEvent represents an event from the global chat watch stream. + * It delivers lifecycle events (created, status change, summary change, + * title change) for all of the authenticated user's chats. When Kind is + * ActionRequired, ToolCalls contains the pending dynamic tool + * invocations the client must execute and submit back. + */ +export interface ChatWatchEvent { + readonly kind: ChatWatchEventKind; + readonly chat: Chat; + readonly tool_calls?: readonly ChatStreamToolCall[]; +} + +// From codersdk/chats.go +export type ChatWatchEventKind = + | "action_required" + | "chat_summary_change" + | "context_dirty" + | "created" + | "deleted" + | "diff_status_change" + | "status_change" + | "summary_change" + | "title_change"; + +export const ChatWatchEventKinds: ChatWatchEventKind[] = [ + "action_required", + "chat_summary_change", + "context_dirty", + "created", + "deleted", + "diff_status_change", + "status_change", + "summary_change", + "title_change", +]; + +// From codersdk/chats.go +/** + * ChatWorkspaceTTLResponse is the response for getting the chat + * workspace TTL setting. + */ +export interface ChatWorkspaceTTLResponse { + /** + * WorkspaceTTLMillis is the workspace TTL in milliseconds. + * Zero means disabled; the template's own autostop setting applies. + */ + readonly workspace_ttl_ms: number; +} + +// From codersdk/deployment.go +export interface ClusterConfig { + readonly host: string; +} + +// From codersdk/client.go +/** + * CoderDesktopTelemetryHeader contains a JSON-encoded representation of Desktop telemetry + * fields, including device ID, OS, and Desktop version. + */ +export const CoderDesktopTelemetryHeader = "Coder-Desktop-Telemetry"; + +// From codersdk/disconnect.go +export type ConnectionDirection = + | "agent_to_client" + | "client_to_server" + | "server_to_agent"; + +export const ConnectionDirections: ConnectionDirection[] = [ + "agent_to_client", + "client_to_server", + "server_to_agent", +]; + +// From codersdk/insights.go +/** + * ConnectionLatency shows the latency for a connection. + */ +export interface ConnectionLatency { + readonly p50: number; + readonly p95: number; +} + +// From codersdk/connectionlog.go +export interface ConnectionLog { + readonly id: string; + readonly connect_time: string; + readonly organization: MinimalOrganization; + readonly workspace_owner_id: string; + readonly workspace_owner_username: string; + readonly workspace_id: string; + readonly workspace_name: string; + readonly agent_name: string; + readonly ip?: string; + readonly type: ConnectionType; + /** + * WebInfo is only set when `type` is one of: + * - `ConnectionTypePortForwarding` + * - `ConnectionTypeWorkspaceApp` + * - `ConnectionTypeTunnel` + */ + readonly web_info?: ConnectionLogWebInfo; + /** + * SSHInfo is only set when `type` is one of: + * - `ConnectionTypeSSH` + * - `ConnectionTypeReconnectingPTY` + * - `ConnectionTypeVSCode` + * - `ConnectionTypeJetBrains` + */ + readonly ssh_info?: ConnectionLogSSHInfo; +} + +// From codersdk/connectionlog.go +export interface ConnectionLogResponse { + readonly connection_logs: readonly ConnectionLog[]; + readonly count: number; + readonly count_cap: number; +} + +// From codersdk/connectionlog.go +export interface ConnectionLogSSHInfo { + readonly connection_id: string; + /** + * DisconnectTime is omitted if a disconnect event with the same connection ID + * has not yet been seen. + */ + readonly disconnect_time?: string; + /** + * DisconnectReason is omitted if a disconnect event with the same connection ID + * has not yet been seen. + */ + readonly disconnect_reason?: string; + /** + * ExitCode is the exit code of the SSH session. It is omitted if a + * disconnect event with the same connection ID has not yet been seen. + */ + readonly exit_code?: number; +} + +// From codersdk/connectionlog.go +export type ConnectionLogStatus = "completed" | "ongoing"; + +export const ConnectionLogStatuses: ConnectionLogStatus[] = [ + "completed", + "ongoing", +]; + +// From codersdk/connectionlog.go +export interface ConnectionLogWebInfo { + readonly user_agent: string; + /** + * User is omitted if the connection event was unauthenticated. + */ + readonly user: User | null; + readonly slug_or_port: string; + /** + * StatusCode is the HTTP status code or tunnel authorization outcome. + */ + readonly status_code: number; +} + +// From codersdk/connectionlog.go +export interface ConnectionLogsRequest extends Pagination { + readonly q?: string; +} + +// From codersdk/disconnect.go +export type ConnectionMethod = "derp" | "direct" | ""; + +export const ConnectionMethods: ConnectionMethod[] = ["derp", "direct", ""]; + +// From codersdk/connectionlog.go +export type ConnectionType = + | "jetbrains" + | "port_forwarding" + | "reconnecting_pty" + | "ssh" + | "tunnel" + | "vscode" + | "workspace_app"; + +export const ConnectionTypes: ConnectionType[] = [ + "jetbrains", + "port_forwarding", + "reconnecting_pty", + "ssh", + "tunnel", + "vscode", + "workspace_app", +]; + +// From codersdk/files.go +export const ContentTypeTar = "application/x-tar"; + +// From codersdk/files.go +export const ContentTypeZip = "application/zip"; + +// From codersdk/users.go +export interface ConvertLoginRequest { + /** + * ToType is the login type to convert to. + */ + readonly to_type: LoginType; + readonly password: string; +} + +// From codersdk/aigatewaykeys.go +/** + * CreateAIGatewayKeyRequest requests a new AI Gateway key. + */ +export interface CreateAIGatewayKeyRequest { + readonly name: string; +} + +// From codersdk/aigatewaykeys.go +/** + * CreateAIGatewayKeyResponse returns all key information. + * Key value is only returned here and cannot be recovered afterwards. + */ +export interface CreateAIGatewayKeyResponse { + readonly id: string; + readonly name: string; + readonly key: string; + readonly key_prefix: string; + readonly created_at: string; +} + +// From codersdk/aiproviders.go +/** + * CreateAIProviderRequest is the payload for creating a new AI + * provider. Name and Type are required. APIKeys carries the plaintext + * keys for OpenAI/Anthropic providers; Bedrock and Copilot providers + * must omit APIKeys (Bedrock authenticates via Settings, Copilot via + * request-time GitHub OAuth tokens). + */ +export interface CreateAIProviderRequest { + readonly type: AIProviderType; + readonly name: string; + readonly display_name?: string; + readonly icon?: string; + readonly enabled: boolean; + readonly base_url: string; + readonly api_keys?: readonly string[]; + readonly settings?: AIProviderSettings; +} + +// From codersdk/chats.go +/** + * CreateChatMessageRequest is the request to add a message to a chat. + */ +export interface CreateChatMessageRequest { + readonly content: readonly ChatInputPart[]; + readonly model_config_id?: string; + readonly mcp_server_ids?: string[]; + readonly busy_behavior?: ChatBusyBehavior; + /** + * PlanMode switches the chat's persistent plan mode. + * nil: no change, ptr to "plan": enable, ptr to "": clear. + */ + readonly plan_mode?: ChatPlanMode; + readonly reasoning_effort?: string; +} + +// From codersdk/chats.go +/** + * CreateChatMessageResponse is the response from adding a message to a chat. + */ +export interface CreateChatMessageResponse { + readonly message?: ChatMessage; + /** + * Messages contains all user-visible messages inserted by the send, in + * insertion order. A queued send on an errored chat may promote the + * previous queue head, so clients must upsert the full batch. + */ + readonly messages?: readonly ChatMessage[]; + readonly queued_message?: ChatQueuedMessage; + readonly queued: boolean; + readonly warnings?: readonly string[]; +} + +// From codersdk/chats.go +/** + * CreateChatModelRequest is the request body for an organization-scoped + * ChatModel. AIProviderID, Model, and a positive ContextLimit are required. + * Enabled defaults to true. IsDefault defaults to false when the organization + * already has a default model. The first model created in an organization is + * automatically promoted to default. CompressionThreshold defaults to 70. An + * omitted ModelConfig uses the provider defaults. + */ +export interface CreateChatModelRequest { + readonly ai_provider_id?: string; + readonly model: string; + readonly display_name?: string; + readonly enabled?: boolean; + readonly is_default?: boolean; + readonly context_limit?: number; + readonly compression_threshold?: number; + readonly model_config?: ChatModelCallConfig; +} + +// From codersdk/chats.go +/** + * CreateChatProviderConfigRequest creates a chat provider config. + */ +export interface CreateChatProviderConfigRequest { + readonly provider: string; + readonly display_name?: string; + readonly icon?: string; + readonly api_key?: string; + readonly base_url?: string; + readonly enabled?: boolean; + readonly central_api_key_enabled?: boolean; + readonly allow_user_api_key?: boolean; + readonly allow_central_api_key_fallback?: boolean; +} + +// From codersdk/chats.go +/** + * CreateChatRequest is the request to create a new chat. + */ +export interface CreateChatRequest { + readonly organization_id: string; + readonly content: readonly ChatInputPart[]; + readonly system_prompt?: string; + readonly workspace_id?: string; + readonly model_config_id?: string; + readonly reasoning_effort?: string; + readonly mcp_server_ids?: readonly string[]; + readonly labels?: Record; + /** + * UnsafeDynamicTools declares client-executed tools that the + * LLM can invoke. This API is highly experimental and highly + * subject to change. + */ + readonly unsafe_dynamic_tools?: readonly DynamicTool[]; + readonly plan_mode?: ChatPlanMode; + readonly client_type?: ChatClientType; +} + +// From codersdk/users.go +/** + * CreateFirstUserOnboardingInfo contains optional newsletter preference + * data collected during first user setup. + */ +export interface CreateFirstUserOnboardingInfo { + readonly newsletter_marketing: boolean; + readonly newsletter_releases: boolean; +} + +// From codersdk/users.go +export interface CreateFirstUserRequest { + readonly email: string; + readonly username: string; + readonly name: string; + readonly password: string; + readonly trial: boolean; + readonly trial_info: CreateFirstUserTrialInfo; + readonly onboarding_info?: CreateFirstUserOnboardingInfo; +} + +// From codersdk/users.go +/** + * CreateFirstUserResponse contains IDs for newly created user info. + */ +export interface CreateFirstUserResponse { + readonly user_id: string; + readonly organization_id: string; +} + +// From codersdk/users.go +export interface CreateFirstUserTrialInfo { + readonly first_name: string; + readonly last_name: string; + readonly phone_number: string; + readonly job_title: string; + readonly company_name: string; + readonly country: string; + readonly developers: string; +} + +// From codersdk/groups.go +export interface CreateGroupRequest { + readonly name: string; + readonly display_name: string; + readonly avatar_url: string; + readonly quota_allowance: number; +} + +// From codersdk/mcp.go +/** + * CreateMCPServerConfigRequest is the request to create a new MCP server config. + */ +export interface CreateMCPServerConfigRequest { + readonly display_name: string; + readonly slug: string; + readonly description: string; + readonly icon_url: string; + readonly transport: string; + readonly url: string; + readonly auth_type: string; + readonly oauth2_client_id?: string; + readonly oauth2_client_secret?: string; + readonly oauth2_auth_url?: string; + readonly oauth2_token_url?: string; + /** + * OAuth2RevocationURL is the provider's RFC 7009 revocation + * endpoint; auto-populated by OAuth2 discovery when omitted. + */ + readonly oauth2_revocation_url?: string; + readonly oauth2_scopes?: string; + readonly api_key_header?: string; + readonly api_key_value?: string; + readonly custom_headers?: Record; + readonly tool_allow_list?: readonly string[]; + readonly tool_deny_list?: readonly string[]; + readonly availability: string; + readonly enabled: boolean; + readonly model_intent: boolean; + readonly allow_in_plan_mode: boolean; + /** + * ForwardCoderHeaders, when true, forwards Coder identity + * headers on every outgoing MCP request. See MCPServerConfig. + */ + readonly forward_coder_headers: boolean; +} + +// From codersdk/organizations.go +export interface CreateOrganizationRequest { + readonly name: string; + /** + * DisplayName will default to the same value as `Name` if not provided. + */ + readonly display_name?: string; + readonly description?: string; + readonly icon?: string; +} + +// From codersdk/provisionerdaemons.go +export interface CreateProvisionerKeyRequest { + readonly name: string; + readonly tags: Record; +} + +// From codersdk/provisionerdaemons.go +export interface CreateProvisionerKeyResponse { + readonly key: string; +} + +// From codersdk/aitasks.go +/** + * CreateTaskRequest represents the request to create a new task. + */ +export interface CreateTaskRequest { + readonly template_version_id: string; + readonly template_version_preset_id?: string; + readonly input: string; + readonly name?: string; + readonly display_name?: string; +} + +// From codersdk/organizations.go +/** + * CreateTemplateRequest provides options when creating a template. + */ +export interface CreateTemplateRequest { + /** + * Name is the name of the template. + */ + readonly name: string; + /** + * DisplayName is the displayed name of the template. + */ + readonly display_name?: string; + /** + * Description is a description of what the template contains. It must be + * less than 128 bytes. + */ + readonly description?: string; + /** + * Icon is a relative path or external URL that specifies + * an icon to be displayed in the dashboard. + */ + readonly icon?: string; + /** + * VersionID is an in-progress or completed job to use as an initial version + * of the template. + * + * This is required on creation to enable a user-flow of validating a + * template works. There is no reason the data-model cannot support empty + * templates, but it doesn't make sense for users. + */ + readonly template_version_id: string; + /** + * DefaultTTLMillis allows optionally specifying the default TTL + * for all workspaces created from this template. + */ + readonly default_ttl_ms?: number; + /** + * ActivityBumpMillis allows optionally specifying the activity bump + * duration for all workspaces created from this template. Defaults to 1h + * but can be set to 0 to disable activity bumping. + */ + readonly activity_bump_ms?: number; + /** + * TimeTilAutostopNotifyMillis allows optionally specifying the duration + * before the autostop deadline at which a reminder notification is sent for + * workspaces created from this template. Defaults to 0 (disabled). + */ + readonly time_til_autostop_notify_ms?: number; + /** + * AutostopRequirement allows optionally specifying the autostop requirement + * for workspaces created from this template. This is an enterprise feature. + */ + readonly autostop_requirement?: TemplateAutostopRequirement; + /** + * AutostartRequirement allows optionally specifying the autostart allowed days + * for workspaces created from this template. This is an enterprise feature. + */ + readonly autostart_requirement?: TemplateAutostartRequirement; + /** + * Allow users to cancel in-progress workspace jobs. + * *bool as the default value is "true". + */ + readonly allow_user_cancel_workspace_jobs: boolean | null; + /** + * AllowUserAutostart allows users to set a schedule for autostarting their + * workspace. By default this is true. This can only be disabled when using + * an enterprise license. + */ + readonly allow_user_autostart?: boolean; + /** + * AllowUserAutostop allows users to set a custom workspace TTL to use in + * place of the template's DefaultTTL field. By default this is true. If + * false, the DefaultTTL will always be used. This can only be disabled when + * using an enterprise license. + */ + readonly allow_user_autostop?: boolean; + /** + * FailureTTLMillis allows optionally specifying the max lifetime before Coder + * stops all resources for failed workspaces created from this template. + */ + readonly failure_ttl_ms?: number; + /** + * TimeTilDormantMillis allows optionally specifying the max lifetime before Coder + * locks inactive workspaces created from this template. + */ + readonly dormant_ttl_ms?: number; + /** + * TimeTilDormantAutoDeleteMillis allows optionally specifying the max lifetime before Coder + * permanently deletes dormant workspaces created from this template. + */ + readonly delete_ttl_ms?: number; + /** + * DisableEveryoneGroupAccess allows optionally disabling the default + * behavior of granting the 'everyone' group access to use the template. + * If this is set to true, the template will not be available to all users, + * and must be explicitly granted to users or groups in the permissions settings + * of the template. + */ + readonly disable_everyone_group_access: boolean; + /** + * RequireActiveVersion mandates that workspaces are built with the active + * template version. + */ + readonly require_active_version: boolean; + /** + * MaxPortShareLevel allows optionally specifying the maximum port share level + * for workspaces created from the template. + */ + readonly max_port_share_level: WorkspaceAgentPortShareLevel | null; + /** + * UseClassicParameterFlow allows optionally specifying whether + * the template should use the classic parameter flow. The default if unset is + * true, and is why `*bool` is used here. When dynamic parameters becomes + * the default, this will default to false. + */ + readonly template_use_classic_parameter_flow?: boolean; + /** + * CORSBehavior allows optionally specifying the CORS behavior for all shared ports. + */ + readonly cors_behavior: CORSBehavior | null; + /** + * AgentsAllowed controls whether Coder Agents can create workspaces using + * this template. Defaults to true. + */ + readonly agents_allowed?: boolean; +} + +// From codersdk/templateversions.go +/** + * CreateTemplateVersionDryRunRequest defines the request parameters for + * CreateTemplateVersionDryRun. + */ +export interface CreateTemplateVersionDryRunRequest { + readonly workspace_name: string; + readonly rich_parameter_values: readonly WorkspaceBuildParameter[]; + readonly user_variable_values?: readonly VariableValue[]; +} + +// From codersdk/organizations.go +/** + * CreateTemplateVersionRequest enables callers to create a new Template Version. + */ +export interface CreateTemplateVersionRequest { + readonly name?: string; + readonly message?: string; + /** + * TemplateID optionally associates a version with a template. + */ + readonly template_id?: string; + readonly storage_method: ProvisionerStorageMethod; + readonly file_id?: string; + readonly example_id?: string; + readonly provisioner: ProvisionerType; + readonly tags: Record; + readonly user_variable_values?: readonly VariableValue[]; +} + +// From codersdk/audit.go +export interface CreateTestAuditLogRequest { + readonly action?: AuditAction; + readonly resource_type?: ResourceType; + readonly resource_id?: string; + readonly additional_fields?: Record; + readonly time?: string; + readonly build_reason?: BuildReason; + readonly organization_id?: string; + readonly request_id?: string; +} + +// From codersdk/apikey.go +export interface CreateTokenRequest { + readonly lifetime: number; + readonly scope?: APIKeyScope; // Deprecated: use Scopes instead. + readonly scopes?: readonly APIKeyScope[]; + readonly token_name: string; + readonly allow_list?: readonly APIAllowListTarget[]; +} + +// From codersdk/licenses.go +/** + * Defines the input payload for requesting a trial license. + */ +export interface CreateTrialLicenseRequest { + readonly email: string; + readonly first_name: string; + readonly last_name: string; + readonly phone_number: string; + readonly job_title: string; + readonly company_name: string; + readonly country: string; + readonly developers: string; + /** + * Source is the premium paywall the request came from, for telemetry. It + * is not forwarded to the licensor. Omit it to report "direct". + */ + readonly source?: PremiumFunnelSource; + /** + * AttributionID is the ID of the cta_click funnel event that led here, so + * that a signup can be joined back to the paywall that produced it. + */ + readonly attribution_id?: string; +} + +// From codersdk/chats.go +/** + * CreateUserAIProviderKeyRequest creates or replaces a user's API key + * for an AI provider. + */ +export interface CreateUserAIProviderKeyRequest { + readonly api_key: string; +} + +// From codersdk/chats.go +/** + * CreateUserChatProviderKeyRequest creates or replaces a user's API key + * for a provider. + */ +export interface CreateUserChatProviderKeyRequest { + readonly api_key: string; +} + +// From codersdk/users.go +export interface CreateUserRequestWithOrgs { + readonly email: string; + readonly username: string; + readonly name: string; + readonly password: string; + /** + * UserLoginType defaults to LoginTypePassword. + */ + readonly login_type: LoginType; + /** + * UserStatus defaults to UserStatusDormant. + */ + readonly user_status: UserStatus | null; + /** + * OrganizationIDs is a list of organization IDs that the user should be a member of. + */ + readonly organization_ids: readonly string[]; + /** + * Service accounts are admin-managed accounts that cannot login. + */ + readonly service_account?: boolean; + /** + * Roles is an optional list of site-level roles to assign at creation. + */ + readonly roles?: readonly string[]; +} + +// From codersdk/usersecrets.go +/** + * CreateUserSecretRequest is the payload for creating a new user + * secret. Name and Value are required. An enabled secret must have at + * least one of EnvName or FilePath non-empty so it has an injection + * target; to keep a secret without injecting it, set Enabled to false. + * All other fields are optional and default to empty string. Enabled + * defaults to true when omitted. + */ +export interface CreateUserSecretRequest { + readonly name: string; + readonly value: string; + readonly description?: string; + readonly env_name?: string; + readonly file_path?: string; + readonly enabled?: boolean; +} + +// From codersdk/userskills.go +/** + * CreateUserSkillRequest is the payload for creating a user skill. + */ +export interface CreateUserSkillRequest { + /** + * Content must be SKILL.md-format Markdown with YAML frontmatter. The + * frontmatter must include name, may include description, and must be + * followed by a non-empty body. + */ + readonly content: string; +} + +// From codersdk/workspaces.go +/** + * CreateWorkspaceBuildOnSuccessRequest queues a follow-up build that + * runs after the parent build succeeds. It currently supports + * restarting a workspace: the parent build must be a "stop" and this + * child build a "start". The child build inherits LogLevel and Reason + * from the parent CreateWorkspaceBuildRequest. + */ +export interface CreateWorkspaceBuildOnSuccessRequest { + /** + * TemplateVersionID pins the child build to a specific template + * version. Pinning requires permission to update the template, + * since the active version may change before the child build + * runs. When empty, the child build uses the template's active + * version at the time it runs. + */ + readonly template_version_id?: string; + /** + * Transition must be "start". The parent build's transition must + * be "stop". + */ + readonly transition: WorkspaceTransition; + /** + * RichParameterValues are applied to the child build. Parameters + * not listed here fall back to their values from the previous + * build, matching normal build behavior. + */ + readonly rich_parameter_values?: readonly WorkspaceBuildParameter[]; + /** + * TemplateVersionPresetID selects a preset for the child build. + * It requires TemplateVersionID to also be set. + */ + readonly template_version_preset_id?: string; +} + +// From codersdk/workspaces.go +export type CreateWorkspaceBuildReason = + | "cli" + | "dashboard" + | "jetbrains_connection" + | "ssh_connection" + | "task_manual_pause" + | "task_resume" + | "vscode_connection"; + +export const CreateWorkspaceBuildReasons: CreateWorkspaceBuildReason[] = [ + "cli", + "dashboard", + "jetbrains_connection", + "ssh_connection", + "task_manual_pause", + "task_resume", + "vscode_connection", +]; + +// From codersdk/workspaces.go +/** + * CreateWorkspaceBuildRequest provides options to update the latest workspace build. + */ +export interface CreateWorkspaceBuildRequest { + readonly template_version_id?: string; + readonly transition: WorkspaceTransition; + readonly dry_run?: boolean; + readonly state?: string; + /** + * Orphan may be set for the Destroy transition. + */ + readonly orphan?: boolean; + /** + * ParameterValues are optional. It will write params to the 'workspace' scope. + * This will overwrite any existing parameters with the same name. + * This will not delete old params not included in this list. + */ + readonly rich_parameter_values?: readonly WorkspaceBuildParameter[]; + /** + * Log level changes the default logging verbosity of a provider ("info" if empty). + */ + readonly log_level?: ProvisionerLogLevel; + /** + * TemplateVersionPresetID is the ID of the template version preset to use for the build. + */ + readonly template_version_preset_id?: string; + /** + * Reason sets the reason for the workspace build. + */ + readonly reason?: CreateWorkspaceBuildReason; + /** + * OnSuccess queues a follow-up workspace build after this build succeeds. + * It currently supports restarting a workspace by starting it after a + * successful stop build. + */ + readonly on_success?: CreateWorkspaceBuildOnSuccessRequest; +} + +// From codersdk/workspaceproxy.go +export interface CreateWorkspaceProxyRequest { + readonly name: string; + readonly display_name: string; + readonly icon: string; +} + +// From codersdk/organizations.go +/** + * CreateWorkspaceRequest provides options for creating a new workspace. + * Either TemplateID or TemplateVersionID must be specified. They cannot both be present. + * @Description CreateWorkspaceRequest provides options for creating a new workspace. + * @Description Only one of TemplateID or TemplateVersionID can be specified, not both. + * @Description If TemplateID is specified, the active version of the template will be used. + * @Description Workspace names: + * @Description - Must start with a letter or number + * @Description - Can only contain letters, numbers, and hyphens + * @Description - Cannot contain spaces or special characters + * @Description - Cannot be named `new` or `create` + * @Description - Must be unique within your workspaces + * @Description - Maximum length of 32 characters + */ +export interface CreateWorkspaceRequest { + /** + * TemplateID specifies which template should be used for creating the workspace. + */ + readonly template_id?: string; + /** + * TemplateVersionID can be used to specify a specific version of a template for creating the workspace. + */ + readonly template_version_id?: string; + readonly name: string; + readonly autostart_schedule?: string; + readonly ttl_ms?: number; + /** + * RichParameterValues allows for additional parameters to be provided + * during the initial provision. + */ + readonly rich_parameter_values?: readonly WorkspaceBuildParameter[]; + readonly automatic_updates?: AutomaticUpdates; + readonly template_version_preset_id?: string; +} + +// From codersdk/deployment.go +export interface CryptoKey { + readonly feature: CryptoKeyFeature; + readonly secret: string; + readonly deletes_at: string; + readonly sequence: number; + readonly starts_at: string; +} + +// From codersdk/deployment.go +export type CryptoKeyFeature = + | "chat_files_token" + | "nats_ca" + | "oidc_convert" + | "tailnet_resume" + | "workspace_apps_api_key" + | "workspace_apps_token"; + +export const CryptoKeyFeatures: CryptoKeyFeature[] = [ + "chat_files_token", + "nats_ca", + "oidc_convert", + "tailnet_resume", + "workspace_apps_api_key", + "workspace_apps_token", +]; + +// From codersdk/notifications.go +export interface CustomNotificationContent { + readonly title: string; + readonly message: string; +} + +// From codersdk/notifications.go +export interface CustomNotificationRequest { + readonly content: CustomNotificationContent | null; +} + +// From codersdk/roles.go +/** + * CustomRoleRequest is used to edit custom roles. + */ +export interface CustomRoleRequest { + readonly name: string; + readonly display_name: string; + readonly site_permissions: readonly Permission[]; + readonly user_permissions: readonly Permission[]; + /** + * OrganizationPermissions are specific to the organization the role belongs to. + */ + readonly organization_permissions: readonly Permission[]; + /** + * OrganizationMemberPermissions are specific to the organization the role belongs to. + */ + readonly organization_member_permissions: readonly Permission[]; +} + +// From codersdk/deployment.go +export interface DAUEntry { + /** + * Date is a string formatted as 2024-01-31. + * Timezone and time information is not included. + */ + readonly date: string; + readonly amount: number; +} + +// From codersdk/deployment.go +export interface DAURequest { + readonly TZHourOffset: number; +} + +// From codersdk/deployment.go +export interface DAUsResponse { + readonly entries: readonly DAUEntry[]; + readonly tz_hour_offset: number; +} + +// From codersdk/deployment.go +export interface DERP { + readonly server: DERPServerConfig; + readonly config: DERPConfig; +} + +// From codersdk/deployment.go +export interface DERPConfig { + readonly block_direct: boolean; + readonly force_websockets: boolean; + readonly url: string; + readonly path: string; +} + +// From healthsdk/healthsdk.go +/** + * DERPHealthReport includes health details of each configured DERP/STUN region. + */ +export interface DERPHealthReport extends BaseReport { + /** + * Healthy is deprecated and left for backward compatibility purposes, use `Severity` instead. + */ + readonly healthy: boolean; + readonly regions: Record; + readonly netcheck?: NetcheckReport; + readonly netcheck_err?: string; + readonly netcheck_logs: readonly string[]; +} + +// From healthsdk/healthsdk.go +/** + * DERPHealthReport includes health details of a single node in a single region. + */ +export interface DERPNodeReport { + /** + * Healthy is deprecated and left for backward compatibility purposes, use `Severity` instead. + */ + readonly healthy: boolean; + readonly severity: HealthSeverity; + readonly warnings: readonly HealthMessage[]; + readonly error?: string; + readonly node: TailDERPNode | null; + readonly node_info: ServerInfoMessage; + readonly can_exchange_messages: boolean; + readonly round_trip_ping: string; + readonly round_trip_ping_ms: number; + readonly uses_websocket: boolean; + readonly client_logs: readonly string[][]; + readonly client_errs: readonly string[][]; + readonly stun: STUNReport; +} + +// From codersdk/workspaceagents.go +export interface DERPRegion { + readonly preferred: boolean; + readonly latency_ms: number; +} + +// From healthsdk/healthsdk.go +/** + * DERPHealthReport includes health details of each node in a single region. + */ +export interface DERPRegionReport { + /** + * Healthy is deprecated and left for backward compatibility purposes, use `Severity` instead. + */ + readonly healthy: boolean; + readonly severity: HealthSeverity; + readonly warnings: readonly HealthMessage[]; + readonly error?: string; + readonly region: TailDERPRegion | null; + readonly node_reports: readonly DERPNodeReport[]; +} + +// From codersdk/deployment.go +export interface DERPServerConfig { + readonly enable: boolean; + readonly region_id: number; + readonly region_code: string; + readonly region_name: string; + readonly stun_addresses: string; + readonly relay_url: string; +} + +// From codersdk/deployment.go +export interface DangerousConfig { + readonly allow_path_app_sharing: boolean; + readonly allow_path_app_site_owner_access: boolean; + readonly allow_all_cors: boolean; +} + +// From codersdk/database.go +export const DatabaseNotReachable = "database not reachable"; + +// From healthsdk/healthsdk.go +/** + * DatabaseReport shows the results of pinging the configured database.Conn. + */ +export interface DatabaseReport extends BaseReport { + /** + * Healthy is deprecated and left for backward compatibility purposes, use `Severity` instead. + */ + readonly healthy: boolean; + readonly reachable: boolean; + readonly latency: string; + readonly latency_ms: number; + readonly threshold_ms: number; +} + +// From codersdk/debug.go +/** + * DebugProfileOptions are options for collecting debug profiles from the + * server via the consolidated /debug/profile endpoint. + */ +export interface DebugProfileOptions { + /** + * Duration controls how long time-based profiles (cpu, trace) run. + * Zero uses the server default (10s). + */ + readonly Duration: number; + /** + * Profiles is the list of profile types to collect. Nil or empty uses + * the server default (cpu, heap, allocs, block, mutex, goroutine). + */ + readonly Profiles: readonly string[]; +} + +// From codersdk/chats.go +/** + * DefaultChatAutoArchiveDays is the default auto-archive window, in + * days, applied when no site config row exists. Zero disables + * auto-archival. + */ +export const DefaultChatAutoArchiveDays = 0; + +// From codersdk/chats.go +/** + * DefaultChatDebugRetentionDays is the default chat debug run retention + * window, in days, applied when no site config row exists. Set the + * config value to zero to disable the purge. + */ +export const DefaultChatDebugRetentionDays = 30; + +// From codersdk/chats.go +/** + * DefaultChatWorkspaceTTL is the default TTL for chat workspaces. + * Zero means disabled; the template's own autostop setting applies. + */ +export const DefaultChatWorkspaceTTL = 0; + +// From codersdk/externalauth.go +export interface DeleteExternalAuthByIDResponse { + /** + * TokenRevoked set to true if token revocation was attempted and was successful + */ + readonly token_revoked: boolean; + readonly token_revocation_error?: string; +} + +// From codersdk/notifications.go +export interface DeleteWebpushSubscription { + readonly endpoint: string; +} + +// From codersdk/workspaceagentportshare.go +export interface DeleteWorkspaceAgentPortShareRequest { + readonly agent_name: string; + readonly port: number; +} + +// From codersdk/deployment.go +/** + * DeploymentConfig contains both the deployment values and how they're set. + */ +export interface DeploymentConfig { + readonly config?: DeploymentValues; + readonly options?: SerpentOptionSet; +} + +// From codersdk/deployment.go +export interface DeploymentStats { + /** + * AggregatedFrom is the time in which stats are aggregated from. + * This might be back in time a specific duration or interval. + */ + readonly aggregated_from: string; + /** + * CollectedAt is the time in which stats are collected at. + */ + readonly collected_at: string; + /** + * NextUpdateAt is the time when the next batch of stats will + * be updated. + */ + readonly next_update_at: string; + readonly workspaces: WorkspaceDeploymentStats; + readonly session_count: SessionCountDeploymentStats; +} + +// From codersdk/deployment.go +/** + * DeploymentValues is the central configuration values the coder server. + */ +export interface DeploymentValues { + readonly verbose?: boolean; + readonly access_url?: string; + readonly wildcard_access_url?: string; + readonly docs_url?: string; + readonly redirect_to_access_url?: boolean; + /** + * HTTPAddress is a string because it may be set to zero to disable. + */ + readonly http_address?: string; + readonly autobuild_poll_interval?: number; + readonly job_hang_detector_interval?: number; + readonly cluster?: ClusterConfig; + readonly derp?: DERP; + readonly prometheus?: PrometheusConfig; + readonly pprof?: PprofConfig; + readonly proxy_trusted_headers?: string; + readonly proxy_trusted_origins?: string; + readonly cache_directory?: string; + readonly ephemeral_deployment?: boolean; + readonly pg_connection_url?: string; + readonly pg_auth?: string; + readonly pg_conn_max_open?: number; + readonly pg_conn_max_idle?: string; + readonly oauth2?: OAuth2Config; + readonly oidc?: OIDCConfig; + readonly telemetry?: TelemetryConfig; + readonly tls?: TLSConfig; + readonly trace?: TraceConfig; + readonly http_cookies?: HTTPCookieConfig; + readonly strict_transport_security?: number; + readonly strict_transport_security_options?: string; + readonly ssh_keygen_algorithm?: string; + readonly metrics_cache_refresh_interval?: number; + readonly agent_stat_refresh_interval?: number; + readonly agent_fallback_troubleshooting_url?: string; + readonly browser_only?: boolean; + readonly scim_api_key?: string; + readonly scim_use_legacy?: boolean; + readonly external_token_encryption_keys?: string; + readonly provisioner?: ProvisionerConfig; + readonly rate_limit?: RateLimitConfig; + readonly experiments?: string; + readonly update_check?: boolean; + readonly swagger?: SwaggerConfig; + readonly logging?: LoggingConfig; + readonly dangerous?: DangerousConfig; + readonly disable_path_apps?: boolean; + readonly session_lifetime?: SessionLifetime; + readonly disable_password_auth?: boolean; + readonly support?: SupportConfig; + readonly enable_authz_recording?: boolean; + readonly external_auth?: SerpentStruct; + readonly external_auth_github_default_provider_enable?: boolean; + readonly config_ssh?: SSHConfig; + readonly wgtunnel_host?: string; + readonly disable_owner_workspace_exec?: boolean; + readonly disable_workspace_sharing?: boolean; + readonly disable_chat_sharing?: boolean; + readonly proxy_health_status_interval?: number; + readonly enable_terraform_debug_mode?: boolean; + readonly user_quiet_hours_schedule?: UserQuietHoursScheduleConfig; + readonly web_terminal_renderer?: string; + readonly allow_workspace_renames?: boolean; + readonly healthcheck?: HealthcheckConfig; + readonly retention?: RetentionConfig; + readonly cli_upgrade_message?: string; + readonly terms_of_service_url?: string; + readonly notifications?: NotificationsConfig; + readonly additional_csp_policy?: string; + readonly workspace_hostname_suffix?: string; + readonly workspace_prebuilds?: PrebuildsConfig; + readonly enable_ai_tasks?: boolean; + readonly ai?: AIConfig; + readonly stats_collection?: StatsCollectionConfig; + readonly template_builder?: TemplateBuilderConfig; + readonly config?: string; + readonly write_config?: boolean; + /** + * @deprecated Use HTTPAddress or TLS.Address instead. + */ + readonly address?: string; +} + +// From codersdk/parameters.go +export interface DiagnosticExtra { + readonly code: string; +} + +// From codersdk/parameters.go +export type DiagnosticSeverityString = "error" | "warning"; + +export const DiagnosticSeverityStrings: DiagnosticSeverityString[] = [ + "error", + "warning", +]; + +// From codersdk/disconnect.go +export type DisconnectInitiator = + | "agent" + | "client" + | "network" + | "server" + | ""; + +export const DisconnectInitiators: DisconnectInitiator[] = [ + "agent", + "client", + "network", + "server", + "", +]; + +// From codersdk/disconnect.go +export type DisconnectReason = + | "client_closed" + | "control_plane_lost" + | "graceful" + | "network_error" + | "protocol_error" + | "server_shutdown" + | "" + | "workspace_stopped"; + +export const DisconnectReasons: DisconnectReason[] = [ + "client_closed", + "control_plane_lost", + "graceful", + "network_error", + "protocol_error", + "server_shutdown", + "", + "workspace_stopped", +]; + +// From codersdk/workspaceagents.go +export type DisplayApp = + | "port_forwarding_helper" + | "ssh_helper" + | "vscode" + | "vscode_insiders" + | "web_terminal"; + +export const DisplayApps: DisplayApp[] = [ + "port_forwarding_helper", + "ssh_helper", + "vscode", + "vscode_insiders", + "web_terminal", +]; + +// From codersdk/parameters.go +export interface DynamicParametersRequest { + /** + * ID identifies the request. The response contains the same + * ID so that the client can match it to the request. + */ + readonly id: number; + readonly inputs: Record; + /** + * OwnerID if uuid.Nil, it defaults to `codersdk.Me` + */ + readonly owner_id?: string; +} + +// From codersdk/parameters.go +export interface DynamicParametersResponse { + readonly id: number; + readonly diagnostics: readonly FriendlyDiagnostic[]; + readonly parameters: readonly PreviewParameter[]; +} + +// From codersdk/chats.go +/** + * DynamicTool describes a client-declared tool definition. On the + * client side, the Handler callback executes the tool when the LLM + * invokes it. On the server side, only Name, Description, and + * InputSchema are used (Handler is not serialized). + */ +export interface DynamicTool { + readonly name: string; + readonly description?: string; + /** + * InputSchema's JSON key "input_schema" uses snake_case for + * SDK consistency, deviating from the camelCase "inputSchema" + * convention used by MCP. + */ + readonly input_schema: Record; +} + +// From codersdk/chats.go +/** + * DynamicToolCall represents a pending tool invocation from the + * chat stream that the client must execute and submit back. + */ +export interface DynamicToolCall { + readonly tool_call_id: string; + readonly tool_name: string; + readonly args: string; +} + +// From codersdk/chats.go +/** + * DynamicToolResponse holds the output of a dynamic tool + * execution. IsError indicates a tool-level error the LLM + * should see, as opposed to an infrastructure failure + * (returned as the error return value). + */ +export interface DynamicToolResponse { + readonly content: string; + readonly is_error: boolean; +} + +// From codersdk/chats.go +/** + * EditChatMessageRequest is the request to edit a user message in a chat. + */ +export interface EditChatMessageRequest { + readonly content: readonly ChatInputPart[]; + /** + * ModelConfigID, when set, overrides the model used for the + * replacement user message and the assistant turn that follows. + * When nil the original message's model is preserved. + */ + readonly model_config_id?: string; + readonly reasoning_effort?: string; + /** + * MCPServerIDs, when set, replaces the chat's MCP server selection + * before the replacement turn runs. When nil the current selection + * is preserved. + */ + readonly mcp_server_ids?: string[]; +} + +// From codersdk/chats.go +/** + * EditChatMessageResponse is the response from editing a message in a chat. + */ +export interface EditChatMessageResponse { + readonly message: ChatMessage; + /** + * Messages holds every user-visible message inserted by the edit, in + * insertion order. Hook-generated suffix messages may follow Message, + * so clients must upsert the full batch. + */ + readonly messages?: readonly ChatMessage[]; + /** + * DeletedMessageIDs holds the IDs of previously visible messages the + * edit removed, including stale hook notices from the edited turn. + * Clients should drop them from local caches. + */ + readonly deleted_message_ids?: readonly number[]; + readonly warnings?: readonly string[]; +} + +// From codersdk/externalauth.go +export type EnhancedExternalAuthProvider = + | "azure-devops" + | "azure-devops-entra" + | "bitbucket-cloud" + | "bitbucket-server" + | "github" + | "gitlab" + | "gitea" + | "jfrog" + | "slack"; + +export const EnhancedExternalAuthProviders: EnhancedExternalAuthProvider[] = [ + "azure-devops", + "azure-devops-entra", + "bitbucket-cloud", + "bitbucket-server", + "github", + "gitlab", + "gitea", + "jfrog", + "slack", +]; + +// From codersdk/deployment.go +export type Entitlement = "entitled" | "grace_period" | "not_entitled"; + +// From codersdk/deployment.go +export interface Entitlements { + readonly features: Record; + readonly warnings: readonly string[]; + readonly errors: readonly string[]; + readonly has_license: boolean; + readonly trial: boolean; + readonly require_telemetry: boolean; + readonly refreshed_at: string; +} + +// From codersdk/client.go +/** + * EntitlementsWarnings contains active warnings for the user's entitlements. + */ +export const EntitlementsWarningHeader = "X-Coder-Entitlements-Warning"; + +// From codersdk/deployment.go +export type Experiment = + | "ai-gateway-seat-exclusion" + | "agent-lifecycle-hooks" + | "auto-fill-parameters" + | "chat-advisor" + | "chat-virtual-desktop" + | "example" + | "mcp-server-http" + | "mcp-tool-search" + | "nats_pubsub" + | "notifications" + | "oauth2" + | "workspace-build-updates" + | "workspace-capable-licensing" + | "workspace-usage"; + +export const Experiments: Experiment[] = [ + "ai-gateway-seat-exclusion", + "agent-lifecycle-hooks", + "auto-fill-parameters", + "chat-advisor", + "chat-virtual-desktop", + "example", + "mcp-server-http", + "mcp-tool-search", + "nats_pubsub", + "notifications", + "oauth2", + "workspace-build-updates", + "workspace-capable-licensing", + "workspace-usage", +]; + +// From codersdk/scopes_catalog.go +export interface ExternalAPIKeyScopes { + readonly external: readonly APIKeyScope[]; +} + +// From codersdk/workspaces.go +/** + * ExternalAgentCredentials contains the credentials needed for an external agent to connect to Coder. + */ +export interface ExternalAgentCredentials { + readonly command: string; + readonly agent_token: string; +} + +// From codersdk/externalauth.go +export interface ExternalAuth { + readonly authenticated: boolean; + readonly device: boolean; + readonly display_name: string; + readonly supports_revocation: boolean; + /** + * User is the user that authenticated with the provider. + */ + readonly user: ExternalAuthUser | null; + /** + * AppInstallable is true if the request for app installs was successful. + */ + readonly app_installable: boolean; + /** + * AppInstallations are the installations that the user has access to. + */ + readonly installations: readonly ExternalAuthAppInstallation[]; + /** + * AppInstallURL is the URL to install the app. + */ + readonly app_install_url: string; +} + +// From codersdk/externalauth.go +export interface ExternalAuthAppInstallation { + readonly id: number; + readonly account: ExternalAuthUser; + readonly configure_url: string; +} + +// From codersdk/deployment.go +export interface ExternalAuthConfig { + /** + * Type is the type of external auth config. + */ + readonly type: string; + readonly client_id: string; + /** + * ID is a unique identifier for the auth config. + * It defaults to `type` when not provided. + */ + readonly id: string; + readonly auth_url: string; + readonly token_url: string; + readonly validate_url: string; + /** + * RedirectURL is optional, defaulting to 'ACCESS_URL'. Only useful in niche + * situations where the OAuth callback domain is different from the ACCESS_URL + * domain. The path component is ignored. + */ + readonly redirect_url: string; + readonly revoke_url: string; + readonly app_install_url: string; + readonly app_installations_url: string; + readonly no_refresh: boolean; + readonly scopes: readonly string[]; + readonly device_flow: boolean; + readonly device_code_url: string; + /** + * @deprecated Injected MCP in AI Bridge is deprecated and will be removed in a future release. + */ + readonly mcp_url: string; + /** + * @deprecated Injected MCP in AI Bridge is deprecated and will be removed in a future release. + */ + readonly mcp_tool_allow_regex: string; + /** + * @deprecated Injected MCP in AI Bridge is deprecated and will be removed in a future release. + */ + readonly mcp_tool_deny_regex: string; + /** + * Regex allows API requesters to match an auth config by + * a string (e.g. coder.com) instead of by it's type. + * + * Git clone makes use of this by parsing the URL from: + * 'Username for "https://github.com":' + * And sending it to the Coder server to match against the Regex. + */ + readonly regex: string; + /** + * APIBaseURL is the base URL for provider REST API calls + * (e.g., "https://api.github.com" for GitHub). Derived from + * defaults when not explicitly configured. + */ + readonly api_base_url: string; + /** + * DisplayName is shown in the UI to identify the auth config. + */ + readonly display_name: string; + /** + * DisplayIcon is a URL to an icon to display in the UI. + */ + readonly display_icon: string; + /** + * CodeChallengeMethodsSupported lists the PKCE code challenge methods + * The only one supported by Coder is "S256". + */ + readonly code_challenge_methods_supported: readonly string[]; +} + +// From codersdk/externalauth.go +/** + * ExternalAuthDevice is the response from the device authorization endpoint. + * See: https://tools.ietf.org/html/rfc8628#section-3.2 + */ +export interface ExternalAuthDevice { + readonly device_code: string; + readonly user_code: string; + readonly verification_uri: string; + readonly expires_in: number; + readonly interval: number; +} + +// From codersdk/externalauth.go +export interface ExternalAuthDeviceExchange { + readonly device_code: string; +} + +// From codersdk/externalauth.go +/** + * ExternalAuthLink is a link between a user and an external auth provider. + * It excludes information that requires a token to access, so can be statically + * built from the database and configs. + */ +export interface ExternalAuthLink { + readonly provider_id: string; + readonly created_at: string; + readonly updated_at: string; + readonly has_refresh_token: boolean; + readonly expires: string; + readonly authenticated: boolean; + readonly validate_error: string; +} + +// From codersdk/externalauth.go +/** + * ExternalAuthLinkProvider are the static details of a provider. + */ +export interface ExternalAuthLinkProvider { + readonly id: string; + readonly type: string; + readonly device: boolean; + readonly display_name: string; + readonly display_icon: string; + readonly allow_refresh: boolean; + readonly allow_validate: boolean; + readonly supports_revocation: boolean; + readonly code_challenge_methods_supported: readonly string[]; +} + +// From codersdk/externalauth.go +export interface ExternalAuthUser { + readonly id: number; + readonly login: string; + readonly avatar_url: string; + readonly profile_url: string; + readonly name: string; +} + +// From codersdk/deployment.go +export interface Feature { + readonly entitlement: Entitlement; + readonly enabled: boolean; + /** + * Limit is the maximum value the license grants for the feature, in the + * feature's own unit. For FeatureAgentRuntimeHours, an enabled feature + * with Limit omitted means the license grants unlimited runtime hours. + */ + readonly limit?: number; + /** + * SoftLimit is the advisory warning threshold that accompanies Limit for + * features whose license carries it. For these features, Limit carries + * the purchased allocation; an unlimited allocation has no thresholds, + * so SoftLimit is omitted alongside the omitted Limit. Only + * FeatureAgentRuntimeHours sets this field. + */ + readonly soft_limit?: number; + /** + * HardLimit is the enforcement threshold that accompanies Limit for + * features whose license carries it. See SoftLimit for the set of + * features that use these thresholds. + */ + readonly hard_limit?: number; + /** + * Actual is the usage measured against Limit, when known: a + * point-in-time count for most features, or usage accumulated over + * UsagePeriod for features that set one. Its unit matches Limit's; + * FeatureAgentRuntimeHours reports whole hours floored from the + * recorded milliseconds, with the precise value available in + * ActualMs. FeatureAgentRuntimeHours usage can trail by roughly one + * hour because the current hour is not emitted, plus the entitlement + * refresh interval. + */ + readonly actual?: number; + /** + * ActualMs is the precise usage backing Actual, in milliseconds, for + * features measured in time. It has the same freshness as Actual. + * Only FeatureAgentRuntimeHours sets this field. + */ + readonly actual_ms?: number; + /** + * UsagePeriod denotes that the usage is a counter that accumulates over + * this period (and most likely resets with the issuance of the next + * license). These dates are determined from the license that this + * entitlement comes from, see enterprise/coderd/license/license.go. + * Only FeatureManagedAgentLimit and FeatureAgentRuntimeHours set this + * field. + */ + readonly usage_period?: UsagePeriod; +} + +// From codersdk/deployment.go +export type FeatureName = + | "aibridge" + | "ai_governance_user_limit" + | "access_control" + | "advanced_template_scheduling" + | "agent_runtime_hours" + | "appearance" + | "audit_log" + | "boundary" + | "browser_only" + | "connection_log" + | "control_shared_ports" + | "custom_roles" + | "external_provisioner_daemons" + | "external_token_encryption" + | "high_availability" + | "managed_agent_limit" + | "multiple_external_auth" + | "multiple_organizations" + | "scim" + | "service_accounts" + | "task_batch_actions" + | "template_rbac" + | "user_limit" + | "user_role_management" + | "workspace_batch_actions" + | "workspace_external_agent" + | "workspace_prebuilds" + | "workspace_proxy"; + +export const FeatureNames: FeatureName[] = [ + "aibridge", + "ai_governance_user_limit", + "access_control", + "advanced_template_scheduling", + "agent_runtime_hours", + "appearance", + "audit_log", + "boundary", + "browser_only", + "connection_log", + "control_shared_ports", + "custom_roles", + "external_provisioner_daemons", + "external_token_encryption", + "high_availability", + "managed_agent_limit", + "multiple_external_auth", + "multiple_organizations", + "scim", + "service_accounts", + "task_batch_actions", + "template_rbac", + "user_limit", + "user_role_management", + "workspace_batch_actions", + "workspace_external_agent", + "workspace_prebuilds", + "workspace_proxy", +]; + +// From codersdk/deployment.go +export type FeatureSet = "enterprise" | "" | "premium"; + +export const FeatureSets: FeatureSet[] = ["enterprise", "", "premium"]; + +// From codersdk/files.go +export const FormatZip = "zip"; + +// From codersdk/parameters.go +/** + * FriendlyDiagnostic == previewtypes.FriendlyDiagnostic + * Copied to avoid import deps + */ +export interface FriendlyDiagnostic { + readonly severity: DiagnosticSeverityString; + readonly summary: string; + readonly detail: string; + readonly extra: DiagnosticExtra; +} + +// From codersdk/apikey.go +/** + * GenerateAPIKeyResponse contains an API key for a user. + */ +export interface GenerateAPIKeyResponse { + readonly key: string; +} + +// From codersdk/inboxnotification.go +export interface GetInboxNotificationResponse { + readonly notification: InboxNotification; + readonly unread_count: number; +} + +// From codersdk/insights.go +export interface GetUserStatusCountsRequest { + readonly timezone: string; + /** + * @deprecated Use Timezone instead. Offset is ignored when Timezone is provided. + */ + readonly offset?: number; +} + +// From codersdk/insights.go +export interface GetUserStatusCountsResponse { + readonly status_counts: Record; +} + +// From codersdk/users.go +export interface GetUsersResponse { + readonly users: readonly User[]; + readonly count: number; +} + +// From codersdk/gitsshkey.go +export interface GitSSHKey { + readonly user_id: string; + readonly created_at: string; + readonly updated_at: string; + /** + * PublicKey is the SSH public key in OpenSSH format. + * Example: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAID3OmYJvT7q1cF1azbybYy0OZ9yrXfA+M6Lr4vzX5zlp\n" + * Note: The key includes a trailing newline (\n). + */ + readonly public_key: string; +} + +// From codersdk/users.go +export interface GithubAuthMethod { + readonly enabled: boolean; + readonly default_provider_configured: boolean; +} + +// From codersdk/groups.go +export interface Group { + readonly id: string; + readonly name: string; + readonly display_name: string; + readonly organization_id: string; + readonly members: readonly ReducedUser[]; + /** + * How many members are in this group. Shows the total count, + * even if the user is not authorized to read group member details. + * May be greater than `len(Group.Members)`. + */ + readonly total_member_count: number; + readonly avatar_url: string; + readonly quota_allowance: number; + readonly source: GroupSource; + readonly organization_name: string; + readonly organization_display_name: string; +} + +// From codersdk/aibridge.go +export interface GroupAIBudget { + readonly group_id: string; + readonly spend_limit_micros: number; + readonly created_at: string; + readonly updated_at: string; +} + +// From codersdk/aibridge.go +/** + * GroupAISpend is the current AI spend snapshot for a single group within + * the active budget period. + */ +export interface GroupAISpend + extends AISpendPeriodWindow, + OrganizationGroupAISpend {} + +// From codersdk/groups.go +export interface GroupArguments { + /** + * Organization can be an org UUID or name + */ + readonly Organization: string; + /** + * HasMember can be a user uuid or username + */ + readonly HasMember: string; + /** + * GroupIDs is a list of group UUIDs to filter by. + * If not set, all groups will be returned. + */ + readonly GroupIDs: readonly string[]; +} + +// From codersdk/aibridge.go +/** + * GroupMemberAISpend is a single member's AI spend attributed to the queried + * group in the current budget period. + */ +export interface GroupMemberAISpend { + readonly user_id: string; + /** + * EffectiveGroupID is the user's effective budget group within the queried + * group's organization, falling back to the Everyone group when no budget + * applies. Null when the effective group belongs to a different organization + * than the queried group. + */ + readonly effective_group_id: string | null; + /** + * GroupBudget is the budget when the queried group is this user's + * effective budget source. Null when the user's budget resolves to another + * group or no budget applies to the user. + */ + readonly group_budget: AIBudgetLimit | null; + /** + * GroupSpendMicros is the user's spend attributed to the queried group + * over the current budget period. + */ + readonly group_spend_micros: number; +} + +// From codersdk/aibridge.go +/** + * GroupMembersAISpend reports per-member AI spend attributed to a specific + * group in the active budget period. + */ +export interface GroupMembersAISpend extends AISpendPeriodWindow { + readonly members: readonly GroupMemberAISpend[]; +} + +// From codersdk/groups.go +export interface GroupMembersResponse { + readonly users: readonly ReducedUser[]; + readonly count: number; +} + +// From codersdk/groups.go +export interface GroupRequest { + readonly exclude_members: boolean; +} + +// From codersdk/groups.go +export type GroupSource = "oidc" | "user"; + +export const GroupSources: GroupSource[] = ["oidc", "user"]; + +// From codersdk/idpsync.go +export interface GroupSyncSettings { + /** + * Field is the name of the claim field that specifies what groups a user + * should be in. If empty, no groups will be synced. + */ + readonly field: string; + /** + * Mapping is a map from OIDC groups to Coder group IDs + */ + readonly mapping: Record; + /** + * RegexFilter is a regular expression that filters the groups returned by + * the OIDC provider. Any group not matched by this regex will be ignored. + * If the group filter is nil, then no group filtering will occur. + */ + readonly regex_filter: string | null; + /** + * AutoCreateMissing controls whether groups returned by the OIDC provider + * are automatically created in Coder if they are missing. + */ + readonly auto_create_missing_groups: boolean; + /** + * LegacyNameMapping is deprecated. It remaps an IDP group name to + * a Coder group name. Since configuration is now done at runtime, + * group IDs are used to account for group renames. + * For legacy configurations, this config option has to remain. + * @deprecated Use Mapping instead. + */ + readonly legacy_group_name_mapping?: Record; +} + +// From codersdk/deployment.go +export interface HTTPCookieConfig { + readonly secure_auth_cookie?: boolean; + readonly same_site?: string; + readonly host_prefix?: boolean; +} + +// From health/model.go +export type HealthCode = + | "EACS03" + | "EACS02" + | "EACS04" + | "EACS01" + | "EDERP03" + | "EDERP01" + | "EDERP02" + | "EDB01" + | "EDB02" + | "EPD03" + | "EPD02" + | "EPD01" + | "EWP02" + | "EWP04" + | "EWP01" + | "EUNKNOWN" + | "EWS01" + | "EWS02" + | "EWS03"; + +// From health/model.go +export const HealthCodeInterfaceSmallMTU = "EIF01"; + +// From health/model.go +export const HealthCodeSTUNMapVaryDest = "ESTUN02"; + +// From health/model.go +export const HealthCodeSTUNNoNodes = "ESTUN01"; + +export const HealthCodes: HealthCode[] = [ + "EACS03", + "EACS02", + "EACS04", + "EACS01", + "EDERP03", + "EDERP01", + "EDERP02", + "EDB01", + "EDB02", + "EPD03", + "EPD02", + "EPD01", + "EWP02", + "EWP04", + "EWP01", + "EUNKNOWN", + "EWS01", + "EWS02", + "EWS03", +]; + +// From health/model.go +/** + * @typescript-generate Message + */ +export interface HealthMessage { + readonly code: HealthCode; + readonly message: string; +} + +// From healthsdk/healthsdk.go +export type HealthSection = + | "AccessURL" + | "DERP" + | "Database" + | "ProvisionerDaemons" + | "Websocket" + | "WorkspaceProxy"; + +export const HealthSections: HealthSection[] = [ + "AccessURL", + "DERP", + "Database", + "ProvisionerDaemons", + "Websocket", + "WorkspaceProxy", +]; + +// From healthsdk/healthsdk.go +export interface HealthSettings { + readonly dismissed_healthchecks: readonly HealthSection[]; +} + +export const HealthSeverities: HealthSeverity[] = ["error", "ok", "warning"]; + +// From health/model.go +export type HealthSeverity = "error" | "ok" | "warning"; + +// From codersdk/workspaceapps.go +export interface Healthcheck { + /** + * URL specifies the endpoint to check for the app health. + */ + readonly url: string; + /** + * Interval specifies the seconds between each health check. + */ + readonly interval: number; + /** + * Threshold specifies the number of consecutive failed health checks before returning "unhealthy". + */ + readonly threshold: number; +} + +// From codersdk/deployment.go +/** + * HealthcheckConfig contains configuration for healthchecks. + */ +export interface HealthcheckConfig { + readonly refresh: number; + readonly threshold_database: number; +} + +// From healthsdk/healthsdk.go +/** + * HealthcheckReport contains information about the health status of a Coder deployment. + */ +export interface HealthcheckReport { + /** + * Time is the time the report was generated at. + */ + readonly time: string; + /** + * Healthy is true if the report returns no errors. + * @deprecated use `Severity` instead + */ + readonly healthy: boolean; + /** + * Severity indicates the status of Coder health. + */ + readonly severity: HealthSeverity; + readonly derp: DERPHealthReport; + readonly access_url: AccessURLReport; + readonly websocket: WebsocketReport; + readonly database: DatabaseReport; + readonly workspace_proxy: WorkspaceProxyReport; + readonly provisioner_daemons: ProvisionerDaemonsReport; + /** + * The Coder version of the server that the report was generated on. + */ + readonly coder_version: string; +} + +// From codersdk/idpsync.go +export interface IDPSyncMapping { + /** + * The IdP claim the user has + */ + readonly Given: string; + /** + * The ID of the Coder resource the user should be added to + */ + readonly Gets: ResourceIdType; +} + +// From codersdk/usersecrets.go +/** + * ImportUserSecretsRequest is the payload for the bulk secret import + * endpoint. Content is the raw file bytes and Format selects the parser. + */ +export interface ImportUserSecretsRequest { + readonly format: SecretsFileFormat; + readonly content: string; +} + +// From codersdk/inboxnotification.go +export interface InboxNotification { + readonly id: string; + readonly user_id: string; + readonly template_id: string; + readonly targets: readonly string[]; + readonly title: string; + readonly content: string; + readonly icon: string; + readonly actions: readonly InboxNotificationAction[]; + readonly read_at: string | null; + readonly created_at: string; +} + +// From codersdk/inboxnotification.go +export interface InboxNotificationAction { + readonly label: string; + readonly url: string; +} + +// From codersdk/inboxnotification.go +export const InboxNotificationFallbackIconAccount = "DEFAULT_ICON_ACCOUNT"; + +// From codersdk/inboxnotification.go +export const InboxNotificationFallbackIconOther = "DEFAULT_ICON_OTHER"; + +// From codersdk/inboxnotification.go +export const InboxNotificationFallbackIconTemplate = "DEFAULT_ICON_TEMPLATE"; + +// From codersdk/inboxnotification.go +export const InboxNotificationFallbackIconWorkspace = "DEFAULT_ICON_WORKSPACE"; + +// From codersdk/insights.go +export type InsightsReportInterval = "day" | "week"; + +export const InsightsReportIntervals: InsightsReportInterval[] = [ + "day", + "week", +]; + +// From codersdk/templates.go +export interface InvalidatePresetsResponse { + readonly invalidated: readonly InvalidatedPreset[]; +} + +// From codersdk/templates.go +export interface InvalidatedPreset { + readonly template_name: string; + readonly template_version_name: string; + readonly preset_name: string; +} + +// From codersdk/workspaceagents.go +export interface IssueReconnectingPTYSignedTokenRequest { + /** + * URL is the URL of the reconnecting-pty endpoint you are connecting to. + */ + readonly url: string; + readonly agentID: string; +} + +// From codersdk/workspaceagents.go +export interface IssueReconnectingPTYSignedTokenResponse { + readonly signed_token: string; +} + +// From codersdk/provisionerdaemons.go +export type JobErrorCode = "INSUFFICIENT_QUOTA" | "REQUIRED_TEMPLATE_VARIABLES"; + +export const JobErrorCodes: JobErrorCode[] = [ + "INSUFFICIENT_QUOTA", + "REQUIRED_TEMPLATE_VARIABLES", +]; + +// From codersdk/licenses.go +export interface License { + readonly id: number; + readonly uuid: string; + readonly uploaded_at: string; + /** + * Claims are the JWT claims asserted by the license. Here we use + * a generic string map to ensure that all data from the server is + * parsed verbatim, not just the fields this version of Coder + * understands. + */ + // empty interface{} type, falling back to unknown + readonly claims: Record; +} + +// From codersdk/licenses.go +export const LicenseAIGovernance90PercentWarningText = + "You have used %d%% of your AI Governance add-on seats."; + +// From codersdk/licenses.go +export const LicenseAIGovernanceOverLimitWarningText = + "Your organization is using %d of %d AI Governance add-on seats (%d over the limit)."; + +// From codersdk/licenses.go +export const LicenseAgentRuntimeHoursAllocationReachedWarningText = + "Your deployment has used %d of the %d Coder Agent runtime hours included in the current license term."; + +// From codersdk/licenses.go +export const LicenseAgentRuntimeHoursClaimsIgnoredWarningText = + "A license contains unusable Coder Agent runtime hour claims, which were ignored. The rest of that license is unaffected. Check the coderd logs for the affected license and claims, and contact support to have the license re-issued."; + +// From codersdk/licenses.go +/** + * The dashboard's LicenseBanner matches this text's pre-placeholder + * prefix to render it muted and without a sales link, so the license + * warning texts must stay pairwise distinct before their first + * placeholder. See TestLicenseAgentRuntimeHoursWarningTexts. + */ +export const LicenseAgentRuntimeHoursSoftLimitWarningText = + "Your deployment is approaching its Coder Agent runtime hours allocation: %d of the %d hours included in the current license term are used, at or above the advisory soft limit of %d hours."; + +// From codersdk/licenses.go +export const LicenseAgentRuntimeUsageUnavailableErrorText = + "Unable to determine Coder Agent runtime usage. Reported runtime hours are unavailable until the next successful refresh; workspaces are unaffected. Check the coderd logs for details."; + +// From codersdk/licenses.go +export const LicenseExpiryClaim = "license_expires"; + +// From codersdk/licenses.go +export const LicenseManagedAgentLimitExceededWarningText = + "You have built more workspaces with managed agents than your license allows."; + +// From codersdk/licenses.go +export const LicenseTelemetryRequiredErrorText = + "License requires telemetry but telemetry is disabled"; + +// From codersdk/deployment.go +export interface LinkConfig { + readonly name: string; + readonly target: string; + readonly icon: string; + readonly location?: string; +} + +// From codersdk/chats.go +/** + * ListChatsOptions are optional parameters for ListChats. + */ +export interface ListChatsOptions extends Pagination { + /** + * Query supports raw chat search terms. If Query includes a source: term, + * Source must be empty. + */ + readonly Query: string; + /** + * Source adds a source: term to Query. + */ + readonly Source: ChatListSource; + readonly Labels: Record; +} + +// From codersdk/inboxnotification.go +export interface ListInboxNotificationsRequest { + readonly targets?: string; + readonly templates?: string; + readonly read_status?: string; + readonly starting_before?: string; +} + +// From codersdk/inboxnotification.go +export interface ListInboxNotificationsResponse { + readonly notifications: readonly InboxNotification[]; + readonly unread_count: number; +} + +// From codersdk/externalauth.go +export interface ListUserExternalAuthResponse { + readonly providers: readonly ExternalAuthLinkProvider[]; + /** + * Links are all the authenticated links for the user. + * If a link has a provider ID that does not exist, then that provider + * is no longer configured, rendering it unusable. It is still valuable + * to include these links so that the user can unlink them. + */ + readonly links: readonly ExternalAuthLink[]; +} + +// From codersdk/provisionerdaemons.go +export type LogLevel = "debug" | "error" | "info" | "trace" | "warn"; + +export const LogLevels: LogLevel[] = [ + "debug", + "error", + "info", + "trace", + "warn", +]; + +// From codersdk/provisionerdaemons.go +export type LogSource = "provisioner" | "provisioner_daemon"; + +export const LogSources: LogSource[] = ["provisioner", "provisioner_daemon"]; + +// From codersdk/deployment.go +export interface LoggingConfig { + readonly log_filter: string; + readonly human: string; + readonly json: string; + readonly stackdriver: string; +} + +// From codersdk/apikey.go +export type LoginType = "github" | "none" | "oidc" | "password" | "token" | ""; + +export const LoginTypes: LoginType[] = [ + "github", + "none", + "oidc", + "password", + "token", + "", +]; + +// From codersdk/users.go +/** + * LoginWithPasswordRequest enables callers to authenticate with email and password. + */ +export interface LoginWithPasswordRequest { + readonly email: string; + readonly password: string; +} + +// From codersdk/users.go +/** + * LoginWithPasswordResponse contains a session token for the newly authenticated user. + */ +export interface LoginWithPasswordResponse { + readonly session_token: string; +} + +// From codersdk/mcp.go +/** + * MCPServerConfig represents an admin-configured MCP server. + */ +export interface MCPServerConfig { + readonly id: string; + readonly organization_id: string; + readonly display_name: string; + readonly slug: string; + readonly description: string; + readonly icon_url: string; + readonly transport: string; // "streamable_http" or "sse" + readonly url: string; + readonly auth_type: string; // "none", "oauth2", "api_key", "custom_headers", "user_oidc" + /** + * OAuth2 fields (only populated for admins). + */ + readonly oauth2_client_id?: string; + readonly has_oauth2_secret: boolean; + readonly oauth2_auth_url?: string; + readonly oauth2_token_url?: string; + readonly oauth2_revocation_url?: string; + readonly oauth2_scopes?: string; + /** + * API key fields (only populated for admins). + */ + readonly api_key_header?: string; + readonly has_api_key: boolean; + readonly has_custom_headers: boolean; + /** + * Tool governance. + */ + readonly tool_allow_list: readonly string[]; + readonly tool_deny_list: readonly string[]; + /** + * Availability policy set by admin. + */ + readonly availability: string; // "force_on", "default_on", "default_off" + readonly enabled: boolean; + readonly model_intent: boolean; + readonly allow_in_plan_mode: boolean; + /** + * ForwardCoderHeaders forwards the same Coder identity headers we + * send to LLM providers (X-Coder-Owner-Id, X-Coder-Chat-Id, and the + * optional X-Coder-Subchat-Id and X-Coder-Workspace-Id) to this + * MCP server on every request. Off by default to avoid leaking + * chat identity to third-party servers. + */ + readonly forward_coder_headers: boolean; + readonly created_at: string; + readonly updated_at: string; + /** + * Per-user state (populated for non-admin requests). + */ + readonly auth_connected: boolean; +} + +// From codersdk/mcp.go +/** + * MCPServerConfigACL is the resolved access control list of an MCP server + * config. + */ +export interface MCPServerConfigACL { + readonly users: readonly MCPServerConfigUser[]; + readonly groups: readonly MCPServerConfigGroup[]; +} + +// From codersdk/mcp.go +/** + * MCPServerConfigGroup is a group entry in an MCP server config ACL. + */ +export interface MCPServerConfigGroup extends Group { + readonly role: MCPServerConfigRole; +} + +// From codersdk/mcp.go +export type MCPServerConfigRole = "" | "read"; + +export const MCPServerConfigRoles: MCPServerConfigRole[] = ["", "read"]; + +// From codersdk/mcp.go +/** + * MCPServerConfigUser is a user entry in an MCP server config ACL. + */ +export interface MCPServerConfigUser extends MinimalUser { + readonly role: MCPServerConfigRole; +} + +// From codersdk/mcp.go +/** + * MCPServerOAuth2DisconnectResponse reports whether the removed token + * was also revoked at the OAuth provider. + */ +export interface MCPServerOAuth2DisconnectResponse { + readonly token_revoked: boolean; + readonly token_revocation_error?: string; +} + +// From codersdk/provisionerdaemons.go +/** + * MatchedProvisioners represents the number of provisioner daemons + * available to take a job at a specific point in time. + * Introduced in Coder version 2.18.0. + */ +export interface MatchedProvisioners { + /** + * Count is the number of provisioner daemons that matched the given + * tags. If the count is 0, it means no provisioner daemons matched the + * requested tags. + */ + readonly count: number; + /** + * Available is the number of provisioner daemons that are available to + * take jobs. This may be less than the count if some provisioners are + * busy or have been stopped. + */ + readonly available: number; + /** + * MostRecentlySeen is the most recently seen time of the set of matched + * provisioners. If no provisioners matched, this field will be null. + */ + readonly most_recently_seen?: string; +} + +// From codersdk/aimodelprices.go +/** + * MaxAIModelPricesBytes bounds an upsert request body. + */ +export const MaxAIModelPricesBytes = 1048576; // 1 MiB + +// From codersdk/aibridge.go +/** + * MaxAISpendLimitMicros is the highest AI spend limit that can be configured, + * $1,000,000 per member per budget period. + */ +export const MaxAISpendLimitMicros = 1000000000000; + +// From codersdk/chats.go +/** + * MaxChatFileIDs is the maximum number of file IDs that can be + * associated with a single chat. This limit prevents unbounded + * growth in the chat_file_links table. It is easier to raise + * this limit than to lower it. + */ +export const MaxChatFileIDs = 50; + +// From codersdk/chats.go +/** + * MaxChatFileSizeBytes is the upload-endpoint cap for chat + * attachments. + */ +export const MaxChatFileSizeBytes = 10485760; + +// From codersdk/usersecretsimport.go +/** + * MaxSecretsFileBytes bounds the raw size of a secrets file before parsing. + */ +export const MaxSecretsFileBytes = 1048576; // 1 MiB + +// From codersdk/usersecretvalidation.go +/** + * MaxUserSecretEnvNameLength caps the length of an env_name when one + * is provided. 256 is a generous round number that should allow any + * realistic env name while still bounding inputs. + * + * This is a per-row syntactic check, not an aggregate. It does not + * interact with the env_bytes aggregate (which is itself an + * approximate budget; see MaxUserSecretsPerUserCount). + */ +export const MaxUserSecretEnvNameLength = 256; + +// From codersdk/usersecretvalidation.go +/** + * MaxUserSecretValueBytes is the maximum number of bytes for a + * single secret value. It is enforced in two places: + * + * - The HTTP handler validates the raw (plaintext) value with + * UserSecretValueValid before the row is written. + * - The Postgres trigger enforce_user_secrets_per_user_limits + * enforces the same number as an aggregate on stored bytes + * across a user's env-injected secrets. This defends the + * ~32 KiB Windows process env block. + * + * On deployments with secret encryption enabled, stored bytes + * exceed plaintext by ~1.33x (AES-GCM + base64), so the trigger's + * env-aggregate budget can be reached at less plaintext than the + * handler's per-value check would suggest. The trigger is + * authoritative; the handler's check is a fast pre-flight that + * catches the common "one value is too big" case before the row + * is encrypted and sent to the DB. + * + * One number serves both roles because the per-value cap can't + * usefully exceed the smallest aggregate cap any single row could + * trip: a value bigger than the env aggregate would be rejected + * the moment its env_name was set, so allowing it at the per-value + * layer would just move the failure later. + * + * See MaxUserSecretsPerUserCount for the rationale behind the other + * two caps (count, total bytes). + */ +export const MaxUserSecretValueBytes = 24576; // 24 KiB + +// From codersdk/usersecretvalidation.go +/** + * MaxUserSecretsPerUserCount caps the number of secrets a single user + * may own. + * + * Why a cap exists at all: user_secrets is user-scoped, so every + * workspace the user owns loads the same set into its agent + * manifest, and env-injected ones land in the workspace agent's + * process env. Without a cap, a user can overflow one of three + * external limits by accumulating enough secrets, or by making + * them large enough. The failure surfaces at workspace start (or + * as a truncated env), not at create-time. + * + * What drives each cap, and the rough math: + * + * - Count (50): backstops row-count growth from many small + * secrets. The total-bytes cap binds first for large secrets; + * this cap binds first for typical-sized ones (~few KB). + * + * - Total bytes (200 KiB): sized to cover realistic credential + * storage (API keys, SSH keys, kubeconfigs, cert bundles) + * with headroom. Well under the 4 MiB DRPC agent manifest + * budget (codersdk/drpcsdk.MaxMessageSize). + * + * - Env bytes (24 KiB): an approximate budget for the value + * bytes of env-injected secrets. Leaves ~8 KiB of headroom + * under the ~32 KiB Windows process env block + * (CreateProcessW's lpEnvironment is capped at 32,767 + * characters) for what this aggregate does not count: + * env_name bytes, per-entry overhead, agent-injected vars + * (CODER_*, PATH, HOME, ...), and template-defined env. Not + * a strict overflow guarantee. Linux/macOS ARG_MAX (~2 MiB) + * is far above this, so one Windows-safe cap works + * everywhere. + * + * Byte caps measure stored bytes (octet_length of encrypted+base64). + * Plaintext is slightly tighter in encrypted deployments. That is + * fine: the limits we defend all measure transmitted bytes, and + * stored bytes upper-bound those. + * + * The Postgres trigger enforce_user_secrets_per_user_limits is the + * source of truth; the HTTP handler maps its check_violation to a + * 400. TestUserSecretLimits in coderd/usersecrets_test.go exercises + * off-by-one at each cap across POST and PATCH, so any drift + * between these constants and the trigger's literals fails an + * assertion. + */ +export const MaxUserSecretsPerUserCount = 50; + +// From codersdk/usersecretvalidation.go +/** + * MaxUserSecretsTotalValueBytes caps the sum of stored value bytes + * per user. See MaxUserSecretsPerUserCount for the full rationale and + * math behind all three caps. + */ +export const MaxUserSecretsTotalValueBytes = 204800; // 200 KiB + +// From codersdk/organizations.go +export interface MinimalOrganization { + readonly id: string; + readonly name: string; + readonly display_name: string; + readonly icon: string; +} + +// From codersdk/users.go +/** + * MinimalUser is the minimal information needed to identify a user and show + * them on the UI. + */ +export interface MinimalUser { + readonly id: string; + readonly username: string; + readonly name?: string; + readonly avatar_url?: string; +} + +// From netcheck/netcheck.go +/** + * Report contains the result of a single netcheck. + */ +export interface NetcheckReport { + readonly UDP: boolean; // a UDP STUN round trip completed + readonly IPv6: boolean; // an IPv6 STUN round trip completed + readonly IPv4: boolean; // an IPv4 STUN round trip completed + readonly IPv6CanSend: boolean; // an IPv6 packet was able to be sent + readonly IPv4CanSend: boolean; // an IPv4 packet was able to be sent + readonly OSHasIPv6: boolean; // could bind a socket to ::1 + readonly ICMPv4: boolean; // an ICMPv4 round trip completed + /** + * MappingVariesByDestIP is whether STUN results depend which + * STUN server you're talking to (on IPv4). + */ + readonly MappingVariesByDestIP: boolean | null; + /** + * HairPinning is whether the router supports communicating + * between two local devices through the NATted public IP address + * (on IPv4). + */ + readonly HairPinning: boolean | null; + /** + * UPnP is whether UPnP appears present on the LAN. + * Empty means not checked. + */ + readonly UPnP: boolean | null; + /** + * PMP is whether NAT-PMP appears present on the LAN. + * Empty means not checked. + */ + readonly PMP: boolean | null; + /** + * PCP is whether PCP appears present on the LAN. + * Empty means not checked. + */ + readonly PCP: boolean | null; + readonly PreferredDERP: number; // or 0 for unknown + readonly RegionLatency: Record; // keyed by DERP Region ID + readonly RegionV4Latency: Record; // keyed by DERP Region ID + readonly RegionV6Latency: Record; // keyed by DERP Region ID + readonly GlobalV4: string; // ip:port of global IPv4 + readonly GlobalV6: string; // [ip]:port of global IPv6 + /** + * CaptivePortal is set when we think there's a captive portal that is + * intercepting HTTP traffic. + */ + readonly CaptivePortal: boolean | null; +} + +// From codersdk/notifications.go +export interface NotificationMethodsResponse { + readonly available: readonly string[]; + readonly default: string; +} + +// From codersdk/notifications.go +export interface NotificationPreference { + readonly id: string; + readonly disabled: boolean; + readonly updated_at: string; +} + +// From codersdk/notifications.go +export interface NotificationTemplate { + readonly id: string; + readonly name: string; + readonly title_template: string; + readonly body_template: string; + readonly actions: string; + readonly group: string; + readonly method: string; + readonly kind: string; + readonly enabled_by_default: boolean; +} + +// From codersdk/deployment.go +export interface NotificationsConfig { + /** + * The upper limit of attempts to send a notification. + */ + readonly max_send_attempts: number; + /** + * The minimum time between retries. + */ + readonly retry_interval: number; + /** + * The notifications system buffers message updates in memory to ease pressure on the database. + * This option controls how often it synchronizes its state with the database. The shorter this value the + * lower the change of state inconsistency in a non-graceful shutdown - but it also increases load on the + * database. It is recommended to keep this option at its default value. + */ + readonly sync_interval: number; + /** + * The notifications system buffers message updates in memory to ease pressure on the database. + * This option controls how many updates are kept in memory. The lower this value the + * lower the change of state inconsistency in a non-graceful shutdown - but it also increases load on the + * database. It is recommended to keep this option at its default value. + */ + readonly sync_buffer_size: number; + /** + * How long a notifier should lease a message. This is effectively how long a notification is 'owned' + * by a notifier, and once this period expires it will be available for lease by another notifier. Leasing + * is important in order for multiple running notifiers to not pick the same messages to deliver concurrently. + * This lease period will only expire if a notifier shuts down ungracefully; a dispatch of the notification + * releases the lease. + */ + readonly lease_period: number; + /** + * How many notifications a notifier should lease per fetch interval. + */ + readonly lease_count: number; + /** + * How often to query the database for queued notifications. + */ + readonly fetch_interval: number; + /** + * Which delivery method to use (available options: 'smtp', 'webhook'). + */ + readonly method: string; + /** + * How long to wait while a notification is being sent before giving up. + */ + readonly dispatch_timeout: number; + /** + * SMTP settings. + */ + readonly email: NotificationsEmailConfig; + /** + * Webhook settings. + */ + readonly webhook: NotificationsWebhookConfig; + /** + * Inbox settings. + */ + readonly inbox: NotificationsInboxConfig; +} + +// From codersdk/deployment.go +export interface NotificationsEmailAuthConfig { + /** + * Identity for PLAIN auth. + */ + readonly identity: string; + /** + * Username for LOGIN/PLAIN auth. + */ + readonly username: string; + /** + * Password for LOGIN/PLAIN auth. + */ + readonly password: string; + /** + * File from which to load the password for LOGIN/PLAIN auth. + */ + readonly password_file: string; +} + +// From codersdk/deployment.go +export interface NotificationsEmailConfig { + /** + * The sender's address. + */ + readonly from: string; + /** + * The intermediary SMTP host through which emails are sent (host:port). + */ + readonly smarthost: string; + /** + * The hostname identifying the SMTP server. + */ + readonly hello: string; + /** + * Authentication details. + */ + readonly auth: NotificationsEmailAuthConfig; + /** + * TLS details. + */ + readonly tls: NotificationsEmailTLSConfig; + /** + * ForceTLS causes a TLS connection to be attempted. + */ + readonly force_tls: boolean; +} + +// From codersdk/deployment.go +export interface NotificationsEmailTLSConfig { + /** + * StartTLS attempts to upgrade plain connections to TLS. + */ + readonly start_tls: boolean; + /** + * ServerName to verify the hostname for the targets. + */ + readonly server_name: string; + /** + * InsecureSkipVerify skips target certificate validation. + */ + readonly insecure_skip_verify: boolean; + /** + * CAFile specifies the location of the CA certificate to use. + */ + readonly ca_file: string; + /** + * CertFile specifies the location of the certificate to use. + */ + readonly cert_file: string; + /** + * KeyFile specifies the location of the key to use. + */ + readonly key_file: string; +} + +// From codersdk/deployment.go +export interface NotificationsInboxConfig { + readonly enabled: boolean; +} + +// From codersdk/notifications.go +export interface NotificationsSettings { + readonly notifier_paused: boolean; +} + +// From codersdk/deployment.go +export interface NotificationsWebhookConfig { + /** + * The URL to which the payload will be sent with an HTTP POST request. + */ + readonly endpoint: string; +} + +// From codersdk/parameters.go +/** + * NullHCLString == `previewtypes.NullHCLString`. + */ +export interface NullHCLString { + readonly value: string; + readonly valid: boolean; +} + +// From codersdk/oauth2.go +export interface OAuth2AppEndpoints { + readonly authorization: string; + readonly token: string; + readonly token_revoke: string; + /** + * DeviceAuth is optional. + */ + readonly device_authorization: string; +} + +// From codersdk/oauth2.go +/** + * OAuth2AuthorizationServerMetadata represents RFC 8414 OAuth 2.0 Authorization Server Metadata. + */ +export interface OAuth2AuthorizationServerMetadata { + readonly issuer: string; + readonly authorization_endpoint: string; + readonly token_endpoint: string; + readonly registration_endpoint?: string; + readonly revocation_endpoint?: string; + readonly response_types_supported: readonly OAuth2ProviderResponseType[]; + readonly grant_types_supported?: readonly OAuth2ProviderGrantType[]; + readonly code_challenge_methods_supported?: readonly OAuth2PKCECodeChallengeMethod[]; + readonly scopes_supported?: readonly string[]; + readonly token_endpoint_auth_methods_supported?: readonly OAuth2TokenEndpointAuthMethod[]; +} + +// From codersdk/oauth2.go +/** + * OAuth2ClientConfiguration represents RFC 7592 Client Read Response. + */ +export interface OAuth2ClientConfiguration { + readonly client_id: string; + readonly client_id_issued_at: number; + readonly client_secret_expires_at?: number; + readonly redirect_uris?: readonly string[]; + readonly client_name?: string; + readonly client_uri?: string; + readonly logo_uri?: string; + readonly tos_uri?: string; + readonly policy_uri?: string; + readonly jwks_uri?: string; + readonly jwks?: Record; + readonly software_id?: string; + readonly software_version?: string; + readonly grant_types: readonly OAuth2ProviderGrantType[]; + readonly response_types: readonly OAuth2ProviderResponseType[]; + readonly token_endpoint_auth_method: OAuth2TokenEndpointAuthMethod; + readonly scope?: string; + readonly contacts?: readonly string[]; + readonly registration_access_token?: string; + readonly registration_client_uri: string; +} + +// From codersdk/oauth2.go +/** + * OAuth2ClientRegistrationRequest represents RFC 7591 Dynamic Client Registration Request. + */ +export interface OAuth2ClientRegistrationRequest { + readonly redirect_uris?: readonly string[]; + readonly client_name?: string; + readonly client_uri?: string; + readonly logo_uri?: string; + readonly tos_uri?: string; + readonly policy_uri?: string; + readonly jwks_uri?: string; + readonly jwks?: Record; + readonly software_id?: string; + readonly software_version?: string; + readonly software_statement?: string; + readonly grant_types?: readonly OAuth2ProviderGrantType[]; + readonly response_types?: readonly OAuth2ProviderResponseType[]; + readonly token_endpoint_auth_method?: OAuth2TokenEndpointAuthMethod; + readonly scope?: string; + readonly contacts?: readonly string[]; +} + +// From codersdk/oauth2.go +/** + * OAuth2ClientRegistrationResponse represents RFC 7591 Dynamic Client Registration Response. + */ +export interface OAuth2ClientRegistrationResponse { + readonly client_id: string; + readonly client_secret?: string; + readonly client_id_issued_at?: number; + readonly client_secret_expires_at?: number; + readonly redirect_uris?: readonly string[]; + readonly client_name?: string; + readonly client_uri?: string; + readonly logo_uri?: string; + readonly tos_uri?: string; + readonly policy_uri?: string; + readonly jwks_uri?: string; + readonly jwks?: Record; + readonly software_id?: string; + readonly software_version?: string; + readonly grant_types: readonly OAuth2ProviderGrantType[]; + readonly response_types: readonly OAuth2ProviderResponseType[]; + readonly token_endpoint_auth_method: OAuth2TokenEndpointAuthMethod; + readonly scope?: string; + readonly contacts?: readonly string[]; + readonly registration_access_token: string; + readonly registration_client_uri: string; +} + +// From codersdk/oauth2.go +export type OAuth2ClientType = "confidential" | "public"; + +export const OAuth2ClientTypes: OAuth2ClientType[] = ["confidential", "public"]; + +// From codersdk/deployment.go +export interface OAuth2Config { + readonly github: OAuth2GithubConfig; +} + +// From codersdk/oauth2.go +export interface OAuth2DeviceFlowCallbackResponse { + readonly redirect_url: string; +} + +// From codersdk/oauth2.go +/** + * OAuth2Error represents an OAuth2-compliant error response per RFC 6749. + */ +export interface OAuth2Error { + readonly error: OAuth2ErrorCode; + readonly error_description?: string; + readonly error_uri?: string; +} + +// From codersdk/oauth2.go +export type OAuth2ErrorCode = + | "access_denied" + | "invalid_client" + | "invalid_grant" + | "invalid_request" + | "invalid_scope" + | "invalid_target" + | "server_error" + | "temporarily_unavailable" + | "unauthorized_client" + | "unsupported_grant_type" + | "unsupported_response_type" + | "unsupported_token_type"; + +export const OAuth2ErrorCodes: OAuth2ErrorCode[] = [ + "access_denied", + "invalid_client", + "invalid_grant", + "invalid_request", + "invalid_scope", + "invalid_target", + "server_error", + "temporarily_unavailable", + "unauthorized_client", + "unsupported_grant_type", + "unsupported_response_type", + "unsupported_token_type", +]; + +// From codersdk/deployment.go +export interface OAuth2GithubConfig { + readonly client_id: string; + readonly client_secret: string; + readonly device_flow: boolean; + readonly default_provider_enable: boolean; + readonly allowed_orgs: string; + readonly allowed_teams: string; + readonly allow_signups: boolean; + readonly allow_everyone: boolean; + readonly enterprise_base_url: string; +} + +// From codersdk/oauth2.go +export type OAuth2PKCECodeChallengeMethod = "plain" | "S256"; + +export const OAuth2PKCECodeChallengeMethods: OAuth2PKCECodeChallengeMethod[] = [ + "plain", + "S256", +]; + +// From codersdk/client.go +/** + * OAuth2PKCEVerifier is the name of the cookie that stores the oauth2 PKCE + * verifier. This is the raw verifier that when hashed, will match the challenge + * sent in the initial oauth2 request. + */ +export const OAuth2PKCEVerifier = "oauth_pkce_verifier"; + +// From codersdk/oauth2.go +/** + * OAuth2ProtectedResourceMetadata represents RFC 9728 OAuth 2.0 Protected Resource Metadata + */ +export interface OAuth2ProtectedResourceMetadata { + readonly resource: string; + readonly authorization_servers: readonly string[]; + readonly scopes_supported?: readonly string[]; + readonly bearer_methods_supported?: readonly string[]; +} + +// From codersdk/oauth2.go +export interface OAuth2ProviderApp { + readonly id: string; + readonly name: string; + readonly callback_url: string; + readonly icon: string; + /** + * Endpoints are included in the app response for easier discovery. The OAuth2 + * spec does not have a defined place to find these (for comparison, OIDC has + * a '/.well-known/openid-configuration' endpoint). + */ + readonly endpoints: OAuth2AppEndpoints; +} + +// From codersdk/oauth2.go +export interface OAuth2ProviderAppFilter { + readonly user_id?: string; +} + +// From codersdk/oauth2.go +export interface OAuth2ProviderAppSecret { + readonly id: string; + readonly last_used_at: string | null; + readonly client_secret_truncated: string; +} + +// From codersdk/oauth2.go +export interface OAuth2ProviderAppSecretFull { + readonly id: string; + readonly client_secret_full: string; +} + +// From codersdk/oauth2.go +export type OAuth2ProviderGrantType = + | "authorization_code" + | "client_credentials" + | "implicit" + | "password" + | "refresh_token"; + +export const OAuth2ProviderGrantTypes: OAuth2ProviderGrantType[] = [ + "authorization_code", + "client_credentials", + "implicit", + "password", + "refresh_token", +]; + +// From codersdk/oauth2.go +export type OAuth2ProviderResponseType = "code" | "token"; + +export const OAuth2ProviderResponseTypes: OAuth2ProviderResponseType[] = [ + "code", + "token", +]; + +// From codersdk/oauth2.go +/** + * OAuth2ProviderSettings controls deployment-wide OAuth2 provider behavior. + * + * DynamicClientRegistrationEnabled is a pointer so a PUT can omit it to leave + * the current value unchanged, rather than a decoded zero value silently + * resetting it to false. This matters once a second field lands in this + * struct (e.g. a future initial-access-token requirement): a client built + * against an older, single-field version of this struct would otherwise + * always encode the newer field's zero value, silently clearing it on every + * unrelated update. GET always returns a non-nil value. + */ +export interface OAuth2ProviderSettings { + readonly dynamic_client_registration_enabled?: boolean; +} + +// From codersdk/client.go +/** + * OAuth2RedirectCookie is the name of the cookie that stores the oauth2 redirect. + */ +export const OAuth2RedirectCookie = "oauth_redirect"; + +// From codersdk/client.go +/** + * OAuth2RedirectURICookie stores the dynamically computed OIDC redirect_uri + * when CODER_OIDC_REDIRECT_ALLOWED_HOSTS is enabled. The same value must be + * used for both the authorization request and the token exchange (RFC 6749 + * section 4.1.3). + */ +export const OAuth2RedirectURICookie = "oauth_redirect_uri"; + +// From codersdk/oauth2.go +export type OAuth2RevocationTokenTypeHint = "access_token" | "refresh_token"; + +export const OAuth2RevocationTokenTypeHints: OAuth2RevocationTokenTypeHint[] = [ + "access_token", + "refresh_token", +]; + +// From codersdk/client.go +/** + * OAuth2StateCookie is the name of the cookie that stores the oauth2 state. + */ +export const OAuth2StateCookie = "oauth_state"; + +// From codersdk/oauth2.go +export type OAuth2TokenEndpointAuthMethod = + | "client_secret_basic" + | "client_secret_post" + | "none"; + +export const OAuth2TokenEndpointAuthMethods: OAuth2TokenEndpointAuthMethod[] = [ + "client_secret_basic", + "client_secret_post", + "none", +]; + +// From codersdk/oauth2.go +/** + * OAuth2TokenRequest represents a token request per RFC 6749. The actual wire + * format is application/x-www-form-urlencoded; this struct is for SDK docs. + */ +export interface OAuth2TokenRequest { + readonly grant_type: OAuth2ProviderGrantType; + readonly code?: string; + readonly redirect_uri?: string; + readonly client_id?: string; + readonly client_secret?: string; + readonly code_verifier?: string; + readonly refresh_token?: string; + readonly resource?: string; + readonly scope?: string; +} + +// From codersdk/oauth2.go +/** + * OAuth2TokenResponse represents a successful token response per RFC 6749. + */ +export interface OAuth2TokenResponse { + readonly access_token: string; + readonly token_type: OAuth2TokenType; + readonly expires_in?: number; + readonly refresh_token?: string; + readonly scope?: string; + /** + * Expiry is not part of RFC 6749 but is included for compatibility with + * golang.org/x/oauth2.Token and clients that expect a timestamp. + */ + readonly expiry?: string; +} + +// From codersdk/oauth2.go +/** + * OAuth2TokenRevocationRequest represents a token revocation request per RFC 7009. + */ +export interface OAuth2TokenRevocationRequest { + readonly token: string; + readonly token_type_hint?: OAuth2RevocationTokenTypeHint; + readonly client_id?: string; + readonly client_secret?: string; +} + +// From codersdk/oauth2.go +export type OAuth2TokenType = "Bearer" | "DPoP"; + +export const OAuth2TokenTypes: OAuth2TokenType[] = ["Bearer", "DPoP"]; + +// From codersdk/users.go +export interface OAuthConversionResponse { + readonly state_string: string; + readonly expires_at: string; + readonly to_type: LoginType; + readonly user_id: string; +} + +// From codersdk/users.go +export interface OIDCAuthMethod extends AuthMethod { + readonly signInText: string; + readonly iconUrl: string; +} + +// From codersdk/users.go +/** + * OIDCClaimsResponse represents the merged OIDC claims for a user. + */ +export interface OIDCClaimsResponse { + /** + * Claims are the merged claims from the OIDC provider. These + * are the union of the ID token claims and the userinfo claims, + * where userinfo claims take precedence on conflict. + */ + // empty interface{} type, falling back to unknown + readonly claims: Record; +} + +// From codersdk/deployment.go +export interface OIDCConfig { + readonly allow_signups: boolean; + readonly client_id: string; + readonly client_secret: string; + /** + * ClientKeyFile & ClientCertFile are used in place of ClientSecret for PKI auth. + */ + readonly client_key_file: string; + readonly client_cert_file: string; + readonly email_domain: string; + readonly issuer_url: string; + readonly scopes: string; + readonly ignore_email_verified: boolean; + readonly username_field: string; + readonly name_field: string; + readonly email_field: string; + readonly auth_url_params: SerpentStruct>; + /** + * IgnoreUserInfo & UserInfoFromAccessToken are mutually exclusive. Only 1 + * can be set to true. Ideally this would be an enum with 3 states, ['none', + * 'userinfo', 'access_token']. However, for backward compatibility, + * `ignore_user_info` must remain. And `access_token` is a niche, non-spec + * compliant edge case. So it's use is rare, and should not be advised. + */ + readonly ignore_user_info: boolean; + /** + * UserInfoFromAccessToken as mentioned above is an edge case. This allows + * sourcing the user_info from the access token itself instead of a user_info + * endpoint. This assumes the access token is a valid JWT with a set of claims to + * be merged with the id_token. + */ + readonly source_user_info_from_access_token: boolean; + readonly organization_field: string; + readonly organization_mapping: SerpentStruct>; + readonly organization_assign_default: boolean; + readonly group_auto_create: boolean; + readonly group_regex_filter: string; + readonly group_allow_list: string; + readonly groups_field: string; + readonly group_mapping: SerpentStruct>; + readonly user_role_field: string; + readonly user_role_mapping: SerpentStruct>; + readonly user_roles_default: string; + readonly sign_in_text: string; + readonly icon_url: string; + readonly signups_disabled_text: string; + readonly skip_issuer_checks: boolean; + /** + * RedirectURL is optional, defaulting to 'ACCESS_URL'. Only useful in niche + * situations where the OIDC callback domain is different from the ACCESS_URL + * domain. The path component is ignored. + */ + readonly redirect_url: string; + readonly auto_repair_links: boolean; + /** + * EmailFallback allows OIDC logins to fall back to email-based matching + * when the `linked_id` (issuer+subject) does not match an existing user + * link. INSECURE: weakens the linked_id check. It exists for IdP + * brokers that do not issue a stable `sub` for the same user across + * connections. + */ + readonly email_fallback: boolean; + /** + * RedirectAllowedHosts is an allowlist of hostnames that may be used as + * the host of the OIDC redirect_uri. When non-empty, the redirect_uri is + * constructed from the incoming request's Host header (validated against + * this list) instead of from AccessURL. Every listed host must also be + * registered as a valid redirect URI in the OIDC provider. This setting + * is mutually exclusive with RedirectURL: if RedirectURL is set, this + * allowlist is ignored. + */ + readonly redirect_allowed_hosts: string; +} + +// From codersdk/parameters.go +export type OptionType = "bool" | "list(string)" | "number" | "string"; + +export const OptionTypes: OptionType[] = [ + "bool", + "list(string)", + "number", + "string", +]; + +// From codersdk/organizations.go +/** + * Organization is the JSON representation of a Coder organization. + */ +export interface Organization extends MinimalOrganization { + readonly description: string; + readonly created_at: string; + readonly updated_at: string; + readonly is_default: boolean; + /** + * DefaultOrgMemberRoles are unioned into every member's effective + * roles at request time. Changes propagate to all members on the + * next request. + */ + readonly default_org_member_roles: readonly string[]; +} + +// From codersdk/chats.go +/** + * OrganizationChatModelsResponse is the org chat model config collection: + * the caller-readable configs plus the redacted provider descriptors the + * authoring page needs. + */ +export interface OrganizationChatModelsResponse { + readonly models: readonly ChatModel[]; + readonly providers: readonly ChatModelProviderDescriptor[]; + readonly unsupported_providers: readonly ChatUnsupportedProvider[]; +} + +// From codersdk/aibridge.go +/** + * OrganizationGroupAISpend is the current AI spend snapshot for a group + * within the active budget period. + */ +export interface OrganizationGroupAISpend { + readonly group_id: string; + /** + * SpendLimitMicros is the group's configured AI spend budget per member. + * Null when the group has no configured budget. + */ + readonly spend_limit_micros: number | null; + /** + * TotalSpendLimitMicros is the currently configured combined budget of the + * members attributed to this group, with each member's override replacing + * their share. Null when the group has no budget, and zero when no members + * are attributed to it. + */ + readonly total_spend_limit_micros: number | null; + /** + * CurrentSpendMicros is the group's spend over the current budget period. + */ + readonly current_spend_micros: number; +} + +// From codersdk/aibridge.go +/** + * OrganizationGroupsAISpend reports AI spend for a set of groups in the + * active budget period. + */ +export interface OrganizationGroupsAISpend extends AISpendPeriodWindow { + readonly groups: readonly OrganizationGroupAISpend[]; +} + +// From codersdk/organizations.go +export interface OrganizationMember { + readonly user_id: string; + readonly organization_id: string; + readonly created_at: string; + readonly updated_at: string; + readonly roles: readonly SlimRole[]; +} + +// From codersdk/organizations.go +export interface OrganizationMemberWithUserData extends OrganizationMember { + readonly username: string; + readonly name?: string; + readonly avatar_url?: string; + readonly email: string; + readonly status: UserStatus; + readonly login_type: LoginType; + readonly last_seen_at?: string; + readonly user_created_at: string; + readonly user_updated_at: string; + readonly is_service_account?: boolean; + readonly global_roles: readonly SlimRole[]; + /** + * HasAISeat intentionally omits omitempty so the API always includes the + * field, even when false. + */ + readonly has_ai_seat: boolean; +} + +// From codersdk/users.go +export interface OrganizationMembersQuery { + readonly UserID: string; + readonly IncludeSystem: boolean; + readonly GithubUserID: number; +} + +// From codersdk/organizations.go +export interface OrganizationProvisionerDaemonsOptions { + readonly Limit: number; + readonly Offline: boolean; + readonly Status: readonly ProvisionerDaemonStatus[]; + readonly MaxAge: number; + readonly IDs: readonly string[]; + readonly Tags: Record; +} + +// From codersdk/organizations.go +export interface OrganizationProvisionerJobsOptions { + readonly Limit: number; + readonly IDs: readonly string[]; + readonly Status: readonly ProvisionerJobStatus[]; + readonly Tags: Record; + readonly Initiator: string; +} + +// From codersdk/idpsync.go +export interface OrganizationSyncSettings { + /** + * Field selects the claim field to be used as the created user's + * organizations. If the field is the empty string, then no organization + * updates will ever come from the OIDC provider. + */ + readonly field: string; + /** + * Mapping maps from an OIDC claim --> Coder organization uuid + */ + readonly mapping: Record; + /** + * AssignDefault will ensure the default org is always included + * for every user, regardless of their claims. This preserves legacy behavior. + */ + readonly organization_assign_default: boolean; +} + +// From codersdk/groups.go +/** + * PaginatedGroup is a group summary returned by the paginated groups endpoint. + * It deliberately omits the member roster (which the endpoint does not return) + * and exposes only the total member count. Fetch the roster via the group + * members endpoint. + */ +export interface PaginatedGroup { + readonly id: string; + readonly name: string; + readonly display_name: string; + readonly organization_id: string; + /** + * TotalMemberCount is the number of members in the group, shown even when + * the caller cannot read individual members. The roster itself is not + * returned by this endpoint. + */ + readonly total_member_count: number; + readonly avatar_url: string; + readonly quota_allowance: number; + readonly source: GroupSource; + readonly organization_name: string; + readonly organization_display_name: string; +} + +// From codersdk/groups.go +/** + * PaginatedGroupsRequest are the filters for a paginated groups request. + * Groups only support free-text search, so unlike UsersRequest it exposes no + * key:value filters that the endpoint would reject. + */ +export interface PaginatedGroupsRequest extends Pagination { + readonly q?: string; +} + +// From codersdk/groups.go +export interface PaginatedGroupsResponse { + readonly groups: readonly PaginatedGroup[]; + readonly count: number; +} + +// From codersdk/organizations.go +export interface PaginatedMembersRequest { + readonly limit?: number; + readonly offset?: number; +} + +// From codersdk/organizations.go +export interface PaginatedMembersResponse { + readonly members: readonly OrganizationMemberWithUserData[]; + readonly count: number; +} + +// From codersdk/pagination.go +/** + * Pagination sets pagination options for the endpoints that support it. + */ +export interface Pagination { + /** + * AfterID returns all or up to Limit results after the given + * UUID. This option can be used with or as an alternative to + * Offset for better performance. To use it as an alternative, + * set AfterID to the last UUID returned by the previous + * request. + */ + readonly after_id?: string; + /** + * Limit sets the maximum number of users to be returned + * in a single page. If the limit is <= 0, there is no limit + * and all users are returned. + */ + readonly limit?: number; + /** + * Offset is used to indicate which page to return. An offset of 0 + * returns the first 'limit' number of users. + * To get the next page, use offset=*. + * Offset is 0 indexed, so the first record sits at offset 0. + */ + readonly offset?: number; +} + +// From codersdk/parameters.go +export type ParameterFormType = + | "checkbox" + | "" + | "dropdown" + | "error" + | "input" + | "multi-select" + | "radio" + | "slider" + | "switch" + | "tag-select" + | "textarea"; + +export const ParameterFormTypes: ParameterFormType[] = [ + "checkbox", + "", + "dropdown", + "error", + "input", + "multi-select", + "radio", + "slider", + "switch", + "tag-select", + "textarea", +]; + +// From codersdk/idpsync.go +export interface PatchGroupIDPSyncConfigRequest { + readonly field: string; + readonly regex_filter: string | null; + readonly auto_create_missing_groups: boolean; +} + +// From codersdk/idpsync.go +/** + * If the same mapping is present in both Add and Remove, Remove will take presidence. + */ +export interface PatchGroupIDPSyncMappingRequest { + readonly Add: readonly IDPSyncMapping[]; + readonly Remove: readonly IDPSyncMapping[]; +} + +// From codersdk/groups.go +export interface PatchGroupRequest { + readonly add_users: readonly string[]; + readonly remove_users: readonly string[]; + readonly name: string; + readonly display_name: string | null; + readonly avatar_url: string | null; + readonly quota_allowance: number | null; +} + +// From codersdk/idpsync.go +export interface PatchOrganizationIDPSyncConfigRequest { + readonly field: string; + readonly assign_default: boolean; +} + +// From codersdk/idpsync.go +/** + * If the same mapping is present in both Add and Remove, Remove will take presidence. + */ +export interface PatchOrganizationIDPSyncMappingRequest { + readonly Add: readonly IDPSyncMapping[]; + readonly Remove: readonly IDPSyncMapping[]; +} + +// From codersdk/idpsync.go +export interface PatchRoleIDPSyncConfigRequest { + readonly field: string; +} + +// From codersdk/idpsync.go +/** + * If the same mapping is present in both Add and Remove, Remove will take presidence. + */ +export interface PatchRoleIDPSyncMappingRequest { + readonly Add: readonly IDPSyncMapping[]; + readonly Remove: readonly IDPSyncMapping[]; +} + +// From codersdk/templateversions.go +export interface PatchTemplateVersionRequest { + readonly name: string; + readonly message?: string; +} + +// From codersdk/workspaceproxy.go +export interface PatchWorkspaceProxy { + readonly id: string; + readonly name: string; + readonly display_name: string; + readonly icon: string; + readonly regenerate_token: boolean; +} + +// From codersdk/client.go +/** + * PathAppSessionTokenCookie is the name of the cookie that stores an + * application-scoped API token on workspace proxy path app domains. + *nolint:gosec + */ +export const PathAppSessionTokenCookie = "coder_path_app_session_token"; + +// From codersdk/aitasks.go +/** + * PauseTaskResponse represents the response from pausing a task. + */ +export interface PauseTaskResponse { + readonly workspace_build: WorkspaceBuild | null; +} + +// From codersdk/roles.go +/** + * Permission is the format passed into the rego. + */ +export interface Permission { + /** + * Negate makes this a negative permission + */ + readonly negate: boolean; + readonly resource_type: RBACResource; + readonly action: RBACAction; +} + +// From codersdk/oauth2.go +export interface PostOAuth2ProviderAppRequest { + readonly name: string; + readonly callback_url: string; + readonly icon: string; +} + +// From codersdk/workspaces.go +export interface PostWorkspaceUsageRequest { + readonly agent_id: string; + readonly app_name: UsageAppName; +} + +// From codersdk/deployment.go +export type PostgresAuth = "awsiamrds" | "password"; + +export const PostgresAuths: PostgresAuth[] = ["awsiamrds", "password"]; + +// From codersdk/deployment.go +/** + * PostgresConnMaxIdleAuto is the value for auto-computing max idle connections + * based on max open connections. + */ +export const PostgresConnMaxIdleAuto = "auto"; + +// From codersdk/deployment.go +export interface PprofConfig { + readonly enable: boolean; + readonly address: string; +} + +// From codersdk/deployment.go +export interface PrebuildsConfig { + /** + * ReconciliationInterval defines how often the workspace prebuilds state should be reconciled. + */ + readonly reconciliation_interval: number; + /** + * ReconciliationBackoffInterval specifies the amount of time to increase the backoff interval + * when errors occur during reconciliation. + */ + readonly reconciliation_backoff_interval: number; + /** + * ReconciliationBackoffLookback determines the time window to look back when calculating + * the number of failed prebuilds, which influences the backoff strategy. + */ + readonly reconciliation_backoff_lookback: number; + /** + * FailureHardLimit defines the maximum number of consecutive failed prebuild attempts allowed + * before a preset is considered to be in a hard limit state. When a preset hits this limit, + * no new prebuilds will be created until the limit is reset. + * FailureHardLimit is disabled when set to zero. + */ + readonly failure_hard_limit: number; +} + +// From codersdk/prebuilds.go +export interface PrebuildsSettings { + readonly reconciliation_paused: boolean; +} + +// From codersdk/prebuilds.go +/** + * PrebuildsSystemUserID is the UUID of the Coder prebuilds system + * user. Prebuilt workspaces are owned by this user until they are + * claimed; build #1 of a claimed workspace remains attributed to + * this user as the initiator forever, which is how callers can + * recognize a prebuild claim after the fact. + */ +export const PrebuildsSystemUserID = "c42fdf75-3097-471c-8c33-fb52454d81c0"; + +// From codersdk/premiumfunnel.go +/** + * PremiumFunnelEventRequest is the request body for + * POST /api/v2/deployment/premium-funnel-events. + */ +export interface PremiumFunnelEventRequest { + /** + * ID identifies this click, and doubles as the attribution token that a + * later trial signup reports. + */ + readonly id: string; + readonly source: PremiumFunnelSource; + readonly variant: PremiumFunnelVariant; +} + +// From codersdk/premiumfunnel.go +export type PremiumFunnelSource = + | "aibridge_session_threads" + | "aibridge_sessions" + | "ai_gateway_keys" + | "ai_governance" + | "appearance" + | "audit_log" + | "browser_only" + | "connection_log" + | "custom_roles" + | "direct" + | "external_auth" + | "groups" + | "idp_org_sync" + | "idp_sync" + | "multiple_organizations" + | "observability" + | "provisioner_keys" + | "provisioners" + | "template_permissions" + | "workspace_proxies"; + +export const PremiumFunnelSources: PremiumFunnelSource[] = [ + "aibridge_session_threads", + "aibridge_sessions", + "ai_gateway_keys", + "ai_governance", + "appearance", + "audit_log", + "browser_only", + "connection_log", + "custom_roles", + "direct", + "external_auth", + "groups", + "idp_org_sync", + "idp_sync", + "multiple_organizations", + "observability", + "provisioner_keys", + "provisioners", + "template_permissions", + "workspace_proxies", +]; + +// From codersdk/premiumfunnel.go +export type PremiumFunnelVariant = "ai_governance" | "premium" | "small"; + +export const PremiumFunnelVariants: PremiumFunnelVariant[] = [ + "ai_governance", + "premium", + "small", +]; + +// From codersdk/presets.go +export interface Preset { + readonly ID: string; + readonly Name: string; + readonly Parameters: readonly PresetParameter[]; + readonly Default: boolean; + readonly DesiredPrebuildInstances: number | null; + readonly Description: string; + readonly Icon: string; +} + +// From codersdk/presets.go +export interface PresetParameter { + readonly Name: string; + readonly Value: string; +} + +// From codersdk/parameters.go +export interface PreviewParameter extends PreviewParameterData { + readonly value: NullHCLString; + readonly diagnostics: readonly FriendlyDiagnostic[]; +} + +// From codersdk/parameters.go +export interface PreviewParameterData { + readonly name: string; + readonly display_name: string; + readonly description: string; + readonly type: OptionType; + readonly form_type: ParameterFormType; + readonly styling: PreviewParameterStyling; + readonly mutable: boolean; + readonly default_value: NullHCLString; + readonly icon: string; + readonly options: readonly PreviewParameterOption[]; + readonly validations: readonly PreviewParameterValidation[]; + readonly required: boolean; + /** + * legacy_variable_name was removed (= 14) + */ + readonly order: number; + readonly ephemeral: boolean; +} + +// From codersdk/parameters.go +export interface PreviewParameterOption { + readonly name: string; + readonly description: string; + readonly value: NullHCLString; + readonly icon: string; +} + +// From codersdk/parameters.go +export interface PreviewParameterStyling { + readonly placeholder?: string; + readonly disabled?: boolean; + readonly label?: string; + readonly mask_input?: boolean; +} + +// From codersdk/parameters.go +export interface PreviewParameterValidation { + readonly validation_error: string; + /** + * All validation attributes are optional. + */ + readonly validation_regex: string | null; + readonly validation_min: number | null; + readonly validation_max: number | null; + readonly validation_monotonic: string | null; +} + +// From codersdk/deployment.go +export interface PrometheusConfig { + readonly enable: boolean; + readonly address: string; + readonly collect_agent_stats: boolean; + readonly collect_db_metrics: boolean; + readonly aggregate_agent_stats_by: string; +} + +// From codersdk/chats.go +/** + * ProposeChatTitleResponse is returned by the propose-title endpoint. + */ +export interface ProposeChatTitleResponse { + readonly title: string; +} + +// From codersdk/deployment.go +export interface ProvisionerConfig { + /** + * Daemons is the number of built-in terraform provisioners. + */ + readonly daemons: number; + readonly daemon_types: string; + readonly daemon_poll_interval: number; + readonly daemon_poll_jitter: number; + readonly force_cancel_interval: number; + readonly daemon_psk: string; +} + +// From codersdk/provisionerdaemons.go +export interface ProvisionerDaemon { + readonly id: string; + readonly organization_id: string; + readonly key_id: string; + readonly created_at: string; + readonly last_seen_at?: string; + readonly name: string; + readonly version: string; + readonly api_version: string; + readonly provisioners: readonly ProvisionerType[]; + readonly tags: Record; + /** + * Optional fields. + */ + readonly key_name: string | null; + readonly status: ProvisionerDaemonStatus | null; + readonly current_job: ProvisionerDaemonJob | null; + readonly previous_job: ProvisionerDaemonJob | null; +} + +// From codersdk/provisionerdaemons.go +export interface ProvisionerDaemonJob { + readonly id: string; + readonly status: ProvisionerJobStatus; + readonly template_name: string; + readonly template_icon: string; + readonly template_display_name: string; +} + +// From codersdk/client.go +/** + * ProvisionerDaemonKey contains the authentication key for an external provisioner daemon + */ +export const ProvisionerDaemonKey = "Coder-Provisioner-Daemon-Key"; + +// From codersdk/client.go +/** + * ProvisionerDaemonPSK contains the authentication pre-shared key for an external provisioner daemon + */ +export const ProvisionerDaemonPSK = "Coder-Provisioner-Daemon-PSK"; + +// From codersdk/provisionerdaemons.go +export type ProvisionerDaemonStatus = "busy" | "idle" | "offline"; + +export const ProvisionerDaemonStatuses: ProvisionerDaemonStatus[] = [ + "busy", + "idle", + "offline", +]; + +// From healthsdk/healthsdk.go +/** + * ProvisionerDaemonsReport includes health details of each connected provisioner daemon. + */ +export interface ProvisionerDaemonsReport extends BaseReport { + readonly items: readonly ProvisionerDaemonsReportItem[]; +} + +// From healthsdk/healthsdk.go +export interface ProvisionerDaemonsReportItem { + readonly provisioner_daemon: ProvisionerDaemon; + readonly warnings: readonly HealthMessage[]; +} + +// From codersdk/provisionerdaemons.go +/** + * ProvisionerJob describes the job executed by the provisioning daemon. + */ +export interface ProvisionerJob { + readonly id: string; + readonly created_at: string; + readonly started_at?: string; + readonly completed_at?: string; + readonly canceled_at?: string; + readonly error?: string; + readonly error_code?: JobErrorCode; + readonly status: ProvisionerJobStatus; + readonly worker_id?: string; + readonly worker_name?: string; + readonly file_id: string; + readonly tags: Record; + readonly queue_position: number; + readonly queue_size: number; + readonly organization_id: string; + readonly initiator_id: string; + readonly input: ProvisionerJobInput; + readonly type: ProvisionerJobType; + readonly available_workers?: readonly string[]; + readonly metadata: ProvisionerJobMetadata; + readonly logs_overflowed: boolean; +} + +// From codersdk/provisionerdaemons.go +/** + * ProvisionerJobInput represents the input for the job. + */ +export interface ProvisionerJobInput { + readonly template_version_id?: string; + readonly workspace_build_id?: string; + readonly error?: string; +} + +// From codersdk/provisionerdaemons.go +/** + * ProvisionerJobLog represents the provisioner log entry annotated with source and level. + */ +export interface ProvisionerJobLog { + readonly id: number; + readonly created_at: string; + readonly log_source: LogSource; + readonly log_level: LogLevel; + readonly stage: string; + readonly output: string; +} + +// From codersdk/provisionerdaemons.go +/** + * ProvisionerJobMetadata contains metadata for the job. + */ +export interface ProvisionerJobMetadata { + readonly template_version_name: string; + readonly template_id: string; + readonly template_name: string; + readonly template_display_name: string; + readonly template_icon: string; + readonly workspace_id?: string; + readonly workspace_name?: string; + readonly workspace_build_transition?: WorkspaceTransition; +} + +// From codersdk/provisionerdaemons.go +export type ProvisionerJobStatus = + | "canceled" + | "canceling" + | "failed" + | "pending" + | "running" + | "succeeded" + | "unknown"; + +export const ProvisionerJobStatuses: ProvisionerJobStatus[] = [ + "canceled", + "canceling", + "failed", + "pending", + "running", + "succeeded", + "unknown", +]; + +// From codersdk/provisionerdaemons.go +export type ProvisionerJobType = + | "template_version_dry_run" + | "template_version_import" + | "workspace_build"; + +export const ProvisionerJobTypes: ProvisionerJobType[] = [ + "template_version_dry_run", + "template_version_import", + "workspace_build", +]; + +// From codersdk/provisionerdaemons.go +export interface ProvisionerKey { + readonly id: string; + readonly created_at: string; + readonly organization: string; + readonly name: string; + readonly tags: ProvisionerKeyTags; +} + +// From codersdk/provisionerdaemons.go +export interface ProvisionerKeyDaemons { + readonly key: ProvisionerKey; + readonly daemons: readonly ProvisionerDaemon[]; +} + +// From codersdk/provisionerdaemons.go +export const ProvisionerKeyIDBuiltIn = "00000000-0000-0000-0000-000000000001"; + +// From codersdk/provisionerdaemons.go +export const ProvisionerKeyIDPSK = "00000000-0000-0000-0000-000000000003"; + +// From codersdk/provisionerdaemons.go +export const ProvisionerKeyIDUserAuth = "00000000-0000-0000-0000-000000000002"; + +// From codersdk/provisionerdaemons.go +export const ProvisionerKeyNameBuiltIn = "built-in"; + +// From codersdk/provisionerdaemons.go +export const ProvisionerKeyNamePSK = "psk"; + +// From codersdk/provisionerdaemons.go +export const ProvisionerKeyNameUserAuth = "user-auth"; + +// From codersdk/provisionerdaemons.go +export type ProvisionerKeyTags = Record; + +// From codersdk/workspaces.go +export type ProvisionerLogLevel = "debug"; + +export const ProvisionerLogLevels: ProvisionerLogLevel[] = ["debug"]; + +// From codersdk/organizations.go +export type ProvisionerStorageMethod = "file"; + +export const ProvisionerStorageMethods: ProvisionerStorageMethod[] = ["file"]; + +// From codersdk/workspacebuilds.go +export interface ProvisionerTiming { + readonly job_id: string; + readonly started_at: string; + readonly ended_at: string; + readonly stage: TimingStage; + readonly source: string; + readonly action: string; + readonly resource: string; +} + +// From codersdk/organizations.go +export type ProvisionerType = "echo" | "terraform"; + +export const ProvisionerTypes: ProvisionerType[] = ["echo", "terraform"]; + +// From codersdk/workspaceproxy.go +/** + * ProxyHealthReport is a report of the health of the workspace proxy. + * A healthy report will have no errors. Warnings are not fatal. + */ +export interface ProxyHealthReport { + /** + * Errors are problems that prevent the workspace proxy from being healthy + */ + readonly errors: readonly string[]; + /** + * Warnings do not prevent the workspace proxy from being healthy, but + * should be addressed. + */ + readonly warnings: readonly string[]; +} + +// From codersdk/workspaceproxy.go +export type ProxyHealthStatus = + | "ok" + | "unhealthy" + | "unreachable" + | "unregistered"; + +export const ProxyHealthStatuses: ProxyHealthStatus[] = [ + "ok", + "unhealthy", + "unreachable", + "unregistered", +]; + +// From codersdk/workspaces.go +/** + * PutExtendWorkspaceRequest is a request to extend the deadline of + * the active workspace build. + */ +export interface PutExtendWorkspaceRequest { + readonly deadline: string; +} + +// From codersdk/oauth2.go +export interface PutOAuth2ProviderAppRequest { + readonly name: string; + readonly callback_url: string; + readonly icon: string; +} + +// From codersdk/rbacresources_gen.go +export type RBACAction = + | "application_connect" + | "assign" + | "create" + | "create_agent" + | "delete" + | "delete_agent" + | "read" + | "read_personal" + | "ssh" + | "share" + | "unassign" + | "update" + | "update_agent" + | "update_personal" + | "use" + | "view_insights" + | "start" + | "stop"; + +export const RBACActions: RBACAction[] = [ + "application_connect", + "assign", + "create", + "create_agent", + "delete", + "delete_agent", + "read", + "read_personal", + "ssh", + "share", + "unassign", + "update", + "update_agent", + "update_personal", + "use", + "view_insights", + "start", + "stop", +]; + +// From codersdk/rbacresources_gen.go +export type RBACResource = + | "ai_gateway_key" + | "ai_provider" + | "ai_model_price" + | "ai_seat" + | "aibridge_interception" + | "api_key" + | "assign_org_role" + | "assign_role" + | "audit_log" + | "boundary_log" + | "boundary_usage" + | "chat" + | "chat_model_config" + | "connection_log" + | "crypto_key" + | "debug_info" + | "deployment_config" + | "deployment_stats" + | "file" + | "group" + | "group_member" + | "idpsync_settings" + | "inbox_notification" + | "license" + | "mcp_server_config" + | "notification_message" + | "notification_preference" + | "notification_template" + | "oauth2_app" + | "oauth2_app_code_token" + | "oauth2_app_secret" + | "organization" + | "organization_member" + | "prebuilt_workspace" + | "provisioner_daemon" + | "provisioner_jobs" + | "replicas" + | "system" + | "tailnet_coordinator" + | "task" + | "template" + | "usage_event" + | "user" + | "user_secret" + | "user_skill" + | "webpush_subscription" + | "*" + | "workspace" + | "workspace_agent_devcontainers" + | "workspace_agent_resource_monitor" + | "workspace_build_orchestration" + | "workspace_dormant" + | "workspace_proxy"; + +export const RBACResources: RBACResource[] = [ + "ai_gateway_key", + "ai_provider", + "ai_model_price", + "ai_seat", + "aibridge_interception", + "api_key", + "assign_org_role", + "assign_role", + "audit_log", + "boundary_log", + "boundary_usage", + "chat", + "chat_model_config", + "connection_log", + "crypto_key", + "debug_info", + "deployment_config", + "deployment_stats", + "file", + "group", + "group_member", + "idpsync_settings", + "inbox_notification", + "license", + "mcp_server_config", + "notification_message", + "notification_preference", + "notification_template", + "oauth2_app", + "oauth2_app_code_token", + "oauth2_app_secret", + "organization", + "organization_member", + "prebuilt_workspace", + "provisioner_daemon", + "provisioner_jobs", + "replicas", + "system", + "tailnet_coordinator", + "task", + "template", + "usage_event", + "user", + "user_secret", + "user_skill", + "webpush_subscription", + "*", + "workspace", + "workspace_agent_devcontainers", + "workspace_agent_resource_monitor", + "workspace_build_orchestration", + "workspace_dormant", + "workspace_proxy", +]; + +// From codersdk/deployment.go +export interface RateLimitConfig { + readonly disable_all: boolean; + readonly api: number; +} + +// From codersdk/users.go +/** + * ReducedUser omits role and organization information. Roles are deduced from + * the user's site and organization roles. This requires fetching the user's + * organizational memberships. Fetching that is more expensive, and not usually + * required by the frontend. + */ +export interface ReducedUser extends MinimalUser { + readonly email: string; + readonly created_at: string; + readonly updated_at: string; + readonly last_seen_at?: string; + readonly status: UserStatus; + readonly login_type: LoginType; + readonly is_service_account?: boolean; + /** + * @deprecated this value should be retrieved from + * `codersdk.UserPreferenceSettings` instead. + */ + readonly theme_preference?: string; +} + +// From codersdk/workspaceproxy.go +export interface Region { + readonly id: string; + readonly name: string; + readonly display_name: string; + readonly icon_url: string; + readonly healthy: boolean; + /** + * PathAppURL is the URL to the base path for path apps. Optional + * unless wildcard_hostname is set. + * E.g. https://us.example.com + */ + readonly path_app_url: string; + /** + * WildcardHostname is the wildcard hostname for subdomain apps. + * E.g. *.us.example.com + * E.g. *--suffix.au.example.com + * Optional. Does not need to be on the same domain as PathAppURL. + */ + readonly wildcard_hostname: string; +} + +// From codersdk/workspaceproxy.go +export type RegionTypes = Region | WorkspaceProxy; + +// From codersdk/workspaceproxy.go +export interface RegionsResponse { + readonly regions: readonly R[]; +} + +// From codersdk/replicas.go +export interface Replica { + /** + * ID is the unique identifier for the replica. + */ + readonly id: string; + /** + * Hostname is the hostname of the replica. + */ + readonly hostname: string; + /** + * CreatedAt is the timestamp when the replica was first seen. + */ + readonly created_at: string; + /** + * RelayAddress is the accessible address to relay DERP connections. + */ + readonly relay_address: string; + /** + * RegionID is the region of the replica. + */ + readonly region_id: number; + /** + * Error is the replica error. + */ + readonly error: string; + /** + * DatabaseLatency is the latency in microseconds to the database. + */ + readonly database_latency: number; +} + +// From codersdk/users.go +/** + * RequestOneTimePasscodeRequest enables callers to request a one-time-passcode to change their password. + */ +export interface RequestOneTimePasscodeRequest { + readonly email: string; +} + +// From codersdk/workspaces.go +export interface ResolveAutostartResponse { + readonly parameter_mismatch: boolean; +} + +// From codersdk/audit.go +export type ResourceType = + | "ai_gateway_key" + | "ai_provider" + | "ai_provider_key" + | "ai_seat" + | "api_key" + | "chat" + | "chat_instruction_settings" + | "chat_model_config" + | "chat_operational_settings" + | "convert_login" + | "custom_role" + | "git_ssh_key" + | "group" + | "group_ai_budget" + | "health_settings" + | "idp_sync_settings_group" + | "idp_sync_settings_organization" + | "idp_sync_settings_role" + | "license" + | "mcp_server_config" + | "notification_template" + | "notifications_settings" + | "oauth2_provider_app" + | "oauth2_provider_app_secret" + | "oauth2_provider_settings" + | "organization" + | "organization_member" + | "prebuilds_settings" + | "task" + | "template" + | "template_version" + | "user" + | "user_ai_budget_override" + | "user_secret" + | "user_skill" + | "workspace" + | "workspace_agent" + | "workspace_app" + | "workspace_build" + | "workspace_proxy"; + +export const ResourceTypes: ResourceType[] = [ + "ai_gateway_key", + "ai_provider", + "ai_provider_key", + "ai_seat", + "api_key", + "chat", + "chat_instruction_settings", + "chat_model_config", + "chat_operational_settings", + "convert_login", + "custom_role", + "git_ssh_key", + "group", + "group_ai_budget", + "health_settings", + "idp_sync_settings_group", + "idp_sync_settings_organization", + "idp_sync_settings_role", + "license", + "mcp_server_config", + "notification_template", + "notifications_settings", + "oauth2_provider_app", + "oauth2_provider_app_secret", + "oauth2_provider_settings", + "organization", + "organization_member", + "prebuilds_settings", + "task", + "template", + "template_version", + "user", + "user_ai_budget_override", + "user_secret", + "user_skill", + "workspace", + "workspace_agent", + "workspace_app", + "workspace_build", + "workspace_proxy", +]; + +// From codersdk/client.go +/** + * Response represents a generic HTTP response. + */ +export interface Response { + /** + * Message is an actionable message that depicts actions the request took. + * These messages should be fully formed sentences with proper punctuation. + * Examples: + * - "A user has been created." + * - "Failed to create a user." + */ + readonly message: string; + /** + * Detail is a debug message that provides further insight into why the + * action failed. This information can be technical and a regular golang + * err.Error() text. + * - "database: too many open connections" + * - "stat: too many open files" + */ + readonly detail?: string; + /** + * Validations are form field-specific friendly error messages. They will be + * shown on a form field in the UI. These can also be used to add additional + * context if there is a set of errors in the primary 'Message'. + */ + readonly validations?: readonly ValidationError[]; +} + +// From codersdk/aitasks.go +/** + * ResumeTaskResponse represents the response from resuming a task. + */ +export interface ResumeTaskResponse { + readonly workspace_build: WorkspaceBuild | null; +} + +// From codersdk/deployment.go +/** + * RetentionConfig contains configuration for data retention policies. + * These settings control how long various types of data are retained in the database + * before being automatically purged. Setting a value to 0 disables retention for that + * data type (data is kept indefinitely). + */ +export interface RetentionConfig { + /** + * AuditLogs controls how long audit log entries are retained. + * Set to 0 to disable (keep indefinitely). + */ + readonly audit_logs: number; + /** + * ConnectionLogs controls how long connection log entries are retained. + * Set to 0 to disable (keep indefinitely). + */ + readonly connection_logs: number; + /** + * APIKeys controls how long expired API keys are retained before being deleted. + * Keys are only deleted if they have been expired for at least this duration. + * Defaults to 7 days to preserve existing behavior. + */ + readonly api_keys: number; + /** + * WorkspaceAgentLogs controls how long workspace agent logs are retained. + * Logs are deleted if the agent hasn't connected within this period. + * Logs from the latest build are always retained regardless of age. + * Defaults to 7 days to preserve existing behavior. + */ + readonly workspace_agent_logs: number; + /** + * BoundaryLogs controls how long boundary audit log entries are + * retained. Boundary logs record every HTTP request processed by + * a Boundary confinement proxy. Set to 0 to disable automatic + * deletion (keep indefinitely). Adjust to match your + * organization's regulatory requirements. + */ + readonly boundary_logs: number; +} + +// From codersdk/roles.go +/** + * Role is a longer form of SlimRole that includes permissions details. + */ +export interface Role { + readonly name: string; + readonly organization_id?: string; + readonly display_name: string; + readonly site_permissions: readonly Permission[]; + readonly user_permissions: readonly Permission[]; + /** + * OrganizationPermissions are specific for the organization in the field 'OrganizationID' above. + */ + readonly organization_permissions: readonly Permission[]; + /** + * OrganizationMemberPermissions are specific for the organization in the field 'OrganizationID' above. + */ + readonly organization_member_permissions: readonly Permission[]; +} + +// From codersdk/rbacroles.go +/** + * Ideally these roles would be generated from the rbac/roles.go package. + * @deprecated the agents-access role was removed. Coder Agents chat + * access is part of the organization-member permission floor, and + * servers without this built-in role reject assigning it. + */ +export const RoleAgentsAccess = "agents-access"; + +// From codersdk/rbacroles.go +/** + * Ideally these roles would be generated from the rbac/roles.go package. + */ +export const RoleAuditor = "auditor"; + +// From codersdk/rbacroles.go +/** + * Ideally these roles would be generated from the rbac/roles.go package. + */ +export const RoleMember = "member"; + +// From codersdk/rbacroles.go +/** + * Ideally these roles would be generated from the rbac/roles.go package. + */ +export const RoleOrganizationAdmin = "organization-admin"; + +// From codersdk/rbacroles.go +/** + * Ideally these roles would be generated from the rbac/roles.go package. + */ +export const RoleOrganizationAuditor = "organization-auditor"; + +// From codersdk/rbacroles.go +/** + * Ideally these roles would be generated from the rbac/roles.go package. + */ +export const RoleOrganizationMember = "organization-member"; + +// From codersdk/rbacroles.go +/** + * Ideally these roles would be generated from the rbac/roles.go package. + */ +export const RoleOrganizationTemplateAdmin = "organization-template-admin"; + +// From codersdk/rbacroles.go +/** + * Ideally these roles would be generated from the rbac/roles.go package. + */ +export const RoleOrganizationUserAdmin = "organization-user-admin"; + +// From codersdk/rbacroles.go +/** + * Ideally these roles would be generated from the rbac/roles.go package. + */ +export const RoleOrganizationWorkspaceAccess = "organization-workspace-access"; + +// From codersdk/rbacroles.go +/** + * Ideally these roles would be generated from the rbac/roles.go package. + */ +export const RoleOrganizationWorkspaceCreationBan = + "organization-workspace-creation-ban"; + +// From codersdk/rbacroles.go +/** + * Ideally these roles would be generated from the rbac/roles.go package. + */ +export const RoleOwner = "owner"; + +// From codersdk/idpsync.go +export interface RoleSyncSettings { + /** + * Field is the name of the claim field that specifies what organization roles + * a user should be given. If empty, no roles will be synced. + */ + readonly field: string; + /** + * Mapping is a map from OIDC groups to Coder organization roles. + */ + readonly mapping: Record; +} + +// From codersdk/rbacroles.go +/** + * Ideally these roles would be generated from the rbac/roles.go package. + */ +export const RoleTemplateAdmin = "template-admin"; + +// From codersdk/rbacroles.go +/** + * Ideally these roles would be generated from the rbac/roles.go package. + */ +export const RoleUserAdmin = "user-admin"; + +// From codersdk/deployment.go +/** + * SSHConfig is configuration the cli & vscode extension use for configuring + * ssh connections. + */ +export interface SSHConfig { + /** + * DeploymentName is the config-ssh Hostname prefix + */ + readonly DeploymentName: string; + /** + * SSHConfigOptions are additional options to add to the ssh config file. + * This will override defaults. + */ + readonly SSHConfigOptions: string; +} + +// From codersdk/deployment.go +export interface SSHConfigResponse { + /** + * HostnamePrefix is the prefix we append to workspace names for SSH hostnames. + * @deprecated use HostnameSuffix instead. + */ + readonly hostname_prefix: string; + /** + * HostnameSuffix is the suffix to append to workspace names for SSH hostnames. + */ + readonly hostname_suffix: string; + readonly ssh_config_options: Record; +} + +// From healthsdk/healthsdk.go +/** + * STUNReport contains information about a given node's STUN capabilities. + */ +export interface STUNReport { + readonly Enabled: boolean; + readonly CanSTUN: boolean; + readonly Error: string | null; +} + +// From codersdk/usersecretsimport.go +export type SecretsFileFormat = "env" | "json" | "yaml"; + +export const SecretsFileFormats: SecretsFileFormat[] = ["env", "json", "yaml"]; + +// From serpent/serpent.go +/** + * Annotations is an arbitrary key-mapping used to extend the Option and Command types. + * Its methods won't panic if the map is nil. + */ +export type SerpentAnnotations = Record; + +// From serpent/serpent.go +/** + * Group describes a hierarchy of groups that an option or command belongs to. + */ +export interface SerpentGroup { + readonly parent?: SerpentGroup; + readonly name?: string; + readonly yaml?: string; + readonly description?: string; +} + +// From serpent/option.go +/** + * Option is a configuration option for a CLI application. + */ +export interface SerpentOption { + readonly name?: string; + readonly description?: string; + /** + * Required means this value must be set by some means. It requires + * `ValueSource != ValueSourceNone` + * If `Default` is set, then `Required` is ignored. + */ + readonly required?: boolean; + /** + * Flag is the long name of the flag used to configure this option. If unset, + * flag configuring is disabled. + */ + readonly flag?: string; + /** + * FlagShorthand is the one-character shorthand for the flag. If unset, no + * shorthand is used. + */ + readonly flag_shorthand?: string; + /** + * Env is the environment variable used to configure this option. If unset, + * environment configuring is disabled. + */ + readonly env?: string; + /** + * YAML is the YAML key used to configure this option. If unset, YAML + * configuring is disabled. + */ + readonly yaml?: string; + /** + * Default is parsed into Value if set. + * Must be `""` if `DefaultFn` != nil + */ + readonly default?: string; + /** + * Value includes the types listed in values.go. + */ + // interface type, falling back to unknown + // this is likely an enum in an external package "github.com/spf13/pflag.Value" + readonly value?: unknown; + /** + * Annotations enable extensions to serpent higher up in the stack. It's useful for + * help formatting and documentation generation. + */ + readonly annotations?: SerpentAnnotations; + /** + * Group is a group hierarchy that helps organize this option in help, configs + * and other documentation. + */ + readonly group?: SerpentGroup; + /** + * UseInstead is a list of options that should be used instead of this one. + * The field is used to generate a deprecation warning. + */ + readonly use_instead?: readonly SerpentOption[]; + readonly hidden?: boolean; + readonly value_source?: SerpentValueSource; +} + +// From serpent/option.go +/** + * OptionSet is a group of options that can be applied to a command. + */ +export type SerpentOptionSet = readonly SerpentOption[]; + +// From serpent/values.go +export type SerpentStruct = T; + +// From serpent/option.go +export type SerpentValueSource = string; + +// From derp/derp_client.go +/** + * ServerInfoMessage is sent by the server upon first connect. + */ +export interface ServerInfoMessage { + /** + * TokenBucketBytesPerSecond is how many bytes per second the + * server says it will accept, including all framing bytes. + * + * Zero means unspecified. There might be a limit, but the + * client need not try to respect it. + */ + readonly TokenBucketBytesPerSecond: number; + /** + * TokenBucketBytesBurst is how many bytes the server will + * allow to burst, temporarily violating + * TokenBucketBytesPerSecond. + * + * Zero means unspecified. There might be a limit, but the + * client need not try to respect it. + */ + readonly TokenBucketBytesBurst: number; +} + +// From codersdk/serversentevents.go +export interface ServerSentEvent { + readonly type: ServerSentEventType; + // empty interface{} type, falling back to unknown + readonly data: unknown; +} + +// From codersdk/serversentevents.go +export type ServerSentEventType = "data" | "error" | "ping"; + +export const ServerSentEventTypes: ServerSentEventType[] = [ + "data", + "error", + "ping", +]; + +// From codersdk/deployment.go +/** + * @deprecated ServiceBannerConfig has been renamed to BannerConfig. + */ +export interface ServiceBannerConfig { + readonly enabled: boolean; + readonly message?: string; + readonly background_color?: string; +} + +// From codersdk/deployment.go +export interface SessionCountDeploymentStats { + readonly vscode: number; + readonly ssh: number; + readonly jetbrains: number; + readonly reconnecting_pty: number; +} + +// From codersdk/deployment.go +/** + * SessionLifetime refers to "sessions" authenticating into Coderd. Coder has + * multiple different session types: api keys, tokens, workspace app tokens, + * agent tokens, etc. This configuration struct should be used to group all + * settings referring to any of these session lifetime controls. + * TODO: These config options were created back when coder only had api keys. + * Today, the config is ambigously used for all of them. For example: + * - cli based api keys ignore all settings + * - login uses the default lifetime, not the MaximumTokenDuration + * - Tokens use the Default & MaximumTokenDuration + * - ... etc ... + * The rational behind each decision is undocumented. The naming behind these + * config options is also confusing without any clear documentation. + * 'CreateAPIKey' is used to make all sessions, and it's parameters are just + * 'LifetimeSeconds' and 'DefaultLifetime'. Which does not directly correlate to + * the config options here. + */ +export interface SessionLifetime { + /** + * DisableExpiryRefresh will disable automatically refreshing api + * keys when they are used from the api. This means the api key lifetime at + * creation is the lifetime of the api key. + */ + readonly disable_expiry_refresh?: boolean; + /** + * DefaultDuration is only for browser, workspace app and oauth sessions. + */ + readonly default_duration: number; + /** + * RefreshDefaultDuration is the default lifetime for OAuth2 refresh tokens. + * This should generally be longer than access token lifetimes to allow + * refreshing after access token expiry. + */ + readonly refresh_default_duration?: number; + readonly default_token_lifetime?: number; + readonly max_token_lifetime?: number; + readonly max_admin_token_lifetime?: number; +} + +// From codersdk/client.go +/** + * SessionTokenHeader is the custom header to use for authentication. + */ +export const SessionTokenHeader = "Coder-Session-Token"; + +// From codersdk/workspacesharing.go +export type ShareableWorkspaceOwners = "everyone" | "none" | "service_accounts"; + +export const ShareableWorkspaceOwnerses: ShareableWorkspaceOwners[] = [ + "everyone", + "none", + "service_accounts", +]; + +// From codersdk/workspaces.go +export interface SharedWorkspaceActor { + readonly id: string; + readonly actor_type: SharedWorkspaceActorType; + readonly name: string; + readonly avatar_url?: string; + readonly roles: readonly WorkspaceRole[]; +} + +// From codersdk/workspaces.go +export type SharedWorkspaceActorType = "group" | "user"; + +export const SharedWorkspaceActorTypes: SharedWorkspaceActorType[] = [ + "group", + "user", +]; + +// From codersdk/client.go +/** + * SignedAppTokenCookie is the name of the cookie that stores a temporary + * JWT that can be used to authenticate instead of the app session token. + *nolint:gosec + */ +export const SignedAppTokenCookie = "coder_signed_app_token"; + +// From codersdk/client.go +/** + * SignedAppTokenQueryParameter is the name of the query parameter that + * stores a temporary JWT that can be used to authenticate instead of the + * session token. This is only acceptable on reconnecting-pty requests, not + * apps. + * + * It has a random suffix to avoid conflict with user query parameters on + * apps. + *nolint:gosec + */ +export const SignedAppTokenQueryParameter = "coder_signed_app_token_23db1dde"; + +// From codersdk/roles.go +/** + * SlimRole omits permission information from a role. + * At present, this is because our apis do not return permission information, + * and it would require extra db calls to fetch this information. The UI does + * not need it, so most api calls will use this structure that omits information. + */ +export interface SlimRole { + readonly name: string; + readonly display_name: string; + readonly organization_id?: string; +} + +// From codersdk/deployment.go +export interface StatsCollectionConfig { + readonly usage_stats: UsageStatsConfig; +} + +// From codersdk/chats.go +/** + * StreamChatOptions are optional parameters for StreamChat. + */ +export interface StreamChatOptions { + /** + * AfterID limits the initial snapshot to messages created + * after the given ID. This is useful for relay connections + * that only need live message_part events and can skip the + * full message history. + */ + readonly AfterID: number | null; +} + +// From codersdk/client.go +/** + * SubdomainAppSessionTokenCookie is the name of the cookie that stores an + * application-scoped API token on subdomain app domains (both the primary + * and proxies). + * + * To avoid conflicts between multiple proxies, we append an underscore and + * a hash suffix to the cookie name. + *nolint:gosec + */ +export const SubdomainAppSessionTokenCookie = + "coder_subdomain_app_session_token"; + +// From codersdk/chats.go +/** + * SubmitToolResultsRequest is the body for POST /chats/{id}/tool-results. + */ +export interface SubmitToolResultsRequest { + readonly results: readonly ToolResult[]; +} + +// From codersdk/deployment.go +export interface SupportConfig { + readonly links: SerpentStruct; +} + +// From codersdk/deployment.go +export interface SwaggerConfig { + readonly enable: boolean; +} + +// From codersdk/deployment.go +export interface TLSConfig { + readonly enable: boolean; + readonly address: string; + readonly redirect_http: boolean; + readonly cert_file: string; + readonly client_auth: string; + readonly client_ca_file: string; + readonly key_file: string; + readonly min_version: string; + readonly client_cert_file: string; + readonly client_key_file: string; + readonly supported_ciphers: string; + readonly allow_insecure_ciphers: boolean; +} + +// From tailcfg/derpmap.go +/** + * DERPNode describes a DERP packet relay node running within a DERPRegion. + */ +export interface TailDERPNode { + /** + * Name is a unique node name (across all regions). + * It is not a host name. + * It's typically of the form "1b", "2a", "3b", etc. (region + * ID + suffix within that region) + */ + readonly Name: string; + /** + * RegionID is the RegionID of the DERPRegion that this node + * is running in. + */ + readonly RegionID: number; + /** + * HostName is the DERP node's hostname. + * + * It is required but need not be unique; multiple nodes may + * have the same HostName but vary in configuration otherwise. + */ + readonly HostName: string; + /** + * CertName optionally specifies the expected TLS cert common + * name. If empty, HostName is used. If CertName is non-empty, + * HostName is only used for the TCP dial (if IPv4/IPv6 are + * not present) + TLS ClientHello. + */ + readonly CertName?: string; + /** + * IPv4 optionally forces an IPv4 address to use, instead of using DNS. + * If empty, A record(s) from DNS lookups of HostName are used. + * If the string is not an IPv4 address, IPv4 is not used; the + * conventional string to disable IPv4 (and not use DNS) is + * "none". + */ + readonly IPv4?: string; + /** + * IPv6 optionally forces an IPv6 address to use, instead of using DNS. + * If empty, AAAA record(s) from DNS lookups of HostName are used. + * If the string is not an IPv6 address, IPv6 is not used; the + * conventional string to disable IPv6 (and not use DNS) is + * "none". + */ + readonly IPv6?: string; + /** + * Port optionally specifies a STUN port to use. + * Zero means 3478. + * To disable STUN on this node, use -1. + */ + readonly STUNPort?: number; + /** + * STUNOnly marks a node as only a STUN server and not a DERP + * server. + */ + readonly STUNOnly?: boolean; + /** + * DERPPort optionally provides an alternate TLS port number + * for the DERP HTTPS server. + * + * If zero, 443 is used. + */ + readonly DERPPort?: number; + /** + * InsecureForTests is used by unit tests to disable TLS verification. + * It should not be set by users. + */ + readonly InsecureForTests?: boolean; + /** + * ForceHTTP is used by unit tests to force HTTP. + * It should not be set by users. + */ + readonly ForceHTTP?: boolean; + /** + * STUNTestIP is used in tests to override the STUN server's IP. + * If empty, it's assumed to be the same as the DERP server. + */ + readonly STUNTestIP?: string; + /** + * CanPort80 specifies whether this DERP node is accessible over HTTP + * on port 80 specifically. This is used for captive portal checks. + */ + readonly CanPort80?: boolean; +} + +// From tailcfg/derpmap.go +/** + * DERPRegion is a geographic region running DERP relay node(s). + * + * Client nodes discover which region they're closest to, advertise + * that "home" DERP region (previously called "home node", when there + * was only 1 node per region) and maintain a persistent connection + * that region as long as it's the closest. Client nodes will further + * connect to other regions as necessary to communicate with peers + * advertising other regions as their homes. + */ +export interface TailDERPRegion { + /** + * EmbeddedRelay is true when the region is bundled with the Coder + * control plane. + */ + readonly EmbeddedRelay: boolean; + /** + * RegionID is a unique integer for a geographic region. + * + * It corresponds to the legacy derpN.tailscale.com hostnames + * used by older clients. (Older clients will continue to resolve + * derpN.tailscale.com when contacting peers, rather than use + * the server-provided DERPMap) + * + * RegionIDs must be non-zero, positive, and guaranteed to fit + * in a JavaScript number. + * + * RegionIDs in range 900-999 are reserved for end users to run their + * own DERP nodes. + */ + readonly RegionID: number; + /** + * RegionCode is a short name for the region. It's usually a popular + * city or airport code in the region: "nyc", "sf", "sin", + * "fra", etc. + */ + readonly RegionCode: string; + /** + * RegionName is a long English name for the region: "New York City", + * "San Francisco", "Singapore", "Frankfurt", etc. + */ + readonly RegionName: string; + /** + * Avoid is whether the client should avoid picking this as its home + * region. The region should only be used if a peer is there. + * Clients already using this region as their home should migrate + * away to a new region without Avoid set. + */ + readonly Avoid?: boolean; + /** + * Nodes are the DERP nodes running in this region, in + * priority order for the current client. Client TLS + * connections should ideally only go to the first entry + * (falling back to the second if necessary). STUN packets + * should go to the first 1 or 2. + * + * If nodes within a region route packets amongst themselves, + * but not to other regions. That said, each user/domain + * should get a the same preferred node order, so if all nodes + * for a user/network pick the first one (as they should, when + * things are healthy), the inter-cluster routing is minimal + * to zero. + */ + readonly Nodes: readonly TailDERPNode[]; +} + +// From codersdk/aitasks.go +/** + * Task represents a task. + */ +export interface Task { + readonly id: string; + readonly organization_id: string; + readonly owner_id: string; + readonly owner_name: string; + readonly owner_avatar_url?: string; + readonly name: string; + readonly display_name: string; + readonly template_id: string; + readonly template_version_id: string; + readonly template_name: string; + readonly template_display_name: string; + readonly template_icon: string; + readonly workspace_id: string | null; + readonly workspace_name: string; + readonly workspace_status?: WorkspaceStatus; + readonly workspace_build_number?: number; + readonly workspace_agent_id: string | null; + readonly workspace_agent_lifecycle: WorkspaceAgentLifecycle | null; + readonly workspace_agent_health: WorkspaceAgentHealth | null; + readonly workspace_app_id: string | null; + readonly initial_prompt: string; + readonly status: TaskStatus; + readonly current_state: TaskStateEntry | null; + readonly created_at: string; + readonly updated_at: string; +} + +// From codersdk/aitasks.go +/** + * TaskLogEntry represents a single log entry for a task. + */ +export interface TaskLogEntry { + readonly id: number; + readonly content: string; + readonly type: TaskLogType; + readonly time: string; +} + +// From codersdk/aitasks.go +export type TaskLogType = "input" | "output"; + +export const TaskLogTypes: TaskLogType[] = ["input", "output"]; + +// From codersdk/aitasks.go +/** + * TaskLogsResponse contains task logs and metadata. When snapshot is false, + * logs are fetched live from the task app. When snapshot is true, logs are + * fetched from a stored snapshot captured during pause. + */ +export interface TaskLogsResponse { + readonly logs: readonly TaskLogEntry[]; + readonly snapshot?: boolean; + readonly snapshot_at?: string; +} + +// From codersdk/aitasks.go +/** + * TaskSendRequest is used to send task input to the tasks sidebar app. + */ +export interface TaskSendRequest { + readonly input: string; +} + +// From codersdk/aitasks.go +export type TaskState = "complete" | "failed" | "idle" | "working"; + +// From codersdk/aitasks.go +/** + * TaskStateEntry represents a single entry in the task's state history. + */ +export interface TaskStateEntry { + readonly timestamp: string; + readonly state: TaskState; + readonly message: string; + readonly uri: string; +} + +export const TaskStates: TaskState[] = [ + "complete", + "failed", + "idle", + "working", +]; + +// From codersdk/aitasks.go +export type TaskStatus = + | "active" + | "error" + | "initializing" + | "paused" + | "pending" + | "unknown"; + +export const TaskStatuses: TaskStatus[] = [ + "active", + "error", + "initializing", + "paused", + "pending", + "unknown", +]; + +// From codersdk/aitasks.go +/** + * TasksFilter filters the list of tasks. + */ +export interface TasksFilter { + /** + * Owner can be a username, UUID, or "me". + */ + readonly owner?: string; + /** + * Organization can be an organization name or UUID. + */ + readonly organization?: string; + /** + * Status filters the tasks by their task status. + */ + readonly status?: TaskStatus; + /** + * FilterQuery allows specifying a raw filter query. + */ + readonly filter_query?: string; +} + +// From codersdk/aitasks.go +/** + * TaskListResponse is the response shape for tasks list. + */ +export interface TasksListResponse { + readonly tasks: readonly Task[]; + readonly count: number; +} + +// From codersdk/deployment.go +export interface TelemetryConfig { + readonly enable: boolean; + readonly trace: boolean; + readonly url: string; +} + +// From codersdk/templates.go +/** + * Template is the JSON representation of a Coder template. This type matches the + * database object for now, but is abstracted for ease of change later on. + */ +export interface Template { + readonly id: string; + readonly created_at: string; + readonly updated_at: string; + readonly organization_id: string; + readonly organization_name: string; + readonly organization_display_name: string; + readonly organization_icon: string; + readonly name: string; + readonly display_name: string; + readonly provisioner: ProvisionerType; + readonly active_version_id: string; + /** + * ActiveUserCount is set to -1 when loading. + */ + readonly active_user_count: number; + readonly build_time_stats: TemplateBuildTimeStats; + readonly description: string; + readonly deprecated: boolean; + readonly deprecation_message: string; + readonly deleted: boolean; + readonly icon: string; + readonly default_ttl_ms: number; + readonly activity_bump_ms: number; + /** + * TimeTilAutostopNotifyMillis is the duration before the workspace's + * autostop deadline at which a reminder notification is sent. 0 disables + * the notification. + */ + readonly time_til_autostop_notify_ms: number; + /** + * AutostopRequirement and AutostartRequirement are enterprise features. Its + * value is only used if your license is entitled to use the advanced template + * scheduling feature. + */ + readonly autostop_requirement: TemplateAutostopRequirement; + readonly autostart_requirement: TemplateAutostartRequirement; + readonly created_by_id: string; + readonly created_by_name: string; + /** + * AllowUserAutostart and AllowUserAutostop are enterprise-only. Their + * values are only used if your license is entitled to use the advanced + * template scheduling feature. + */ + readonly allow_user_autostart: boolean; + readonly allow_user_autostop: boolean; + readonly allow_user_cancel_workspace_jobs: boolean; + /** + * FailureTTLMillis, TimeTilDormantMillis, and TimeTilDormantAutoDeleteMillis are enterprise-only. Their + * values are used if your license is entitled to use the advanced + * template scheduling feature. + */ + readonly failure_ttl_ms: number; + readonly time_til_dormant_ms: number; + readonly time_til_dormant_autodelete_ms: number; + /** + * RequireActiveVersion mandates that workspaces are built with the active + * template version. + */ + readonly require_active_version: boolean; + readonly max_port_share_level: WorkspaceAgentPortShareLevel; + readonly cors_behavior: CORSBehavior; + readonly use_classic_parameter_flow: boolean; + readonly agents_allowed: boolean; + /** + * DisableModuleCache disables the use of cached Terraform modules during + * provisioning. + */ + readonly disable_module_cache: boolean; +} + +// From codersdk/templates.go +export interface TemplateACL { + readonly users: readonly TemplateUser[]; + readonly group: readonly TemplateGroup[]; +} + +// From codersdk/insights.go +/** + * TemplateAppUsage shows the usage of an app for one or more templates. + */ +export interface TemplateAppUsage { + readonly template_ids: readonly string[]; + readonly type: TemplateAppsType; + readonly display_name: string; + readonly slug: string; + readonly icon: string; + readonly seconds: number; + readonly times_used: number; +} + +// From codersdk/insights.go +export type TemplateAppsType = "app" | "builtin"; + +export const TemplateAppsTypes: TemplateAppsType[] = ["app", "builtin"]; + +// From codersdk/templates.go +export interface TemplateAutostartRequirement { + /** + * DaysOfWeek is a list of days of the week in which autostart is allowed + * to happen. If no days are specified, autostart is not allowed. + */ + readonly days_of_week: readonly string[]; +} + +// From codersdk/templates.go +export interface TemplateAutostopRequirement { + /** + * DaysOfWeek is a list of days of the week on which restarts are required. + * Restarts happen within the user's quiet hours (in their configured + * timezone). If no days are specified, restarts are not required. Weekdays + * cannot be specified twice. + * + * Restarts will only happen on weekdays in this list on weeks which line up + * with Weeks. + */ + readonly days_of_week: readonly string[]; + /** + * Weeks is the number of weeks between required restarts. Weeks are synced + * across all workspaces (and Coder deployments) using modulo math on a + * hardcoded epoch week of January 2nd, 2023 (the first Monday of 2023). + * Values of 0 or 1 indicate weekly restarts. Values of 2 indicate + * fortnightly restarts, etc. + */ + readonly weeks: number; +} + +// From codersdk/templates.go +export type TemplateBuildTimeStats = Record< + WorkspaceTransition, + TransitionStats +>; + +// From codersdk/templatebuilder.go +/** + * TemplateBuilderBase is the API response type for a base template + * returned by GET /api/v2/templatebuilder/bases. + */ +export interface TemplateBuilderBase { + readonly id: string; + readonly name: string; + readonly description: string; + readonly icon: string; + readonly os: string; + readonly variables: readonly TemplateBuilderModuleVariable[]; + readonly prerequisites: string; +} + +// From codersdk/templatebuilder.go +/** + * TemplateBuilderBasesResponse is the response body for listing template builder bases. + */ +export interface TemplateBuilderBasesResponse { + readonly bases: readonly TemplateBuilderBase[]; +} + +// From codersdk/templatebuilder.go +/** + * TemplateBuilderComposeModule identifies a module and its variable + * values for the compose request. + */ +export interface TemplateBuilderComposeModule { + readonly id: string; + readonly variables?: Record; +} + +// From codersdk/templatebuilder.go +/** + * TemplateBuilderComposeRequest is the request body for + * POST /api/v2/templatebuilder/compose. + */ +export interface TemplateBuilderComposeRequest { + readonly base_template_id: string; + readonly base_variable_values?: Record; + readonly modules: readonly TemplateBuilderComposeModule[]; +} + +// From codersdk/deployment.go +export interface TemplateBuilderConfig { + readonly disabled?: boolean; + readonly registry_url?: string; +} + +// From codersdk/templatebuilder.go +/** + * TemplateBuilderCreateTemplateRequest is the request body for + * POST /api/v2/templatebuilder/compose/template. + */ +export interface TemplateBuilderCreateTemplateRequest { + readonly base_template_id: string; + readonly base_variable_values?: Record; + readonly modules: readonly TemplateBuilderComposeModule[]; + readonly organization_id: string; + readonly name: string; + readonly display_name?: string; + readonly description?: string; + readonly icon?: string; + readonly provisioner_tags?: Record; +} + +// From codersdk/templatebuilder.go +/** + * TemplateBuilderCreateTemplateResponse is the response body for + * POST /api/v2/templatebuilder/compose/template. + */ +export interface TemplateBuilderCreateTemplateResponse { + readonly template: Template; +} + +// From codersdk/templatebuilder.go +/** + * TemplateBuilderModule is the API response type returned by + * GET /api/v2/templatebuilder/modules. The Version field is + * populated from the catalog manifest's PinnedVersion at serving time. + */ +export interface TemplateBuilderModule { + readonly id: string; + readonly display_name: string; + readonly description: string; + readonly icon: string; + readonly category: string; + readonly version: string; + readonly compatible_os: readonly string[]; + readonly conflicts_with: readonly string[]; + readonly variables: readonly TemplateBuilderModuleVariable[]; +} + +// From codersdk/templatebuilder.go +export interface TemplateBuilderModuleVariable { + readonly name: string; + readonly type: TemplateBuilderVariableType; + readonly description: string; + readonly default?: Record; + readonly required: boolean; + readonly sensitive: boolean; +} + +// From codersdk/templatebuilder.go +/** + * TemplateBuilderModulesResponse is the response body for listing template builder modules. + */ +export interface TemplateBuilderModulesResponse { + readonly modules: readonly TemplateBuilderModule[]; +} + +// From codersdk/templatebuilder.go +export type TemplateBuilderSessionEventType = + | "compose_completion" + | "wizard_entry"; + +export const TemplateBuilderSessionEventTypes: TemplateBuilderSessionEventType[] = + ["compose_completion", "wizard_entry"]; + +// From codersdk/templatebuilder.go +/** + * TemplateBuilderSessionRequest is the request body for + * POST /api/v2/templatebuilder/sessions. + */ +export interface TemplateBuilderSessionRequest { + readonly session_id: string; + readonly event_type: TemplateBuilderSessionEventType; + readonly base_template_id?: string; + readonly module_ids?: readonly string[]; + readonly duration_seconds?: number; + readonly success?: boolean; +} + +// From codersdk/templatebuilder.go +export type TemplateBuilderVariableType = "bool" | "number" | "string"; + +export const TemplateBuilderVariableTypes: TemplateBuilderVariableType[] = [ + "bool", + "number", + "string", +]; + +// From codersdk/insights.go +/** + * Enums define the display name of the builtin app reported. + */ +export const TemplateBuiltinAppDisplayNameJetBrains = "JetBrains"; + +// From codersdk/insights.go +/** + * Enums define the display name of the builtin app reported. + */ +export const TemplateBuiltinAppDisplayNameSFTP = "SFTP"; + +// From codersdk/insights.go +/** + * Enums define the display name of the builtin app reported. + */ +export const TemplateBuiltinAppDisplayNameSSH = "SSH"; + +// From codersdk/insights.go +/** + * Enums define the display name of the builtin app reported. + */ +export const TemplateBuiltinAppDisplayNameVSCode = "Visual Studio Code"; + +// From codersdk/insights.go +/** + * Enums define the display name of the builtin app reported. + */ +export const TemplateBuiltinAppDisplayNameWebTerminal = "Web Terminal"; + +// From codersdk/templates.go +export interface TemplateExample { + readonly id: string; + readonly url: string; + readonly name: string; + readonly description: string; + readonly icon: string; + readonly tags: readonly string[]; + readonly markdown: string; +} + +// From codersdk/organizations.go +export interface TemplateFilter { + readonly q?: string; +} + +// From codersdk/templates.go +export interface TemplateGroup extends Group { + readonly role: TemplateRole; +} + +// From codersdk/insights.go +/** + * TemplateInsightsIntervalReport is the report from the template insights + * endpoint for a specific interval. + */ +export interface TemplateInsightsIntervalReport { + readonly start_time: string; + readonly end_time: string; + readonly template_ids: readonly string[]; + readonly interval: InsightsReportInterval; + readonly active_users: number; +} + +// From codersdk/insights.go +/** + * TemplateInsightsReport is the report from the template insights endpoint. + */ +export interface TemplateInsightsReport { + readonly start_time: string; + readonly end_time: string; + readonly template_ids: readonly string[]; + readonly active_users: number; + readonly apps_usage: readonly TemplateAppUsage[]; + readonly parameters_usage: readonly TemplateParameterUsage[]; +} + +// From codersdk/insights.go +export interface TemplateInsightsRequest { + readonly start_time: string; + readonly end_time: string; + readonly template_ids: readonly string[]; + readonly interval: InsightsReportInterval; + readonly sections: readonly TemplateInsightsSection[]; +} + +// From codersdk/insights.go +/** + * TemplateInsightsResponse is the response from the template insights endpoint. + */ +export interface TemplateInsightsResponse { + readonly report?: TemplateInsightsReport; + readonly interval_reports?: readonly TemplateInsightsIntervalReport[]; +} + +// From codersdk/insights.go +export type TemplateInsightsSection = "interval_reports" | "report"; + +export const TemplateInsightsSections: TemplateInsightsSection[] = [ + "interval_reports", + "report", +]; + +// From codersdk/insights.go +/** + * TemplateParameterUsage shows the usage of a parameter for one or more + * templates. + */ +export interface TemplateParameterUsage { + readonly template_ids: readonly string[]; + readonly display_name: string; + readonly name: string; + readonly type: string; + readonly description: string; + readonly options?: readonly TemplateVersionParameterOption[]; + readonly values: readonly TemplateParameterValue[]; +} + +// From codersdk/insights.go +/** + * TemplateParameterValue shows the usage of a parameter value for one or more + * templates. + */ +export interface TemplateParameterValue { + readonly value: string; + readonly count: number; +} + +// From codersdk/templates.go +export type TemplateRole = "admin" | "" | "use"; + +export const TemplateRoles: TemplateRole[] = ["admin", "", "use"]; + +// From codersdk/templates.go +export interface TemplateUser extends User { + readonly role: TemplateRole; +} + +// From codersdk/templateversions.go +/** + * TemplateVersion represents a single version of a template. + */ +export interface TemplateVersion { + readonly id: string; + readonly template_id?: string; + readonly organization_id?: string; + readonly created_at: string; + readonly updated_at: string; + readonly name: string; + readonly message: string; + readonly job: ProvisionerJob; + readonly readme: string; + readonly created_by: MinimalUser; + readonly archived: boolean; + readonly warnings?: readonly TemplateVersionWarning[]; + readonly matched_provisioners?: MatchedProvisioners; + readonly has_external_agent: boolean; +} + +// From codersdk/templateversions.go +export interface TemplateVersionExternalAuth { + readonly id: string; + readonly type: string; + readonly display_name: string; + readonly display_icon: string; + readonly authenticate_url: string; + readonly authenticated: boolean; + readonly optional?: boolean; +} + +// From codersdk/templateversions.go +/** + * TemplateVersionParameter represents a parameter for a template version. + */ +export interface TemplateVersionParameter { + readonly name: string; + readonly display_name?: string; + readonly description: string; + readonly description_plaintext: string; + readonly type: string; + /** + * FormType has an enum value of empty string, `""`. + * Keep the leading comma in the enums struct tag. + */ + readonly form_type: string; + readonly mutable: boolean; + readonly default_value: string; + readonly icon: string; + readonly options: readonly TemplateVersionParameterOption[]; + readonly validation_error?: string; + readonly validation_regex?: string; + readonly validation_min?: number; + readonly validation_max?: number; + readonly validation_monotonic?: ValidationMonotonicOrder; + readonly required: boolean; + readonly ephemeral: boolean; +} + +// From codersdk/templateversions.go +/** + * TemplateVersionParameterOption represents a selectable option for a template parameter. + */ +export interface TemplateVersionParameterOption { + readonly name: string; + readonly description: string; + readonly value: string; + readonly icon: string; +} + +// From codersdk/templateversions.go +/** + * TemplateVersionVariable represents a managed template variable. + */ +export interface TemplateVersionVariable { + readonly name: string; + readonly description: string; + readonly type: string; + readonly value: string; + readonly default_value: string; + readonly required: boolean; + readonly sensitive: boolean; +} + +// From codersdk/templateversions.go +export type TemplateVersionWarning = "UNSUPPORTED_WORKSPACES"; + +export const TemplateVersionWarnings: TemplateVersionWarning[] = [ + "UNSUPPORTED_WORKSPACES", +]; + +// From codersdk/templates.go +/** + * TemplateVersionsByTemplateRequest defines the request parameters for + * TemplateVersionsByTemplate. + */ +export interface TemplateVersionsByTemplateRequest extends Pagination { + readonly template_id: string; + readonly include_archived: boolean; +} + +// From codersdk/users.go +export type TerminalFontName = + | "fira-code" + | "geist-mono" + | "ibm-plex-mono" + | "jetbrains-mono" + | "source-code-pro" + | ""; + +export const TerminalFontNames: TerminalFontName[] = [ + "fira-code", + "geist-mono", + "ibm-plex-mono", + "jetbrains-mono", + "source-code-pro", + "", +]; + +// From codersdk/users.go +export type ThemeMode = "single" | "sync" | ""; + +export const ThemeModes: ThemeMode[] = ["single", "sync", ""]; + +// From codersdk/users.go +export type ThinkingDisplayMode = + | "always_collapsed" + | "always_expanded" + | "auto" + | "preview"; + +export const ThinkingDisplayModes: ThinkingDisplayMode[] = [ + "always_collapsed", + "always_expanded", + "auto", + "preview", +]; + +// From codersdk/workspacebuilds.go +export type TimingStage = + | "apply" + | "connect" + | "cron" + | "graph" + | "init" + | "plan" + | "start" + | "stop"; + +export const TimingStages: TimingStage[] = [ + "apply", + "connect", + "cron", + "graph", + "init", + "plan", + "start", + "stop", +]; + +// From codersdk/apikey.go +export interface TokenConfig { + readonly max_token_lifetime: number; +} + +// From codersdk/apikey.go +export interface TokensFilter { + readonly include_all: boolean; + readonly include_expired: boolean; +} + +// From codersdk/chats.go +/** + * ToolResult is the client's response to a dynamic tool call. + */ +export interface ToolResult { + readonly tool_call_id: string; + readonly output: Record; + readonly is_error: boolean; +} + +// From codersdk/deployment.go +export interface TraceConfig { + readonly enable: boolean; + readonly honeycomb_api_key: string; + readonly capture_logs: boolean; + readonly data_dog: boolean; +} + +// From codersdk/templates.go +export interface TransitionStats { + readonly P50: number | null; + readonly P95: number | null; +} + +// From codersdk/aiproviders.go +/** + * UpdateAIProviderRequest is the payload for partially updating an + * AI provider. At least one field must be non-nil. Pointer fields + * distinguish "not sent" (nil) from "set to empty/zero" (a pointer + * to the zero value). When APIKeys is non-nil, the supplied list + * describes the post-patch state of the key set; see + * AIProviderKeyMutation for the per-entry semantics. An empty slice + * clears all keys. + */ +export interface UpdateAIProviderRequest { + readonly display_name?: string; + readonly icon?: string; + readonly enabled?: boolean; + readonly base_url?: string; + readonly api_keys?: AIProviderKeyMutation[]; + readonly settings?: AIProviderSettings; +} + +// From codersdk/templates.go +export interface UpdateActiveTemplateVersion { + readonly id: string; +} + +// From codersdk/chats.go +/** + * UpdateAdvisorConfigRequest is the request body for updating advisor runtime configuration. + */ +export interface UpdateAdvisorConfigRequest { + readonly max_uses_per_run: number; + readonly max_output_tokens: number; + /** + * @deprecated moved to the organization model override endpoint. + */ + readonly model_config_id?: string; + /** + * @deprecated moved to the organization model override endpoint. + */ + readonly reasoning_effort?: string; +} + +// From codersdk/deployment.go +export interface UpdateAppearanceConfig { + readonly application_name: string; + readonly logo_url: string; + /** + * @deprecated ServiceBanner has been replaced by AnnouncementBanners. + */ + readonly service_banner: BannerConfig; + readonly announcement_banners: readonly BannerConfig[]; + readonly codernauts_enabled: boolean; +} + +// From codersdk/chats.go +export interface UpdateChatACL { + readonly user_roles?: Record; + readonly group_roles?: Record; +} + +// From codersdk/chats.go +/** + * UpdateChatAutoArchiveDaysRequest is a request to update the chat + * auto-archive period. + */ +export interface UpdateChatAutoArchiveDaysRequest { + readonly auto_archive_days: number; +} + +// From codersdk/chats.go +/** + * UpdateChatComputerUseProviderRequest is the request to update the computer use + * provider setting. + */ +export interface UpdateChatComputerUseProviderRequest { + readonly provider: ChatComputerUseProvider; +} + +// From codersdk/chats.go +/** + * UpdateChatDebugLoggingAllowUsersRequest is the admin request to + * toggle whether users may opt into chat debug logging. + */ +export interface UpdateChatDebugLoggingAllowUsersRequest { + readonly allow_users: boolean; +} + +// From codersdk/chats.go +/** + * UpdateChatDebugRetentionDaysRequest is a request to update the chat + * debug run retention period. + */ +export interface UpdateChatDebugRetentionDaysRequest { + readonly debug_retention_days: number; +} + +// From codersdk/chats.go +/** + * UpdateChatModelACLRequest is a sparse update of a chat model ACL. Only the + * listed principals change. ChatRoleDeleted removes an entry, while an omitted + * map or principal is unchanged. + */ +export interface UpdateChatModelACLRequest { + readonly user_roles?: Record; + readonly group_roles?: Record; +} + +// From codersdk/chats.go +/** + * UpdateChatModelOverrideRequest is the request body for updating the chat + * model override configuration endpoint. + */ +export interface UpdateChatModelOverrideRequest { + readonly model_config_id: string; + readonly reasoning_effort?: string; +} + +// From codersdk/chats.go +/** + * UpdateChatModelRequest updates a ChatModel. Empty Model and DisplayName + * values preserve the stored values. Nil pointer fields preserve their stored + * values. This request cannot clear DisplayName. + */ +export interface UpdateChatModelRequest { + readonly ai_provider_id?: string; + readonly model?: string; + readonly display_name?: string; + readonly enabled?: boolean; + readonly is_default?: boolean; + readonly context_limit?: number; + readonly compression_threshold?: number; + readonly model_config?: ChatModelCallConfig; +} + +// From codersdk/chats.go +/** + * UpdateChatPersonalModelOverridesAdminSettingsRequest is the request body for + * updating personal model override admin settings. + */ +export interface UpdateChatPersonalModelOverridesAdminSettingsRequest { + readonly allow_users: boolean; +} + +// From codersdk/chats.go +/** + * UpdateChatPlanModeInstructionsRequest is the request body for + * updating the plan mode instructions configuration. + */ +export interface UpdateChatPlanModeInstructionsRequest { + readonly plan_mode_instructions: string; +} + +// From codersdk/chats.go +/** + * UpdateChatProviderConfigRequest updates a chat provider config. + */ +export interface UpdateChatProviderConfigRequest { + readonly display_name?: string; + readonly icon?: string; + readonly api_key?: string; + readonly base_url?: string; + readonly enabled?: boolean; + readonly central_api_key_enabled?: boolean; + readonly allow_user_api_key?: boolean; + readonly allow_central_api_key_fallback?: boolean; +} + +// From codersdk/chats.go +/** + * UpdateChatRequest is the request to update a chat. + */ +export interface UpdateChatRequest { + readonly title?: string; + readonly archived?: boolean; + readonly workspace_id?: string; + /** + * PinOrder controls the chat's pinned state and position. + * - nil: no change to pin state. + * - 0: unpin the chat. + * - >0 (chat is unpinned): pin the chat, appending it to + * the end of the pinned list. The specific value is + * ignored; the server assigns the next available position. + * - >0 (chat is already pinned): move the chat to the + * requested position, shifting neighbors as needed. The + * value is clamped to [1, pinned_count]. + */ + readonly pin_order?: number; + readonly labels?: Record; + /** + * PlanMode switches the chat's persistent plan mode. + * nil: no change, ptr to "plan": enable, ptr to "": clear. + */ + readonly plan_mode?: ChatPlanMode; +} + +// From codersdk/chats.go +/** + * UpdateChatRetentionDaysRequest is a request to update the chat + * retention period. + */ +export interface UpdateChatRetentionDaysRequest { + readonly retention_days: number; +} + +// From codersdk/chats.go +/** + * UpdateChatSystemPromptRequest is the request body for updating the chat + * system prompt configuration. + */ +export interface UpdateChatSystemPromptRequest { + readonly system_prompt: string; + readonly include_default_system_prompt?: boolean; +} + +// From codersdk/chats.go +/** + * UpdateChatWorkspaceTTLRequest is the request to update the chat + * workspace TTL setting. + */ +export interface UpdateChatWorkspaceTTLRequest { + /** + * WorkspaceTTLMillis is the workspace TTL in milliseconds. + * Zero means disabled; the template's own autostop setting applies. + */ + readonly workspace_ttl_ms: number; +} + +// From codersdk/updatecheck.go +/** + * UpdateCheckResponse contains information on the latest release of Coder. + */ +export interface UpdateCheckResponse { + /** + * Current indicates whether the server version is the same as the latest. + */ + readonly current: boolean; + /** + * Version is the semantic version for the latest release of Coder. + */ + readonly version: string; + /** + * URL to download the latest release of Coder. + */ + readonly url: string; +} + +// From healthsdk/healthsdk.go +export interface UpdateHealthSettings { + readonly dismissed_healthchecks: readonly HealthSection[]; +} + +// From codersdk/inboxnotification.go +export interface UpdateInboxNotificationReadStatusRequest { + readonly is_read: boolean; +} + +// From codersdk/inboxnotification.go +export interface UpdateInboxNotificationReadStatusResponse { + readonly notification: InboxNotification; + readonly unread_count: number; +} + +// From codersdk/mcp.go +/** + * UpdateMCPServerConfigACLRequest is a sparse update of an MCP server + * config ACL: only the listed principals change, and + * MCPServerConfigRoleDeleted removes an entry. + */ +export interface UpdateMCPServerConfigACLRequest { + readonly user_roles?: Record; + readonly group_roles?: Record; +} + +// From codersdk/mcp.go +/** + * UpdateMCPServerConfigRequest is the request to update an MCP server config. + */ +export interface UpdateMCPServerConfigRequest { + readonly display_name?: string; + readonly slug?: string; + readonly description?: string; + readonly icon_url?: string; + readonly transport?: string; + readonly url?: string; + readonly auth_type?: string; + readonly oauth2_client_id?: string; + readonly oauth2_client_secret?: string; + readonly oauth2_auth_url?: string; + readonly oauth2_token_url?: string; + /** + * OAuth2RevocationURL is validated in the handler because a + * validate tag would reject the pointer to "" that clears it. + */ + readonly oauth2_revocation_url?: string; + readonly oauth2_scopes?: string; + readonly api_key_header?: string; + readonly api_key_value?: string; + readonly custom_headers?: Record; + readonly tool_allow_list?: string[]; + readonly tool_deny_list?: string[]; + readonly availability?: string; + readonly enabled?: boolean; + readonly model_intent?: boolean; + readonly allow_in_plan_mode?: boolean; + /** + * ForwardCoderHeaders, when set, updates whether Coder identity + * headers are forwarded on every outgoing MCP request. + */ + readonly forward_coder_headers?: boolean; +} + +// From codersdk/notifications.go +export interface UpdateNotificationTemplateMethod { + readonly method?: string; +} + +// From codersdk/organizations.go +export interface UpdateOrganizationRequest { + readonly name?: string; + readonly display_name?: string; + readonly description?: string; + readonly icon?: string; + /** + * DefaultOrgMemberRoles, when non-nil, replaces the org's default + * member roles. + */ + readonly default_org_member_roles?: string[]; +} + +// From codersdk/users.go +export interface UpdateRoles { + readonly roles: readonly string[]; +} + +// From codersdk/aitasks.go +/** + * UpdateTaskInputRequest is used to update a task's input. + */ +export interface UpdateTaskInputRequest { + readonly input: string; +} + +// From codersdk/templates.go +export interface UpdateTemplateACL { + /** + * UserPerms is a mapping from valid user UUIDs to the template role they + * should be granted. To remove a user from the template, use "" as the role + * (available as a constant named codersdk.TemplateRoleDeleted) + */ + readonly user_perms?: Record; + /** + * GroupPerms is a mapping from valid group UUIDs to the template role they + * should be granted. To remove a group from the template, use "" as the role + * (available as a constant named codersdk.TemplateRoleDeleted) + */ + readonly group_perms?: Record; +} + +// From codersdk/templates.go +/** + * UpdateTemplateMeta is the request body for the PATCH /templates/{template} + * endpoint. All fields are optional. Fields that are nil are not modified. + */ +export interface UpdateTemplateMeta { + readonly name?: string; + readonly display_name?: string; + readonly description?: string; + readonly icon?: string; + readonly default_ttl_ms?: number; + /** + * ActivityBumpMillis allows optionally specifying the activity bump + * duration for all workspaces created from this template. Defaults to 1h + * but can be set to 0 to disable activity bumping. + */ + readonly activity_bump_ms?: number; + /** + * TimeTilAutostopNotifyMillis allows optionally specifying the duration + * before the autostop deadline at which a reminder notification is sent for + * workspaces created from this template. Defaults to 0 (disabled). Omitting + * the field keeps the existing value. + */ + readonly time_til_autostop_notify_ms?: number; + /** + * AutostopRequirement and AutostartRequirement can only be set if your license + * includes the advanced template scheduling feature. If you attempt to set this + * value while unlicensed, it will be ignored. + */ + readonly autostop_requirement?: TemplateAutostopRequirement; + readonly autostart_requirement?: TemplateAutostartRequirement; + readonly allow_user_autostart?: boolean; + readonly allow_user_autostop?: boolean; + readonly allow_user_cancel_workspace_jobs?: boolean; + readonly failure_ttl_ms?: number; + readonly time_til_dormant_ms?: number; + readonly time_til_dormant_autodelete_ms?: number; + /** + * UpdateWorkspaceLastUsedAt updates the last_used_at field of workspaces + * spawned from the template. This is useful for preventing workspaces being + * immediately locked when updating the inactivity_ttl field to a new, shorter + * value. + */ + readonly update_workspace_last_used_at?: boolean; + /** + * UpdateWorkspaceDormantAt updates the dormant_at field of workspaces spawned + * from the template. This is useful for preventing dormant workspaces being + * immediately deleted when updating the dormant_ttl field to a new, shorter + * value. + */ + readonly update_workspace_dormant_at?: boolean; + /** + * RequireActiveVersion mandates workspaces built using this template + * use the active version of the template. This option has no + * effect on template admins. + */ + readonly require_active_version?: boolean; + /** + * DeprecationMessage if set, will mark the template as deprecated and block + * any new workspaces from using this template. + * If passed an empty string, will remove the deprecated message, making + * the template usable for new workspaces again. + */ + readonly deprecation_message?: string; + /** + * DisableEveryoneGroupAccess allows optionally disabling the default + * behavior of granting the 'everyone' group access to use the template. + * If this is set to true, the template will not be available to all users, + * and must be explicitly granted to users or groups in the permissions settings + * of the template. + */ + readonly disable_everyone_group_access?: boolean; + readonly max_port_share_level?: WorkspaceAgentPortShareLevel; + readonly cors_behavior?: CORSBehavior; + /** + * UseClassicParameterFlow is a flag that switches the default behavior to use the classic + * parameter flow when creating a workspace. This only affects deployments with the experiment + * "dynamic-parameters" enabled. This setting will live for a period after the experiment is + * made the default. + * An "opt-out" is present in case the new feature breaks some existing templates. + */ + readonly use_classic_parameter_flow?: boolean; + /** + * DisableModuleCache disables the using of cached Terraform modules during + * provisioning. It is recommended not to disable this. + */ + readonly disable_module_cache?: boolean; + /** + * AgentsAllowed controls whether Coder Agents can create workspaces using + * this template. If omitted, the current value is preserved. + */ + readonly agents_allowed?: boolean; +} + +// From codersdk/users.go +export interface UpdateUserAppearanceSettingsRequest { + readonly theme_preference: string; + /** + * ThemeMode is optional for backward compatibility. When empty, + * the server leaves theme_mode, theme_light, and theme_dark + * unchanged so older CLI clients do not erase sync-mode settings. + * Legacy auto preferences are the exception: they clear theme_mode + * so clients can migrate the old sync-with-system setting. + */ + readonly theme_mode: ThemeMode; + /** + * ThemeLight is required when ThemeMode is "sync". In "single" + * mode an empty value means "preserve the previously persisted + * slot" rather than "clear the slot", so partial updates that send + * only one slot keep the other intact. + */ + readonly theme_light: string; + /** + * ThemeDark is required when ThemeMode is "sync". In "single" mode + * an empty value means "preserve the previously persisted slot" + * rather than "clear the slot", so partial updates that send only + * one slot keep the other intact. + */ + readonly theme_dark: string; + readonly terminal_font: TerminalFontName; +} + +// From codersdk/chats.go +/** + * UpdateUserChatCompactionThresholdRequest sets a user's per-model + * chat compaction threshold override. + */ +export interface UpdateUserChatCompactionThresholdRequest { + readonly threshold_percent: number; +} + +// From codersdk/chats.go +/** + * UpdateUserChatDebugLoggingRequest is the per-user request to + * opt into or out of chat debug logging. + */ +export interface UpdateUserChatDebugLoggingRequest { + readonly debug_logging_enabled: boolean; +} + +// From codersdk/chats.go +/** + * UpdateUserChatPersonalModelOverrideRequest is the request body for updating + * a user personal model override. + */ +export interface UpdateUserChatPersonalModelOverrideRequest { + readonly mode: ChatPersonalModelOverrideMode; + readonly model_config_id: string; + readonly reasoning_effort?: string; +} + +// From codersdk/notifications.go +export interface UpdateUserNotificationPreferences { + readonly template_disabled_map: Record; +} + +// From codersdk/users.go +export interface UpdateUserPasswordRequest { + readonly old_password: string; + readonly password: string; +} + +// From codersdk/users.go +export interface UpdateUserPreferenceSettingsRequest { + readonly task_notification_alert_dismissed?: boolean; + readonly thinking_display_mode?: ThinkingDisplayMode; + readonly shell_tool_display_mode?: AgentDisplayMode; + readonly code_diff_display_mode?: AgentDisplayMode; + readonly agent_chat_send_shortcut?: AgentChatSendShortcut; +} + +// From codersdk/users.go +export interface UpdateUserProfileRequest { + readonly username: string; + readonly name: string; + /** + * AvatarURL is only applied for users whose login type is password or + * none. For other login types the avatar is synced from the identity + * provider on login, so a submitted value is ignored. + */ + readonly avatar_url: string; +} + +// From codersdk/users.go +export interface UpdateUserQuietHoursScheduleRequest { + /** + * Schedule is a cron expression that defines when the user's quiet hours + * window is. Schedule must not be empty. For new users, the schedule is set + * to 2am in their browser or computer's timezone. The schedule denotes the + * beginning of a 4 hour window where the workspace is allowed to + * automatically stop or restart due to maintenance or template schedule. + * + * The schedule must be daily with a single time, and should have a timezone + * specified via a CRON_TZ prefix (otherwise UTC will be used). + * + * If the schedule is empty, the user will be updated to use the default + * schedule. + */ + readonly schedule: string; +} + +// From codersdk/usersecrets.go +/** + * UpdateUserSecretRequest is the payload for partially updating a + * user secret. At least one field must be non-nil. Pointer fields + * distinguish "not sent" (nil) from "set to empty string" (pointer + * to empty string). If the post-update row is enabled it must still + * have at least one of EnvName or FilePath non-empty; clearing both + * targets is only allowed when the secret is (or becomes) disabled. + */ +export interface UpdateUserSecretRequest { + readonly value?: string; + readonly description?: string; + readonly env_name?: string; + readonly file_path?: string; + readonly enabled?: boolean; +} + +// From codersdk/userskills.go +/** + * UpdateUserSkillRequest is the payload for updating a user skill. + */ +export interface UpdateUserSkillRequest { + /** + * Content must be SKILL.md-format Markdown with YAML frontmatter. The + * frontmatter must include name, may include description, and must be + * followed by a non-empty body. + */ + readonly content: string; +} + +// From codersdk/workspaces.go +export interface UpdateWorkspaceACL { + /** + * UserRoles is a mapping from valid user UUIDs to the workspace role they + * should be granted. To remove a user from the workspace, use "" as the role + * (available as a constant named codersdk.WorkspaceRoleDeleted) + */ + readonly user_roles?: Record; + /** + * GroupRoles is a mapping from valid group UUIDs to the workspace role they + * should be granted. To remove a group from the workspace, use "" as the role + * (available as a constant named codersdk.WorkspaceRoleDeleted) + */ + readonly group_roles?: Record; +} + +// From codersdk/workspaces.go +/** + * UpdateWorkspaceAutomaticUpdatesRequest is a request to updates a workspace's automatic updates setting. + */ +export interface UpdateWorkspaceAutomaticUpdatesRequest { + readonly automatic_updates: AutomaticUpdates; +} + +// From codersdk/workspaces.go +/** + * UpdateWorkspaceAutostartRequest is a request to update a workspace's autostart schedule. + */ +export interface UpdateWorkspaceAutostartRequest { + /** + * Schedule is expected to be of the form `CRON_TZ= * * ` + * Example: `CRON_TZ=US/Central 30 9 * * 1-5` represents 0930 in the timezone US/Central + * on weekdays (Mon-Fri). `CRON_TZ` defaults to UTC if not present. + */ + readonly schedule?: string; +} + +// From codersdk/workspacebuilds.go +/** + * UpdateWorkspaceBuildStateRequest is the request body for updating the + * provisioner state of a workspace build. + */ +export interface UpdateWorkspaceBuildStateRequest { + readonly state: string; +} + +// From codersdk/workspaces.go +/** + * UpdateWorkspaceDormancy is a request to activate or make a workspace dormant. + * A value of false will activate a dormant workspace. + */ +export interface UpdateWorkspaceDormancy { + readonly dormant: boolean; +} + +// From codersdk/workspaceproxy.go +export interface UpdateWorkspaceProxyResponse { + readonly proxy: WorkspaceProxy; + readonly proxy_token: string; +} + +// From codersdk/workspaces.go +export interface UpdateWorkspaceRequest { + readonly name?: string; +} + +// From codersdk/workspacesharing.go +/** + * UpdateWorkspaceSharingSettingsRequest represents workspace sharing settings + * that can be updated for an organization. + */ +export interface UpdateWorkspaceSharingSettingsRequest { + /** + * SharingDisabled is deprecated and left for backward compatibility + * purposes. + * @deprecated use `ShareableWorkspaceOwners` instead + */ + readonly sharing_disabled?: boolean; + /** + * ShareableWorkspaceOwners controls whose workspaces can be shared + * within the organization. + */ + readonly shareable_workspace_owners?: ShareableWorkspaceOwners; +} + +// From codersdk/workspaces.go +/** + * UpdateWorkspaceTTLRequest is a request to update a workspace's TTL. + */ +export interface UpdateWorkspaceTTLRequest { + readonly ttl_ms: number | null; +} + +// From codersdk/chats.go +/** + * UploadChatFileResponse is the response from uploading a chat file. + */ +export interface UploadChatFileResponse { + readonly id: string; +} + +// From codersdk/files.go +/** + * UploadResponse contains the hash to reference the uploaded file. + */ +export interface UploadResponse { + readonly hash: string; +} + +// From codersdk/aimodelprices.go +/** + * UpsertAIModelPricesRequest sets prices for the listed models. Models absent + * from the request are left untouched. + */ +export interface UpsertAIModelPricesRequest { + readonly prices: readonly AIModelPriceUpsert[]; +} + +// From codersdk/aibridge.go +export interface UpsertGroupAIBudgetRequest { + /** + * SpendLimitMicros must not exceed MaxAISpendLimitMicros. + */ + readonly spend_limit_micros: number; +} + +// From codersdk/aibridge.go +export interface UpsertUserAIBudgetOverrideRequest { + /** + * GroupID is the group the user's spend is attributed to. The user must + * be a member of this group. + */ + readonly group_id: string; + /** + * SpendLimitMicros must not exceed MaxAISpendLimitMicros. + */ + readonly spend_limit_micros: number; +} + +// From codersdk/workspaceagentportshare.go +export interface UpsertWorkspaceAgentPortShareRequest { + readonly agent_name: string; + readonly port: number; + readonly share_level: WorkspaceAgentPortShareLevel; + readonly protocol: WorkspaceAgentPortShareProtocol; +} + +// From codersdk/workspaces.go +export type UsageAppName = "jetbrains" | "reconnecting-pty" | "ssh" | "vscode"; + +export const UsageAppNames: UsageAppName[] = [ + "jetbrains", + "reconnecting-pty", + "ssh", + "vscode", +]; + +// From codersdk/deployment.go +export interface UsagePeriod { + readonly issued_at: string; + readonly start: string; + readonly end: string; +} + +// From codersdk/deployment.go +export interface UsageStatsConfig { + readonly enable: boolean; +} + +// From codersdk/users.go +/** + * User represents a user in Coder. + */ +export interface User extends ReducedUser { + readonly organization_ids: readonly string[]; + readonly roles: readonly SlimRole[]; + /** + * HasAISeat intentionally omits omitempty so the API always includes the + * field, even when false. + */ + readonly has_ai_seat: boolean; +} + +// From codersdk/aibridge.go +export interface UserAIBudgetOverride { + readonly user_id: string; + readonly group_id: string; + readonly spend_limit_micros: number; + readonly created_at: string; + readonly updated_at: string; +} + +// From codersdk/aibridge.go +/** + * UserAIBudgetSummary is the effective AI budget for a user. When no budget + * applies, the effective group falls back to the Everyone group with a null + * budget. + */ +export interface UserAIBudgetSummary { + readonly user_id: string; + /** + * EffectiveGroupID is the group the spend is attributed to, falling back to + * the Everyone group when no budget applies. Null only when the user has no + * organization membership. + */ + readonly effective_group_id: string | null; + /** + * EffectiveBudget is the spend limit that applies to the user, whether it + * came from a group budget or a user override. Null when no budget + * applies, leaving the user's spend unlimited. + */ + readonly effective_budget: AIBudgetLimit | null; +} + +// From codersdk/chats.go +/** + * UserAIProviderKeyConfig is a provider summary from the current user's + * perspective. It reports key presence but never returns key material. + */ +export interface UserAIProviderKeyConfig { + readonly provider: AIProviderSummary; + readonly has_user_api_key: boolean; + readonly has_provider_api_key: boolean; + readonly byok_enabled: boolean; +} + +// From codersdk/aibridge.go +/** + * UserAISpendStatus is the current AI spend snapshot for a user within + * the active budget period. + */ +export interface UserAISpendStatus + extends UserAIBudgetSummary, + AISpendPeriodWindow { + /** + * CurrentSpendMicros is the user's spend on their effective group over + * the current budget period. + */ + readonly current_spend_micros: number; +} + +// From codersdk/insights.go +/** + * UserActivity shows the session time for a user. + */ +export interface UserActivity { + readonly template_ids: readonly string[]; + readonly user_id: string; + readonly username: string; + readonly avatar_url: string; + readonly seconds: number; +} + +// From codersdk/insights.go +/** + * UserActivityInsightsReport is the report from the user activity insights + * endpoint. + */ +export interface UserActivityInsightsReport { + readonly start_time: string; + readonly end_time: string; + readonly template_ids: readonly string[]; + readonly users: readonly UserActivity[]; +} + +// From codersdk/insights.go +export interface UserActivityInsightsRequest { + readonly start_time: string; + readonly end_time: string; + readonly template_ids: readonly string[]; +} + +// From codersdk/insights.go +/** + * UserActivityInsightsResponse is the response from the user activity insights + * endpoint. + */ +export interface UserActivityInsightsResponse { + readonly report: UserActivityInsightsReport; +} + +// From codersdk/users.go +export interface UserAppearanceSettings { + /** + * ThemePreference is the legacy single-field appearance setting. In + * "single" mode it mirrors the active theme. In "sync" mode modern + * clients normally mirror the active OS slot, but older clients can + * update only this field, so it may diverge from ThemeLight or + * ThemeDark until a modern client saves the full appearance state + * again. + */ + readonly theme_preference: string; + readonly theme_mode: ThemeMode; + /** + * Ignored when ThemeMode is "single" + */ + readonly theme_light: string; + /** + * Ignored when ThemeMode is "single" + */ + readonly theme_dark: string; + readonly terminal_font: TerminalFontName; +} + +// From codersdk/chats.go +/** + * UserChatCompactionThreshold is a user's per-model chat compaction + * threshold override. + */ +export interface UserChatCompactionThreshold { + readonly model_config_id: string; + readonly threshold_percent: number; +} + +// From codersdk/chats.go +/** + * UserChatCompactionThresholds wraps the user's per-model chat + * compaction threshold overrides. + */ +export interface UserChatCompactionThresholds { + readonly thresholds: readonly UserChatCompactionThreshold[]; +} + +// From codersdk/chats.go +/** + * UserChatCustomPrompt is the request and response body for the + * user chat custom prompt configuration endpoint. + */ +export interface UserChatCustomPrompt { + readonly custom_prompt: string; +} + +// From codersdk/chats.go +/** + * UserChatDebugLoggingSettings describes whether debug logging is + * active for the current user and whether the user may control it. + */ +export interface UserChatDebugLoggingSettings { + readonly debug_logging_enabled: boolean; + readonly user_toggle_allowed: boolean; + readonly forced_by_deployment: boolean; +} + +// From codersdk/chats.go +/** + * UserChatPersonalModelOverridesResponse is the response body for user + * personal model override settings. + */ +export interface UserChatPersonalModelOverridesResponse { + readonly enabled: boolean; + readonly root: ChatPersonalModelOverride; + readonly general: ChatPersonalModelOverride; + readonly explore: ChatPersonalModelOverride; + readonly deployment_defaults: ChatPersonalModelOverrideDeploymentDefaults; +} + +// From codersdk/chats.go +/** + * UserChatProviderConfig is a summary of a provider that allows + * user-supplied keys, as seen from the current user's perspective. + */ +export interface UserChatProviderConfig { + readonly provider_id: string; + readonly provider: string; + readonly display_name: string; + readonly icon: string; + readonly enabled: boolean; + readonly has_user_api_key: boolean; + readonly has_central_api_key_fallback: boolean; + readonly byok_enabled: boolean; +} + +// From codersdk/insights.go +/** + * UserLatency shows the connection latency for a user. + */ +export interface UserLatency { + readonly template_ids: readonly string[]; + readonly user_id: string; + readonly username: string; + readonly avatar_url: string; + readonly latency_ms: ConnectionLatency; +} + +// From codersdk/insights.go +/** + * UserLatencyInsightsReport is the report from the user latency insights + * endpoint. + */ +export interface UserLatencyInsightsReport { + readonly start_time: string; + readonly end_time: string; + readonly template_ids: readonly string[]; + readonly users: readonly UserLatency[]; +} + +// From codersdk/insights.go +export interface UserLatencyInsightsRequest { + readonly start_time: string; + readonly end_time: string; + readonly template_ids: readonly string[]; +} + +// From codersdk/insights.go +/** + * UserLatencyInsightsResponse is the response from the user latency insights + * endpoint. + */ +export interface UserLatencyInsightsResponse { + readonly report: UserLatencyInsightsReport; +} + +// From codersdk/users.go +export interface UserLoginType { + readonly login_type: LoginType; +} + +// From codersdk/users.go +export interface UserParameter { + readonly name: string; + readonly value: string; +} + +// From codersdk/users.go +export interface UserPreferenceSettings { + readonly task_notification_alert_dismissed: boolean; + readonly thinking_display_mode: ThinkingDisplayMode; + readonly shell_tool_display_mode: AgentDisplayMode; + readonly code_diff_display_mode: AgentDisplayMode; + readonly agent_chat_send_shortcut: AgentChatSendShortcut; +} + +// From codersdk/deployment.go +export interface UserQuietHoursScheduleConfig { + readonly default_schedule: string; + readonly allow_user_custom: boolean; +} + +// From codersdk/users.go +export interface UserQuietHoursScheduleResponse { + readonly raw_schedule: string; + /** + * UserSet is true if the user has set their own quiet hours schedule. If + * false, the user is using the default schedule. + */ + readonly user_set: boolean; + /** + * UserCanSet is true if the user is allowed to set their own quiet hours + * schedule. If false, the user cannot set a custom schedule and the default + * schedule will always be used. + */ + readonly user_can_set: boolean; + /** + * Time is the time of day that the quiet hours window starts in the given + * Timezone each day. + */ + readonly time: string; // HH:mm (24-hour) + readonly timezone: string; // raw format from the cron expression, UTC if unspecified + /** + * Next is the next time that the quiet hours window will start. + */ + readonly next: string; +} + +// From codersdk/users.go +export interface UserRoles { + readonly roles: readonly string[]; + readonly organization_roles: Record; +} + +// From codersdk/usersecrets.go +/** + * UserSecret represents a user secret's metadata. The secret value + * is never included in API responses. + */ +export interface UserSecret { + readonly id: string; + readonly name: string; + readonly description: string; + readonly env_name: string; + readonly file_path: string; + /** + * Enabled controls whether the secret is injected into workspaces. + * Disabled secrets remain visible and editable, but are not added + * to the agent manifest, so they are not exposed as environment + * variables or written to secret files. + */ + readonly enabled: boolean; + readonly created_at: string; + readonly updated_at: string; +} + +// From codersdk/usersecretvalidation.go +/** + * UserSecret*Field constants are the canonical ValidationError.Field values + * for user secret fields. UserSecretNameField is also the chi URL parameter + * name used in coderd route segments. + */ +export const UserSecretEnvNameField = "env_name"; + +// From codersdk/usersecretvalidation.go +/** + * UserSecret*Field constants are the canonical ValidationError.Field values + * for user secret fields. UserSecretNameField is also the chi URL parameter + * name used in coderd route segments. + */ +export const UserSecretFilePathField = "file_path"; + +// From codersdk/usersecretvalidation.go +/** + * UserSecretInjectionTargetRequiredDetail explains the injection-target + * invariant. It is shared by the create validator above and the PATCH + * handler's post-state check in coderd. The value is a user-facing + * validation message, not a credential. + */ +export const UserSecretInjectionTargetRequiredDetail = + "An enabled secret must have at least one of env_name or file_path set. To keep a secret without injecting it, set enabled to false instead of clearing both targets."; //nolint:gosec // G101: message text, not a hardcoded credential. + +// From codersdk/usersecretvalidation.go +/** + * UserSecret*Field constants are the canonical ValidationError.Field values + * for user secret fields. UserSecretNameField is also the chi URL parameter + * name used in coderd route segments. + */ +export const UserSecretNameField = "name"; + +// From codersdk/usersecretvalidation.go +/** + * UserSecret*Field constants are the canonical ValidationError.Field values + * for user secret fields. UserSecretNameField is also the chi URL parameter + * name used in coderd route segments. + */ +export const UserSecretValueField = "value"; + +// From codersdk/userskills.go +/** + * UserSkill represents a user skill with its raw Markdown content. + */ +export interface UserSkill extends UserSkillMetadata { + readonly content: string; +} + +// From codersdk/userskills.go +/** + * UserSkillMetadata represents a user skill without its raw Markdown content. + */ +export interface UserSkillMetadata { + readonly id: string; + readonly name: string; + readonly description: string; + readonly created_at: string; + readonly updated_at: string; +} + +// From codersdk/users.go +export type UserStatus = "active" | "dormant" | "suspended"; + +// From codersdk/insights.go +export interface UserStatusChangeCount { + readonly date: string; + readonly count: number; +} + +export const UserStatuses: UserStatus[] = ["active", "dormant", "suspended"]; + +// From codersdk/users.go +export interface UsersRequest extends Pagination { + readonly q?: string; +} + +// From codersdk/users.go +export interface ValidateUserPasswordRequest { + readonly password: string; +} + +// From codersdk/users.go +export interface ValidateUserPasswordResponse { + readonly valid: boolean; + readonly details: string; +} + +// From codersdk/client.go +/** + * ValidationError represents a scoped error to a user input. + */ +export interface ValidationError { + readonly field: string; + readonly detail: string; +} + +// From codersdk/templateversions.go +export type ValidationMonotonicOrder = "decreasing" | "increasing"; + +export const ValidationMonotonicOrders: ValidationMonotonicOrder[] = [ + "decreasing", + "increasing", +]; + +// From codersdk/organizations.go +export interface VariableValue { + readonly name: string; + readonly value: string; +} + +// From codersdk/notifications.go +export interface WebpushMessage { + readonly icon: string; + readonly title: string; + readonly body: string; + readonly tag?: string; + readonly actions: readonly WebpushMessageAction[]; + readonly data?: Record; +} + +// From codersdk/notifications.go +export interface WebpushMessageAction { + readonly label: string; + readonly url: string; +} + +// From codersdk/notifications.go +export interface WebpushSubscription { + readonly endpoint: string; + readonly auth_key: string; + readonly p256dh_key: string; +} + +// From healthsdk/healthsdk.go +/** + * WebsocketReport shows if the configured access URL allows establishing WebSocket connections. + */ +export interface WebsocketReport extends BaseReport { + /** + * Healthy is deprecated and left for backward compatibility purposes, use `Severity` instead. + */ + readonly healthy: boolean; + readonly body: string; + readonly code: number; +} + +// From codersdk/workspaces.go +/** + * Workspace is a deployment of a template. It references a specific + * version and can be updated. + */ +export interface Workspace { + readonly id: string; + readonly created_at: string; + readonly updated_at: string; + readonly owner_id: string; + /** + * OwnerName is the username of the owner of the workspace. + */ + readonly owner_name: string; + readonly owner_avatar_url: string; + readonly organization_id: string; + readonly organization_name: string; + readonly template_id: string; + readonly template_name: string; + readonly template_display_name: string; + readonly template_icon: string; + readonly template_allow_user_cancel_workspace_jobs: boolean; + readonly template_active_version_id: string; + readonly template_require_active_version: boolean; + readonly template_use_classic_parameter_flow: boolean; + readonly latest_build: WorkspaceBuild; + readonly latest_app_status: WorkspaceAppStatus | null; + readonly outdated: boolean; + readonly name: string; + readonly autostart_schedule?: string; + readonly ttl_ms?: number; + readonly last_used_at: string; + /** + * DeletingAt indicates the time at which the workspace will be permanently deleted. + * A workspace is eligible for deletion if it is dormant (a non-nil dormant_at value) + * and a value has been specified for time_til_dormant_autodelete on its template. + */ + readonly deleting_at: string | null; + /** + * DormantAt being non-nil indicates a workspace that is dormant. + * A dormant workspace is no longer accessible must be activated. + * It is subject to deletion if it breaches + * the duration of the time_til_ field on its template. + */ + readonly dormant_at: string | null; + /** + * Health shows the health of the workspace and information about + * what is causing an unhealthy status. + */ + readonly health: WorkspaceHealth; + readonly automatic_updates: AutomaticUpdates; + readonly allow_renames: boolean; + readonly favorite: boolean; + readonly next_start_at: string | null; + /** + * IsPrebuild indicates whether the workspace is a prebuilt workspace. + * Prebuilt workspaces are owned by the prebuilds system user and have specific behavior, + * such as being managed differently from regular workspaces. + * Once a prebuilt workspace is claimed by a user, it transitions to a regular workspace, + * and IsPrebuild returns false. + */ + readonly is_prebuild: boolean; + /** + * TaskID, if set, indicates that the workspace is relevant to the given codersdk.Task. + */ + readonly task_id?: string; + readonly shared_with?: readonly SharedWorkspaceActor[]; +} + +// From codersdk/workspaces.go +export interface WorkspaceACL { + readonly users: readonly WorkspaceUser[]; + readonly group: readonly WorkspaceGroup[]; +} + +// From codersdk/workspaceagents.go +export interface WorkspaceAgent { + readonly id: string; + readonly parent_id: string | null; + readonly created_at: string; + readonly updated_at: string; + readonly first_connected_at?: string; + readonly last_connected_at?: string; + readonly disconnected_at?: string; + readonly started_at?: string; + readonly ready_at?: string; + readonly status: WorkspaceAgentStatus; + readonly lifecycle_state: WorkspaceAgentLifecycle; + readonly name: string; + readonly resource_id: string; + readonly instance_id?: string; + readonly architecture: string; + readonly environment_variables: Record; + readonly operating_system: string; + readonly logs_length: number; + readonly logs_overflowed: boolean; + readonly directory?: string; + readonly expanded_directory?: string; + readonly version: string; + readonly api_version: string; + readonly apps: readonly WorkspaceApp[]; + /** + * DERPLatency is mapped by region name (e.g. "New York City", "Seattle"). + */ + readonly latency?: Record; + readonly connection_timeout_seconds: number; + readonly troubleshooting_url: string; + readonly subsystems: readonly AgentSubsystem[]; + readonly health: WorkspaceAgentHealth; // Health reports the health of the agent. + readonly display_apps: readonly DisplayApp[]; + readonly log_sources: readonly WorkspaceAgentLogSource[]; + readonly scripts: readonly WorkspaceAgentScript[]; + /** + * Metadata is only populated on the workspaces list endpoint when the + * request opts in with the include_agent_metadata search key, and it + * only carries the requested keys. The description's script is always + * empty here: it can be long, and list consumers want values. + */ + readonly metadata?: readonly WorkspaceAgentMetadata[]; + /** + * StartupScriptBehavior is a legacy field that is deprecated in favor + * of the `coder_script` resource. It's only referenced by old clients. + * @deprecated Remove in the future! + */ + readonly startup_script_behavior: WorkspaceAgentStartupScriptBehavior; +} + +// From codersdk/workspaceagents.go +/** + * WorkspaceAgentContainer describes a devcontainer of some sort + * that is visible to the workspace agent. This struct is an abstraction + * of potentially multiple implementations, and the fields will be + * somewhat implementation-dependent. + */ +export interface WorkspaceAgentContainer { + /** + * CreatedAt is the time the container was created. + */ + readonly created_at: string; + /** + * ID is the unique identifier of the container. + */ + readonly id: string; + /** + * FriendlyName is the human-readable name of the container. + */ + readonly name: string; + /** + * Image is the name of the container image. + */ + readonly image: string; + /** + * Labels is a map of key-value pairs of container labels. + */ + readonly labels: Record; + /** + * Running is true if the container is currently running. + */ + readonly running: boolean; + /** + * Ports includes ports exposed by the container. + */ + readonly ports: readonly WorkspaceAgentContainerPort[]; + /** + * Status is the current status of the container. This is somewhat + * implementation-dependent, but should generally be a human-readable + * string. + */ + readonly status: string; + /** + * Volumes is a map of "things" mounted into the container. Again, this + * is somewhat implementation-dependent. + */ + readonly volumes: Record; +} + +// From codersdk/workspaceagents.go +/** + * WorkspaceAgentContainerPort describes a port as exposed by a container. + */ +export interface WorkspaceAgentContainerPort { + /** + * Port is the port number *inside* the container. + */ + readonly port: number; + /** + * Network is the network protocol used by the port (tcp, udp, etc). + */ + readonly network: string; + /** + * HostIP is the IP address of the host interface to which the port is + * bound. Note that this can be an IPv4 or IPv6 address. + */ + readonly host_ip?: string; + /** + * HostPort is the port number *outside* the container. + */ + readonly host_port?: number; +} + +// From codersdk/workspaceagents.go +/** + * WorkspaceAgentDevcontainer defines the location of a devcontainer + * configuration in a workspace that is visible to the workspace agent. + */ +export interface WorkspaceAgentDevcontainer { + readonly id: string; + readonly name: string; + readonly workspace_folder: string; + readonly config_path?: string; + readonly subagent_id?: string; + /** + * Additional runtime fields. + */ + readonly status: WorkspaceAgentDevcontainerStatus; + readonly dirty: boolean; + readonly container?: WorkspaceAgentContainer; + readonly agent?: WorkspaceAgentDevcontainerAgent; + readonly error?: string; +} + +// From codersdk/workspaceagents.go +/** + * WorkspaceAgentDevcontainerAgent represents the sub agent for a + * devcontainer. + */ +export interface WorkspaceAgentDevcontainerAgent { + readonly id: string; + readonly name: string; + readonly directory: string; +} + +// From codersdk/workspaceagents.go +export type WorkspaceAgentDevcontainerStatus = + | "deleting" + | "error" + | "running" + | "starting" + | "stopped" + | "stopping"; + +export const WorkspaceAgentDevcontainerStatuses: WorkspaceAgentDevcontainerStatus[] = + ["deleting", "error", "running", "starting", "stopped", "stopping"]; + +// From codersdk/workspaceagents.go +/** + * WorkspaceAgentGitClientMessage is a message sent from the client to + * the agent over the git watch WebSocket. + */ +export interface WorkspaceAgentGitClientMessage { + readonly type: WorkspaceAgentGitClientMessageType; +} + +// From codersdk/workspaceagents.go +export type WorkspaceAgentGitClientMessageType = "refresh"; + +export const WorkspaceAgentGitClientMessageTypes: WorkspaceAgentGitClientMessageType[] = + ["refresh"]; + +// From codersdk/workspaceagents.go +/** + * WorkspaceAgentGitServerMessage is a message sent from the agent to + * the client over the git watch WebSocket. + */ +export interface WorkspaceAgentGitServerMessage { + readonly type: WorkspaceAgentGitServerMessageType; + readonly scanned_at?: string; + readonly repositories?: readonly WorkspaceAgentRepoChanges[]; + readonly message?: string; +} + +// From codersdk/workspaceagents.go +export type WorkspaceAgentGitServerMessageType = "changes" | "error"; + +export const WorkspaceAgentGitServerMessageTypes: WorkspaceAgentGitServerMessageType[] = + ["changes", "error"]; + +// From codersdk/workspaceagents.go +export interface WorkspaceAgentHealth { + readonly healthy: boolean; // Healthy is true if the agent is healthy. + readonly reason?: string; // Reason is a human-readable explanation of the agent's health. It is empty if Healthy is true. +} + +// From codersdk/workspaceagents.go +export type WorkspaceAgentLifecycle = + | "created" + | "off" + | "ready" + | "shutdown_error" + | "shutdown_timeout" + | "shutting_down" + | "start_error" + | "start_timeout" + | "starting"; + +export const WorkspaceAgentLifecycles: WorkspaceAgentLifecycle[] = [ + "created", + "off", + "ready", + "shutdown_error", + "shutdown_timeout", + "shutting_down", + "start_error", + "start_timeout", + "starting", +]; + +// From codersdk/workspaceagents.go +/** + * WorkspaceAgentListContainersResponse is the response to the list containers + * request. + */ +export interface WorkspaceAgentListContainersResponse { + /** + * Devcontainers is a list of devcontainers visible to the workspace agent. + */ + readonly devcontainers: readonly WorkspaceAgentDevcontainer[]; + /** + * Containers is a list of containers visible to the workspace agent. + */ + readonly containers: readonly WorkspaceAgentContainer[]; + /** + * Warnings is a list of warnings that may have occurred during the + * process of listing containers. This should not include fatal errors. + */ + readonly warnings?: readonly string[]; +} + +// From codersdk/workspaceagents.go +export interface WorkspaceAgentListeningPort { + readonly process_name: string; // may be empty + readonly network: string; // only "tcp" at the moment + readonly port: number; +} + +// From codersdk/workspaceagents.go +export interface WorkspaceAgentListeningPortsResponse { + /** + * If there are no ports in the list, nothing should be displayed in the UI. + * There must not be a "no ports available" message or anything similar, as + * there will always be no ports displayed on platforms where our port + * detection logic is unsupported. + */ + readonly ports: readonly WorkspaceAgentListeningPort[]; +} + +// From codersdk/workspaceagents.go +export interface WorkspaceAgentLog { + readonly id: number; + readonly created_at: string; + readonly output: string; + readonly level: LogLevel; + readonly source_id: string; +} + +// From codersdk/workspaceagents.go +export interface WorkspaceAgentLogSource { + readonly workspace_agent_id: string; + readonly id: string; + readonly created_at: string; + readonly display_name: string; + readonly icon: string; +} + +// From codersdk/workspaceagents.go +export interface WorkspaceAgentMetadata { + readonly result: WorkspaceAgentMetadataResult; + readonly description: WorkspaceAgentMetadataDescription; +} + +// From codersdk/workspaceagents.go +/** + * WorkspaceAgentMetadataDescription is a description of dynamic metadata the agent should report + * back to coderd. It is provided via the `metadata` list in the `coder_agent` + * block. + */ +export interface WorkspaceAgentMetadataDescription { + readonly display_name: string; + readonly key: string; + readonly script: string; + readonly interval: number; + readonly timeout: number; +} + +// From codersdk/workspaceagents.go +export interface WorkspaceAgentMetadataResult { + readonly collected_at: string; + /** + * Age is the number of seconds since the metadata was collected. + * It is provided in addition to CollectedAt to protect against clock skew. + */ + readonly age: number; + readonly value: string; + readonly error: string; +} + +// From codersdk/workspaceagentportshare.go +export interface WorkspaceAgentPortShare { + readonly workspace_id: string; + readonly agent_name: string; + readonly port: number; + readonly share_level: WorkspaceAgentPortShareLevel; + readonly protocol: WorkspaceAgentPortShareProtocol; +} + +// From codersdk/workspaceagentportshare.go +export type WorkspaceAgentPortShareLevel = + | "authenticated" + | "organization" + | "owner" + | "public"; + +export const WorkspaceAgentPortShareLevels: WorkspaceAgentPortShareLevel[] = [ + "authenticated", + "organization", + "owner", + "public", +]; + +// From codersdk/workspaceagentportshare.go +export type WorkspaceAgentPortShareProtocol = "http" | "https"; + +export const WorkspaceAgentPortShareProtocols: WorkspaceAgentPortShareProtocol[] = + ["http", "https"]; + +// From codersdk/workspaceagentportshare.go +export interface WorkspaceAgentPortShares { + readonly shares: readonly WorkspaceAgentPortShare[]; +} + +// From codersdk/workspaceagents.go +/** + * WorkspaceAgentRepoChanges describes the current state of a single + * git repository's working tree. When Removed is true the repo root + * directory or its .git subdirectory no longer exists; all other + * fields (Branch, RemoteOrigin, UnifiedDiff) are empty/zero. + */ +export interface WorkspaceAgentRepoChanges { + readonly repo_root: string; + readonly branch: string; + readonly remote_origin?: string; + readonly unified_diff?: string; + readonly removed?: boolean; +} + +// From codersdk/workspaceagents.go +export interface WorkspaceAgentScript { + readonly id: string; + readonly log_source_id: string; + readonly log_path: string; + readonly script: string; + readonly cron: string; + readonly run_on_start: boolean; + readonly run_on_stop: boolean; + readonly start_blocks_login: boolean; + readonly timeout: number; + readonly display_name: string; + readonly exit_code?: number; + readonly status?: WorkspaceAgentScriptStatus; +} + +// From codersdk/workspaceagents.go +export type WorkspaceAgentScriptStatus = + | "exit_failure" + | "ok" + | "pipes_left_open" + | "timed_out"; + +export const WorkspaceAgentScriptStatuses: WorkspaceAgentScriptStatus[] = [ + "exit_failure", + "ok", + "pipes_left_open", + "timed_out", +]; + +// From codersdk/workspaceagents.go +export type WorkspaceAgentStartupScriptBehavior = "blocking" | "non-blocking"; + +export const WorkspaceAgentStartupScriptBehaviors: WorkspaceAgentStartupScriptBehavior[] = + ["blocking", "non-blocking"]; + +// From codersdk/workspaceagents.go +export type WorkspaceAgentStatus = + | "connected" + | "connecting" + | "disconnected" + | "timeout"; + +export const WorkspaceAgentStatuses: WorkspaceAgentStatus[] = [ + "connected", + "connecting", + "disconnected", + "timeout", +]; + +// From codersdk/workspaceapps.go +export interface WorkspaceApp { + readonly id: string; + /** + * URL is the address being proxied to inside the workspace. + * If external is specified, this will be opened on the client. + */ + readonly url?: string; + /** + * External specifies whether the URL should be opened externally on + * the client or not. + */ + readonly external: boolean; + /** + * Slug is a unique identifier within the agent. + */ + readonly slug: string; + /** + * DisplayName is a friendly name for the app. + */ + readonly display_name?: string; + readonly command?: string; + /** + * Icon is a relative path or external URL that specifies + * an icon to be displayed in the dashboard. + */ + readonly icon?: string; + /** + * Subdomain denotes whether the app should be accessed via a path on the + * `coder server` or via a hostname-based dev URL. If this is set to true + * and there is no app wildcard configured on the server, the app will not + * be accessible in the UI. + */ + readonly subdomain: boolean; + /** + * SubdomainName is the application domain exposed on the `coder server`. + */ + readonly subdomain_name?: string; + readonly sharing_level: WorkspaceAppSharingLevel; + /** + * Healthcheck specifies the configuration for checking app health. + */ + readonly healthcheck?: Healthcheck; + readonly health: WorkspaceAppHealth; + readonly group?: string; + readonly hidden: boolean; + readonly open_in: WorkspaceAppOpenIn; + /** + * Tooltip is an optional markdown supported field that is displayed + * when hovering over workspace apps in the UI. + */ + readonly tooltip?: string; + /** + * Statuses is a list of statuses for the app. + */ + readonly statuses: readonly WorkspaceAppStatus[]; +} + +// From codersdk/workspaceapps.go +export type WorkspaceAppHealth = + | "disabled" + | "healthy" + | "initializing" + | "unhealthy"; + +export const WorkspaceAppHealths: WorkspaceAppHealth[] = [ + "disabled", + "healthy", + "initializing", + "unhealthy", +]; + +// From codersdk/workspaceapps.go +export type WorkspaceAppOpenIn = "slim-window" | "tab"; + +export const WorkspaceAppOpenIns: WorkspaceAppOpenIn[] = ["slim-window", "tab"]; + +// From codersdk/workspaceapps.go +export type WorkspaceAppSharingLevel = + | "authenticated" + | "organization" + | "owner" + | "public"; + +export const WorkspaceAppSharingLevels: WorkspaceAppSharingLevel[] = [ + "authenticated", + "organization", + "owner", + "public", +]; + +// From codersdk/workspaceapps.go +export interface WorkspaceAppStatus { + readonly id: string; + readonly created_at: string; + readonly workspace_id: string; + readonly agent_id: string; + readonly app_id: string; + readonly state: WorkspaceAppStatusState; + readonly message: string; + /** + * URI is the URI of the resource that the status is for. + * e.g. https://github.com/org/repo/pull/123 + * e.g. file:///path/to/file + */ + readonly uri: string; + /** + * @deprecated This field is unused and will be removed in a future version. + * Icon is an external URL to an icon that will be rendered in the UI. + */ + readonly icon: string; + /** + * @deprecated This field is unused and will be removed in a future version. + * NeedsUserAttention specifies whether the status needs user attention. + */ + readonly needs_user_attention: boolean; +} + +// From codersdk/workspaceapps.go +export type WorkspaceAppStatusState = + | "complete" + | "failure" + | "idle" + | "working"; + +export const WorkspaceAppStatusStates: WorkspaceAppStatusState[] = [ + "complete", + "failure", + "idle", + "working", +]; + +// From codersdk/workspacebuilds.go +/** + * WorkspaceBuild is an at-point representation of a workspace state. + * BuildNumbers start at 1 and increase by 1 for each subsequent build + */ +export interface WorkspaceBuild { + readonly id: string; + readonly created_at: string; + readonly updated_at: string; + readonly workspace_id: string; + readonly workspace_name: string; + readonly workspace_owner_id: string; + /** + * WorkspaceOwnerName is the username of the owner of the workspace. + */ + readonly workspace_owner_name: string; + readonly workspace_owner_avatar_url?: string; + readonly template_version_id: string; + readonly template_version_name: string; + readonly build_number: number; + readonly transition: WorkspaceTransition; + readonly initiator_id: string; + readonly initiator_name: string; + readonly job: ProvisionerJob; + readonly reason: BuildReason; + readonly resources: readonly WorkspaceResource[]; + readonly deadline?: string; + readonly max_deadline?: string; + readonly status: WorkspaceStatus; + readonly daily_cost: number; + readonly matched_provisioners?: MatchedProvisioners; + readonly template_version_preset_id: string | null; + /** + * @deprecated This field has been deprecated in favor of Task WorkspaceID. + */ + readonly has_ai_task?: boolean; + readonly has_external_agent?: boolean; +} + +// From codersdk/workspacebuilds.go +/** + * WorkspaceBuildParameter represents a parameter specific for a workspace build. + */ +export interface WorkspaceBuildParameter { + readonly name: string; + readonly value: string; +} + +// From codersdk/workspacebuilds.go +export interface WorkspaceBuildTimings { + readonly provisioner_timings: readonly ProvisionerTiming[]; + /** + * TODO: Consolidate agent-related timing metrics into a single struct when + * updating the API version + */ + readonly agent_script_timings: readonly AgentScriptTiming[]; + readonly agent_connection_timings: readonly AgentConnectionTiming[]; +} + +// From codersdk/workspaces.go +/** + * WorkspaceBuildUpdate contains information about a workspace build state change. + * This is published via the /watch-all-workspacebuilds SSE endpoint when the + * workspace-build-updates experiment is enabled. + */ +export interface WorkspaceBuildUpdate { + readonly workspace_id: string; + readonly workspace_name: string; + readonly build_id: string; + /** + * Transition is the workspace transition type: "start", "stop", or "delete". + */ + readonly transition: string; + /** + * JobStatus is the provisioner job status: "pending", "running", + * "succeeded", "canceling", "canceled", or "failed". + */ + readonly job_status: string; + readonly build_number: number; +} + +// From codersdk/workspaces.go +export interface WorkspaceBuildsRequest extends Pagination { + readonly since?: string; +} + +// From codersdk/deployment.go +export interface WorkspaceConnectionLatencyMS { + readonly P50: number; + readonly P95: number; +} + +// From codersdk/deployment.go +export interface WorkspaceDeploymentStats { + readonly pending: number; + readonly building: number; + readonly running: number; + readonly failed: number; + readonly stopped: number; + readonly connection_latency_ms: WorkspaceConnectionLatencyMS; + readonly rx_bytes: number; + readonly tx_bytes: number; +} + +// From codersdk/workspaces.go +export interface WorkspaceFilter { + /** + * FilterQuery supports a raw filter query string + */ + readonly q?: string; +} + +// From codersdk/workspaces.go +export interface WorkspaceGroup extends Group { + readonly role: WorkspaceRole; +} + +// From codersdk/workspaces.go +export interface WorkspaceHealth { + readonly healthy: boolean; // Healthy is true if the workspace is healthy. + readonly failing_agents: readonly string[]; // FailingAgents lists the IDs of the agents that are failing, if any. +} + +// From codersdk/workspaces.go +export interface WorkspaceOptions { + readonly include_deleted?: boolean; +} + +// From codersdk/workspaceproxy.go +export interface WorkspaceProxy extends Region { + readonly derp_enabled: boolean; + readonly derp_only: boolean; + /** + * Status is the latest status check of the proxy. This will be empty for deleted + * proxies. This value can be used to determine if a workspace proxy is healthy + * and ready to use. + */ + readonly status?: WorkspaceProxyStatus; + readonly created_at: string; + readonly updated_at: string; + readonly deleted: boolean; + readonly version: string; +} + +// From codersdk/deployment.go +export interface WorkspaceProxyBuildInfo { + /** + * TODO: @emyrk what should we include here? + */ + readonly workspace_proxy: boolean; + /** + * DashboardURL is the URL of the coderd this proxy is connected to. + */ + readonly dashboard_url: string; +} + +// From healthsdk/healthsdk.go +/** + * WorkspaceProxyReport includes health details of each connected workspace proxy. + */ +export interface WorkspaceProxyReport extends BaseReport { + /** + * Healthy is deprecated and left for backward compatibility purposes, use `Severity` instead. + */ + readonly healthy: boolean; + readonly workspace_proxies: RegionsResponse; +} + +// From codersdk/workspaceproxy.go +export interface WorkspaceProxyStatus { + readonly status: ProxyHealthStatus; + /** + * Report provides more information about the health of the workspace proxy. + */ + readonly report?: ProxyHealthReport; + readonly checked_at: string; +} + +// From codersdk/workspaces.go +export interface WorkspaceQuota { + readonly credits_consumed: number; + readonly budget: number; +} + +// From codersdk/workspacebuilds.go +/** + * WorkspaceResource describes resources used to create a workspace, for instance: + * containers, images, volumes. + */ +export interface WorkspaceResource { + readonly id: string; + readonly created_at: string; + readonly job_id: string; + readonly workspace_transition: WorkspaceTransition; + readonly type: string; + readonly name: string; + readonly hide: boolean; + readonly icon: string; + readonly agents?: readonly WorkspaceAgent[]; + readonly metadata?: readonly WorkspaceResourceMetadata[]; + readonly daily_cost: number; +} + +// From codersdk/workspacebuilds.go +/** + * WorkspaceResourceMetadata annotates the workspace resource with custom key-value pairs. + */ +export interface WorkspaceResourceMetadata { + readonly key: string; + readonly value: string; + readonly sensitive: boolean; +} + +// From codersdk/workspaces.go +export type WorkspaceRole = "admin" | "" | "use"; + +export const WorkspaceRoles: WorkspaceRole[] = ["admin", "", "use"]; + +// From codersdk/workspacesharing.go +/** + * WorkspaceSharingSettings represents workspace sharing settings affecting an + * organization. + */ +export interface WorkspaceSharingSettings { + /** + * SharingGloballyDisabled is true if sharing has been disabled for this + * organization because of a deployment-wide setting. + */ + readonly sharing_globally_disabled: boolean; + /** + * SharingDisabled is deprecated and left for backward compatibility + * purposes. + * @deprecated use `ShareableWorkspaceOwners` instead + */ + readonly sharing_disabled: boolean; + /** + * ShareableWorkspaceOwners controls whose workspaces can be shared + * within the organization. + */ + readonly shareable_workspace_owners: ShareableWorkspaceOwners; +} + +// From codersdk/workspacebuilds.go +export type WorkspaceStatus = + | "canceled" + | "canceling" + | "deleted" + | "deleting" + | "failed" + | "pending" + | "running" + | "starting" + | "stopped" + | "stopping"; + +export const WorkspaceStatuses: WorkspaceStatus[] = [ + "canceled", + "canceling", + "deleted", + "deleting", + "failed", + "pending", + "running", + "starting", + "stopped", + "stopping", +]; + +// From codersdk/workspacebuilds.go +export type WorkspaceTransition = "delete" | "start" | "stop"; + +export const WorkspaceTransitions: WorkspaceTransition[] = [ + "delete", + "start", + "stop", +]; + +// From codersdk/workspaces.go +export interface WorkspaceUser extends MinimalUser { + readonly role: WorkspaceRole; +} + +// From codersdk/workspaces.go +export interface WorkspacesRequest extends Pagination { + readonly q?: string; +} + +// From codersdk/workspaces.go +export interface WorkspacesResponse { + readonly workspaces: readonly Workspace[]; + readonly count: number; +} diff --git a/vendor/coder/site/src/utils/OneWayWebSocket.ts b/vendor/coder/site/src/utils/OneWayWebSocket.ts new file mode 100644 index 0000000000..fb2dcd3a3a --- /dev/null +++ b/vendor/coder/site/src/utils/OneWayWebSocket.ts @@ -0,0 +1,226 @@ +/** + * @file A wrapper over WebSockets that (1) enforces one-way communication, and + * (2) supports automatically parsing JSON messages as they come in. + * + * This should ALWAYS be favored in favor of using Server-Sent Events and the + * built-in EventSource class for doing one-way communication. SSEs have a hard + * limitation on HTTP/1.1 and below where there is a maximum number of 6 ports + * that can ever be used for a domain (sometimes less depending on the browser). + * Not only is this limit shared with short-lived REST requests, but it also + * applies across tabs and windows. So if a user opens Coder in multiple tabs, + * there is a very real possibility that parts of the app will start to lock up + * without it being clear why. + * + * WebSockets do not have this limitation, even on HTTP/1.1 – all modern + * browsers implement at least some degree of multiplexing for them. + */ + +// Not bothering with trying to borrow methods from the base WebSocket type +// because it's already a mess of inheritance and generics, and we're going to +// have to add a few more +export type WebSocketEventType = "close" | "error" | "message" | "open"; + +export type OneWayMessageEvent = Readonly< + | { + sourceEvent: MessageEvent; + parsedMessage: TData; + parseError: undefined; + } + | { + sourceEvent: MessageEvent; + parsedMessage: undefined; + parseError: Error; + } +>; + +type OneWayEventPayloadMap = { + close: CloseEvent; + error: Event; + message: OneWayMessageEvent; + open: Event; +}; + +type WebSocketMessageCallback = (payload: MessageEvent) => void; + +type OneWayEventCallback = ( + payload: OneWayEventPayloadMap[TEvent], +) => void; + +export interface OneWayWebSocketApi { + get url(): string; + + addEventListener: ( + eventType: TEvent, + callback: OneWayEventCallback, + ) => void; + + removeEventListener: ( + eventType: TEvent, + callback: OneWayEventCallback, + ) => void; + + close: (closeCode?: number, reason?: string) => void; +} + +type OneWayWebSocketInit = Readonly<{ + apiRoute: string; + serverProtocols?: string | string[]; + searchParams?: Record | URLSearchParams; + binaryType?: BinaryType; + websocketInit?: (url: string, protocols?: string | string[]) => WebSocket; + location?: Readonly<{ + protocol: string; + host: string; + }>; +}>; + +function defaultInit(url: string, protocols?: string | string[]): WebSocket { + return new WebSocket(url, protocols); +} + +export class OneWayWebSocket + implements OneWayWebSocketApi +{ + readonly #socket: WebSocket; + readonly #errorListeners = new Set<(e: Event) => void>(); + readonly #messageListenerWrappers = new Map< + OneWayEventCallback, + WebSocketMessageCallback + >(); + + constructor(init: OneWayWebSocketInit) { + const { + apiRoute, + searchParams, + serverProtocols, + binaryType = "blob", + location = window.location, + websocketInit = defaultInit, + } = init; + + if ( + !apiRoute.startsWith("/api/v2/") && + !apiRoute.startsWith("/api/experimental") + ) { + throw new Error( + `API route '${apiRoute}' does not begin with '/api/v2/' or '/api/experimental'`, + ); + } + + const formattedParams = + searchParams instanceof URLSearchParams + ? searchParams + : new URLSearchParams(searchParams); + const paramsString = formattedParams.toString(); + const paramsSuffix = paramsString ? `?${paramsString}` : ""; + const wsProtocol = location.protocol === "https:" ? "wss:" : "ws:"; + const url = `${wsProtocol}//${location.host}${apiRoute}${paramsSuffix}`; + + this.#socket = websocketInit(url, serverProtocols); + this.#socket.binaryType = binaryType; + } + + get url(): string { + return this.#socket.url; + } + + addEventListener( + event: TEvent, + callback: OneWayEventCallback, + ): void { + if (this.#socket.readyState === WebSocket.CLOSED) { + return; + } + + // Not happy about all the type assertions, but there are some nasty + // type contravariance issues if you try to resolve the function types + // properly. This is actually the lesser of two evils + const looseCallback = callback as OneWayEventCallback< + TData, + WebSocketEventType + >; + + // WebSockets automatically handle de-duping callbacks, but we have to + // do a separate check for the wrappers + if (this.#messageListenerWrappers.has(looseCallback)) { + return; + } + if (event !== "message") { + this.#socket.addEventListener(event, looseCallback); + if (event === "error") { + this.#errorListeners.add(looseCallback); + } + return; + } + + const wrapped = (event: MessageEvent): void => { + const messageCallback = looseCallback as OneWayEventCallback< + TData, + "message" + >; + + try { + const message = JSON.parse(event.data) as TData; + messageCallback({ + sourceEvent: event, + parseError: undefined, + parsedMessage: message, + }); + } catch (err) { + messageCallback({ + sourceEvent: event, + parseError: err as Error, + parsedMessage: undefined, + }); + } + }; + + this.#socket.addEventListener(event as "message", wrapped); + this.#messageListenerWrappers.set(looseCallback, wrapped); + } + + removeEventListener( + event: TEvent, + callback: OneWayEventCallback, + ): void { + const looseCallback = callback as OneWayEventCallback< + TData, + WebSocketEventType + >; + + if (event !== "message") { + this.#socket.removeEventListener(event, looseCallback); + if (event === "error") { + this.#errorListeners.delete(looseCallback); + } + return; + } + if (!this.#messageListenerWrappers.has(looseCallback)) { + return; + } + + const wrapper = this.#messageListenerWrappers.get(looseCallback); + if (wrapper === undefined) { + throw new Error( + `Cannot unregister callback for event ${event}. This is likely an issue with the browser itself.`, + ); + } + + this.#socket.removeEventListener(event as "message", wrapper); + this.#messageListenerWrappers.delete(looseCallback); + } + + close(closeCode?: number, reason?: string): void { + // Eject all error event listeners, mainly for ergonomics in React dev + // mode. React's StrictMode will create additional connections to ensure + // there aren't any render bugs, but manually closing a connection via a + // cleanup function sometimes causes error events to get dispatched for + // a connection that is no longer wired up to the UI + for (const cb of this.#errorListeners) { + this.#socket.removeEventListener("error", cb); + this.#errorListeners.delete(cb); + } + + this.#socket.close(closeCode, reason); + } +} diff --git a/vendor/coder/site/src/utils/delay.ts b/vendor/coder/site/src/utils/delay.ts new file mode 100644 index 0000000000..ac32333e79 --- /dev/null +++ b/vendor/coder/site/src/utils/delay.ts @@ -0,0 +1,4 @@ +export const delay = (ms: number): Promise => + new Promise((res) => { + setTimeout(res, ms); + }); diff --git a/vendor/coder/sync.mjs b/vendor/coder/sync.mjs new file mode 100644 index 0000000000..91fd0679ed --- /dev/null +++ b/vendor/coder/sync.mjs @@ -0,0 +1,41 @@ +#!/usr/bin/env node + +/** + * Downloads the coder/coder API client into ./site at upstream's paths, so + * imports stay `coder/site/src/...`. Pulling a few files avoids installing + * the whole repository as a git dependency. + */ + +import { mkdir, rm, writeFile } from "node:fs/promises"; +import { dirname, join } from "node:path"; + +/** A release commit, with its tag alongside. Bump both to resync. */ +const CODER_COMMIT = "22f4284f8fa67bc3af6fc38e0079616d39128566"; // v2.37.1 + +/** The modules we import, plus their relative imports. */ +const FILES = [ + "site/src/api/api.ts", + "site/src/api/errors.ts", + "site/src/api/typesGenerated.ts", + "site/src/utils/delay.ts", + "site/src/utils/OneWayWebSocket.ts", +]; + +const contents = await Promise.all( + FILES.map(async (file) => { + const url = `https://raw.githubusercontent.com/coder/coder/${CODER_COMMIT}/${file}`; + const response = await fetch(url); + if (!response.ok) { + throw new Error(`Failed to download ${url}: ${response.status}`); + } + return response.text(); + }), +); + +await rm(join(import.meta.dirname, "site"), { recursive: true, force: true }); +for (const [i, file] of FILES.entries()) { + const target = join(import.meta.dirname, file); + await mkdir(dirname(target), { recursive: true }); + await writeFile(target, contents[i]); +} +console.log(`Synced coder/coder ${CODER_COMMIT}`);