From 36b30767ede9b92f3165711863d2d0f12e7db31d Mon Sep 17 00:00:00 2001 From: bot-hermes Date: Tue, 8 Sep 2026 18:59:53 +0000 Subject: [PATCH] Integrate komodo_client package: typed rpc overloads, executeAndPoll, auto-generated types --- package-lock.json | 28 ++ package.json | 4 +- scripts/generate_types.ts | 434 +++++++++++++++++++++++++++++++ src/komodo-client.ts | 99 ++++++- src/types.ts | 530 +++++++++++++++++--------------------- 5 files changed, 793 insertions(+), 302 deletions(-) create mode 100644 scripts/generate_types.ts diff --git a/package-lock.json b/package-lock.json index cfc4344..c48d850 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "1.0.0", "dependencies": { "@modelcontextprotocol/sdk": "^1.12.1", + "komodo_client": "^2.3.2", "zod": "^3.24.0" }, "devDependencies": { @@ -1198,6 +1199,24 @@ "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==", "license": "BSD-2-Clause" }, + "node_modules/jwt-decode": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz", + "integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/komodo_client": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/komodo_client/-/komodo_client-2.3.2.tgz", + "integrity": "sha512-t/RQdFvIjphu+Sj7TA25Q6bj+y6ULpPHE/WD4TcBDf82KkVmYCJbX9EkTJaisakSeG/PUvLmu1pg2aAeP4N9PQ==", + "license": "GPL-3.0", + "dependencies": { + "mogh_auth_client": "^1.7.1" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -1257,6 +1276,15 @@ "url": "https://opencollective.com/express" } }, + "node_modules/mogh_auth_client": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/mogh_auth_client/-/mogh_auth_client-1.7.1.tgz", + "integrity": "sha512-IWT7ET5QmqNGqrGgNy4oyF4LMb1TeLXQhDw6AXzd5S/WfWC9JJx7xjnIdJFqXpl3zoZ+va773RrPB5ufGPkVVQ==", + "license": "MPL-2.0", + "dependencies": { + "jwt-decode": "^4.0.0" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", diff --git a/package.json b/package.json index 1207a8f..0a65166 100644 --- a/package.json +++ b/package.json @@ -7,10 +7,12 @@ "scripts": { "build": "tsc", "start": "node dist/index.js", - "dev": "tsx src/index.ts" + "dev": "tsx src/index.ts", + "generate-types": "tsx scripts/generate_types.ts" }, "dependencies": { "@modelcontextprotocol/sdk": "^1.12.1", + "komodo_client": "^2.3.2", "zod": "^3.24.0" }, "devDependencies": { diff --git a/scripts/generate_types.ts b/scripts/generate_types.ts new file mode 100644 index 0000000..8409cd8 --- /dev/null +++ b/scripts/generate_types.ts @@ -0,0 +1,434 @@ +#!/usr/bin/env tsx +/** + * Auto-generates mapping tables in src/types.ts from komodo_client package types. + * + * Run: npm run generate-types + * + * Parses the package's .d.ts files to extract request name literals from the + * ReadRequest, WriteRequest, ExecuteRequest discriminated unions, then derives + * resource-type → request-name mappings using Komodo's naming conventions. + */ + +import { readFileSync, writeFileSync } from "node:fs"; +import { resolve } from "node:path"; + +const PKG_TYPES = resolve( + import.meta.dirname, + "../node_modules/komodo_client/dist/types.d.ts", +); +const PKG_RESPONSES = resolve( + import.meta.dirname, + "../node_modules/komodo_client/dist/responses.d.ts", +); +const OUT = resolve(import.meta.dirname, "../src/types.ts"); + +// ── Parse type literals from a discriminated union ────────────────────────── + +function extractTypeLiterals(fileContent: string, unionName: string): string[] { + // Find the union: "export type ReadRequest = { type: "Foo"; ... } | { type: "Bar"; ... }" + const regex = new RegExp( + `export type ${unionName} =([\\s\\S]*?)(?=\\nexport |$)`, + ); + const match = fileContent.match(regex); + if (!match) throw new Error(`Could not find union ${unionName}`); + + const body = match[1]; + const literals: string[] = []; + const typeRegex = /type:\s*"([^"]+)"/g; + let m: RegExpExecArray | null; + while ((m = typeRegex.exec(body)) !== null) { + literals.push(m[1]); + } + return literals; +} + +// ── Parse response map keys ───────────────────────────────────────────────── + +function extractResponseKeys( + fileContent: string, + mapName: string, +): string[] { + const regex = new RegExp( + `export type ${mapName} =([\\s\\S]*?)(?=\\nexport |$)`, + ); + const match = fileContent.match(regex); + if (!match) throw new Error(`Could not find response map ${mapName}`); + + const body = match[1]; + const keys: string[] = []; + const keyRegex = /(\w+):\s*/g; + let m: RegExpExecArray | null; + while ((m = keyRegex.exec(body)) !== null) { + keys.push(m[1]); + } + return keys; +} + +// ── Resource type mapping rules ───────────────────────────────────────────── + +// Maps Komodo PascalCase resource names to our snake_case resource types. +// Only resources that have List*/Get*/Create*/Update*/Delete* endpoints qualify. +const RESOURCE_NAME_TO_TYPE: Record = { + Stack: "stack", + Build: "build", + Server: "server", + Procedure: "procedure", + Deployment: "deployment", + Alerter: "alerter", + ImageRegistryAccount: "image_registry_account", + ResourceSync: "sync_resource", + User: "user", + Tag: "tag", + Action: "action", + Repo: "repo", + Builder: "builder", + Swarm: "swarm", + Variable: "variable", + UserGroup: "user_group", + GitProviderAccount: "git_provider_account", +}; + +function deriveResourceType(requestName: string): string | null { + for (const [pascal, snake] of Object.entries(RESOURCE_NAME_TO_TYPE)) { + if (requestName === `List${pascal}s`) return snake; + if (requestName === `Get${pascal}`) return snake; + if (requestName === `Create${pascal}`) return snake; + if (requestName === `Update${pascal}`) return snake; + if (requestName === `Delete${pascal}`) return snake; + if (requestName === `Copy${pascal}`) return snake; + if (requestName === `Rename${pascal}`) return snake; + if (requestName === `Get${pascal}sSummary`) return snake; + } + return null; +} + +// ── Main ──────────────────────────────────────────────────────────────────── + +function main() { + const typesContent = readFileSync(PKG_TYPES, "utf-8"); + const responsesContent = readFileSync(PKG_RESPONSES, "utf-8"); + + const readRequests = extractTypeLiterals(typesContent, "ReadRequest"); + const writeRequests = extractTypeLiterals(typesContent, "WriteRequest"); + const executeRequests = extractTypeLiterals(typesContent, "ExecuteRequest"); + + // ── LIST_REQUEST_MAP ──────────────────────────────────────────────────── + const listEntries: [string, string][] = []; + for (const name of readRequests) { + const resourceType = deriveResourceType(name); + if (resourceType && name.startsWith("List") && !name.startsWith("ListFull")) { + listEntries.push([resourceType, name]); + } + } + + // ── GET_REQUEST_MAP ───────────────────────────────────────────────────── + const getEntries: [string, string][] = []; + for (const name of readRequests) { + const resourceType = deriveResourceType(name); + if (resourceType && name.startsWith("Get") && !name.endsWith("Summary") && !name.endsWith("ActionState")) { + getEntries.push([resourceType, name]); + } + } + + // ── CREATE_REQUEST_MAP ────────────────────────────────────────────────── + const createEntries: [string, string][] = []; + for (const name of writeRequests) { + const resourceType = deriveResourceType(name); + if (resourceType && name.startsWith("Create")) { + createEntries.push([resourceType, name]); + } + } + + // ── UPDATE_REQUEST_MAP ────────────────────────────────────────────────── + // Special cases where the update name doesn't follow Update{Resource} + const UPDATE_OVERRIDES: Record = { + tag: "UpdateTagColor", + variable: "UpdateVariableValue", + }; + const updateEntries: [string, string][] = []; + for (const name of writeRequests) { + if (name.startsWith("Update") && !name.startsWith("UpdateResourceMeta") && + !name.startsWith("UpdateServerPublicKey") && !name.startsWith("UpdateOnboardingKey") && + !name.startsWith("UpdateUser") && !name.startsWith("UpdatePermission") && + !name.startsWith("UpdateVariableDescription") && !name.startsWith("UpdateVariableIsSecret") && + !name.startsWith("UpdateGitProviderAccount") && !name.startsWith("UpdateImageRegistryAccount")) { + const resourceType = deriveResourceType(name); + if (resourceType) { + updateEntries.push([resourceType, name]); + } + } + } + // Apply overrides + for (const [rt, name] of Object.entries(UPDATE_OVERRIDES)) { + const idx = updateEntries.findIndex(([r]) => r === rt); + if (idx >= 0) updateEntries[idx][1] = name; + else updateEntries.push([rt, name]); + } + + // ── DELETE_REQUEST_MAP ────────────────────────────────────────────────── + const deleteEntries: [string, string][] = []; + for (const name of writeRequests) { + const resourceType = deriveResourceType(name); + if (resourceType && name.startsWith("Delete")) { + deleteEntries.push([resourceType, name]); + } + } + + // ── EXECUTE_REQUEST_MAP ───────────────────────────────────────────────── + const EXECUTE_OVERRIDES: Record = { + run_build: { route: "execute", name: "RunBuild" }, + deploy_stack_service: { route: "execute", name: "RunStackService" }, + run_server_prune_images: { route: "execute", name: "PruneImages" }, + run_server_prune_containers: { route: "execute", name: "PruneContainers" }, + run_server_prune_networks: { route: "execute", name: "PruneNetworks" }, + }; + + const executeEntries: [string, { route: string; name: string }][] = []; + for (const name of executeRequests) { + // Convert PascalCase to snake_case for the operation name + const snake = name + .replace(/([a-z0-9])([A-Z])/g, "$1_$2") + .toLowerCase(); + + // Check overrides first + if (EXECUTE_OVERRIDES[snake]) { + executeEntries.push([snake, EXECUTE_OVERRIDES[snake]]); + continue; + } + + // Default: route is "execute", name is the PascalCase name + executeEntries.push([snake, { route: "execute", name }]); + } + + // ── SUMMARY_REQUEST_MAP ───────────────────────────────────────────────── + const summaryEntries: [string, string][] = []; + for (const name of readRequests) { + if (name.endsWith("Summary")) { + const resourceType = deriveResourceType(name); + if (resourceType) { + summaryEntries.push([resourceType, name]); + } + } + } + + // ── INSPECT maps ────────────────────────────────────────────────────── + // INSPECT_PARAM_KEY: snake_case type → param key for server-scoped Docker objects + // INSPECT_REQUEST_MAP: snake_case type → request name + const INSPECT_PARAM_ENTRIES: [string, string][] = [ + ["container", "container"], + ["image", "image"], + ["network", "network"], + ["volume", "volume"], + ]; + const INSPECT_REQUEST_ENTRIES: [string, string][] = [ + ["container", "InspectContainer"], + ["image", "InspectImage"], + ["network", "InspectNetwork"], + ["volume", "InspectVolume"], + ["deployment_container", "InspectDeploymentContainer"], + ["deployment_swarm_service", "InspectDeploymentSwarmService"], + ["stack_container", "InspectStackContainer"], + ["stack_swarm_info", "InspectStackSwarmInfo"], + ["stack_swarm_service", "InspectStackSwarmService"], + ["swarm", "InspectSwarm"], + ["swarm_config", "InspectSwarmConfig"], + ["swarm_node", "InspectSwarmNode"], + ["swarm_secret", "InspectSwarmSecret"], + ["swarm_service", "InspectSwarmService"], + ["swarm_stack", "InspectSwarmStack"], + ["swarm_task", "InspectSwarmTask"], + ]; + + // ── SEARCH_LOG_REQUEST_MAP ────────────────────────────────────────────── + const SEARCH_LOG_MAP: Record = { + container: "SearchContainerLog", + deployment: "SearchDeploymentLog", + stack: "SearchStackLog", + swarm_service: "SearchSwarmServiceLog", + }; + const searchLogEntries = Object.entries(SEARCH_LOG_MAP); + + // ── LIST_DETAIL_REQUEST_MAP ───────────────────────────────────────────── + // These are the "extra" list endpoints that don't map to a resource type + const LIST_DETAIL_OVERRIDES: Record = { + containers: "ListContainers", + all_containers: "ListAllContainers", + images: "ListImages", + networks: "ListNetworks", + volumes: "ListVolumes", + system_processes: "ListSystemProcesses", + schedules: "ListSchedules", + permissions: "ListPermissions", + api_keys: "ListApiKeys", + api_keys_for_service_user: "ListApiKeysForServiceUser", + onboarding_keys: "ListOnboardingKeys", + secrets: "ListSecrets", + updates: "ListUpdates", + build_versions: "ListBuildVersions", + compose_projects: "ListComposeProjects", + user_target_permissions: "ListUserTargetPermissions", + full_stacks: "ListFullStacks", + full_builds: "ListFullBuilds", + full_servers: "ListFullServers", + full_deployments: "ListFullDeployments", + full_procedures: "ListFullProcedures", + full_repos: "ListFullRepos", + full_builders: "ListFullBuilders", + full_swarms: "ListFullSwarms", + full_actions: "ListFullActions", + full_alerters: "ListFullAlerters", + full_resource_syncs: "ListFullResourceSyncs", + swarm_configs: "ListSwarmConfigs", + swarm_networks: "ListSwarmNetworks", + swarm_nodes: "ListSwarmNodes", + swarm_secrets: "ListSwarmSecrets", + swarm_services: "ListSwarmServices", + swarm_stacks: "ListSwarmStacks", + swarm_tasks: "ListSwarmTasks", + git_providers_from_config: "ListGitProvidersFromConfig", + image_registries_from_config: "ListImageRegistriesFromConfig", + common_build_extra_args: "ListCommonBuildExtraArgs", + common_deployment_extra_args: "ListCommonDeploymentExtraArgs", + common_stack_build_extra_args: "ListCommonStackBuildExtraArgs", + common_stack_extra_args: "ListCommonStackExtraArgs", + image_history: "ListImageHistory", + all_stack_services: "ListAllStackServices", + }; + const listDetailEntries = Object.entries(LIST_DETAIL_OVERRIDES); + + // ── COPY_REQUEST_MAP ──────────────────────────────────────────────────── + const copyEntries: [string, string][] = []; + for (const name of writeRequests) { + const resourceType = deriveResourceType(name); + if (resourceType && name.startsWith("Copy")) { + copyEntries.push([resourceType, name]); + } + } + + // ── RENAME_REQUEST_MAP ────────────────────────────────────────────────── + const renameEntries: [string, string][] = []; + for (const name of writeRequests) { + if (name.startsWith("Rename")) { + const resourceType = deriveResourceType(name); + if (resourceType) { + renameEntries.push([resourceType, name]); + } + } + } + + // ── Generate output ───────────────────────────────────────────────────── + const out = `import { z } from "zod"; + +export const ResourceType = z.enum([ + "stack", + "build", + "server", + "procedure", + "deployment", + "alerter", + "image_registry_account", + "sync_resource", + "user", + "tag", + "action", + "repo", + "builder", + "swarm", + "variable", + "user_group", + "git_provider_account", +]); +export type ResourceType = z.infer; + +export const ExecuteOperation = z.enum([ + ${executeEntries.map(([k]) => `"${k}"`).join(",\n ")} +]); +export type ExecuteOperation = z.infer; + +// ── Auto-generated from komodo_client package types ──────────────────────── +// Run \`npm run generate-types\` to re-sync with upstream API. + +export const LIST_REQUEST_MAP: Record = { + ${sortEntries(listEntries).map(([k, v]) => `${k}: "${v}"`).join(",\n ")} +}; + +export const SEARCH_REQUEST_MAP: Partial> = { + ${sortEntries(listEntries.filter(([k]) => ["stack", "build", "server", "procedure", "deployment", "alerter", "image_registry_account", "sync_resource"].includes(k))).map(([k, v]) => `${k}: "${v}"`).join(",\n ")} +}; + +export const GET_REQUEST_MAP: Partial> = { + ${sortEntries(getEntries).map(([k, v]) => `${k}: "${v}"`).join(",\n ")} +}; + +export const CREATE_REQUEST_MAP: Partial> = { + ${sortEntries(createEntries).map(([k, v]) => `${k}: "${v}"`).join(",\n ")} +}; + +export const UPDATE_REQUEST_MAP: Partial> = { + ${sortEntries(updateEntries).map(([k, v]) => `${k}: "${v}"`).join(",\n ")} +}; + +export const DELETE_REQUEST_MAP: Partial> = { + ${sortEntries(deleteEntries).map(([k, v]) => `${k}: "${v}"`).join(",\n ")} +}; + +export const EXECUTE_REQUEST_MAP: Record< + ExecuteOperation, + { route: "execute" | "read" | "write"; name: string } +> = { + ${sortEntries(executeEntries).map(([k, v]) => `${k}: ${JSON.stringify(v)}`).join(",\n ")} +}; + +export const SUMMARY_REQUEST_MAP: Partial> = { + ${sortEntries(summaryEntries).map(([k, v]) => `${k}: "${v}"`).join(",\n ")} +}; + +export const INSPECT_PARAM_KEY: Record = { + ${INSPECT_PARAM_ENTRIES.map(([k, v]) => `${k}: "${v}"`).join(",\n ")} +}; + +export const INSPECT_REQUEST_MAP: Record = { + ${INSPECT_REQUEST_ENTRIES.map(([k, v]) => `${k}: "${v}"`).join(",\n ")} +}; + +export const SEARCH_LOG_REQUEST_MAP: Record = { + ${searchLogEntries.map(([k, v]) => `${k}: "${v}"`).join(",\n ")} +}; + +export const LIST_DETAIL_REQUEST_MAP: Record = { + ${listDetailEntries.map(([k, v]) => `${k}: "${v}"`).join(",\n ")} +}; + +export const COPY_REQUEST_MAP: Record = { + ${sortEntries(copyEntries).map(([k, v]) => `${k}: "${v}"`).join(",\n ")} +}; + +export const RENAME_REQUEST_MAP: Record = { + ${sortEntries(renameEntries).map(([k, v]) => `${k}: "${v}"`).join(",\n ")} +}; +`; + + writeFileSync(OUT, out, "utf-8"); + + // Summary + console.log(`Generated ${OUT}`); + console.log(` ReadRequest variants: ${readRequests.length}`); + console.log(` WriteRequest variants: ${writeRequests.length}`); + console.log(` ExecuteRequest variants: ${executeRequests.length}`); + console.log(` LIST_REQUEST_MAP: ${listEntries.length} entries`); + console.log(` GET_REQUEST_MAP: ${getEntries.length} entries`); + console.log(` CREATE_REQUEST_MAP: ${createEntries.length} entries`); + console.log(` UPDATE_REQUEST_MAP: ${updateEntries.length} entries`); + console.log(` DELETE_REQUEST_MAP: ${deleteEntries.length} entries`); + console.log(` EXECUTE_REQUEST_MAP: ${executeEntries.length} entries`); + console.log(` SUMMARY_REQUEST_MAP: ${summaryEntries.length} entries`); + console.log(` INSPECT_REQUEST_MAP: ${INSPECT_REQUEST_ENTRIES.length} entries`); + console.log(` COPY_REQUEST_MAP: ${copyEntries.length} entries`); + console.log(` RENAME_REQUEST_MAP: ${renameEntries.length} entries`); +} + +function sortEntries(entries: [string, string][]): [string, string][] { + return [...entries].sort(([a], [b]) => a.localeCompare(b)); +} + +main(); diff --git a/src/komodo-client.ts b/src/komodo-client.ts index 895701e..e3151ef 100644 --- a/src/komodo-client.ts +++ b/src/komodo-client.ts @@ -1,4 +1,13 @@ import { KomodoApiError } from "./errors.js"; +import { Types } from "komodo_client"; +import type { ReadResponses, WriteResponses, ExecuteResponses } from "komodo_client"; + +type ReadRequest = Types.ReadRequest; +type WriteRequest = Types.WriteRequest; +type ExecuteRequest = Types.ExecuteRequest; +type Update = Types.Update; + +type Route = "read" | "write" | "execute"; export class KomodoClient { private baseUrl: string; @@ -19,11 +28,6 @@ export class KomodoClient { this.useApiKey = !!(this.apiKey && this.apiSecret); } - /** - * Returns the appropriate auth headers based on the configured auth method. - * API key auth (X-Api-Key / X-Api-Secret) is preferred when credentials are set; - * otherwise falls back to JWT Bearer token. - */ private async getAuthHeaders(): Promise> { if (this.useApiKey) { return { @@ -38,7 +42,7 @@ export class KomodoClient { } private async ensureAuth(): Promise { - if (this.useApiKey) return; // No login needed for API key auth + if (this.useApiKey) return; if (this.token) return; await this.login(); } @@ -47,7 +51,6 @@ export class KomodoClient { const res = await fetch(`${this.baseUrl}/auth/login`, { method: "POST", headers: { "Content-Type": "application/json" }, - // Komodo Core v2.3.3 login shape (verified live): {type, params} body: JSON.stringify({ type: "LoginLocalUser", params: { @@ -61,7 +64,6 @@ export class KomodoClient { const text = await res.text().catch(() => ""); throw new Error(`Komodo login failed (${res.status}): ${text}`); } - // Response shape: {type, data: {jwt}} — NOT raw text const body = (await res.json()) as { data?: { jwt?: string } }; const jwt = body?.data?.jwt; if (!jwt) { @@ -70,8 +72,8 @@ export class KomodoClient { this.token = jwt; } - async rpc( - route: "read" | "write" | "execute", + private async rawRpc( + route: Route, requestName: string, params: Record = {}, ): Promise { @@ -96,7 +98,6 @@ export class KomodoClient { const authHeaders = await this.getAuthHeaders(); let res = await attempt(authHeaders); - // Auto-refresh on 401 (JWT only) if (res.status === 401 && !this.useApiKey) { this.token = null; await this.login(); @@ -129,4 +130,80 @@ export class KomodoClient { return text; } } + + async rpc( + route: "read", + requestName: string, + params?: Record, + ): Promise; + async rpc( + route: "write", + requestName: string, + params?: Record, + ): Promise; + async rpc( + route: "execute", + requestName: string, + params?: Record, + ): Promise; + async rpc( + route: Route, + requestName: string, + params?: Record, + ): Promise; + async rpc( + route: Route, + requestName: string, + params: Record = {}, + ): Promise { + return this.rawRpc(route, requestName, params); + } + + async executeAndPoll( + requestName: ExecuteRequest["type"], + params: Record = {}, + opts: { pollIntervalMs?: number; timeoutMs?: number } = {}, + ): Promise { + const { pollIntervalMs = 1000, timeoutMs = 300_000 } = opts; + + const result = await this.rawRpc("execute", requestName, params); + + if (Array.isArray(result)) { + const first = (result as Record[])[0]; + if (first && first.status === "Err") { + throw new Error(`Batch execute failed: ${JSON.stringify(first.data)}`); + } + const update = result[0] as unknown as Update; + if (update.status === "Complete" || !update._id?.$oid) { + return update; + } + return this.pollUntilComplete(update._id.$oid, pollIntervalMs, timeoutMs); + } + + const update = result as unknown as Update; + if (update.status === "Complete" || !update._id?.$oid) { + return update; + } + return this.pollUntilComplete(update._id.$oid, pollIntervalMs, timeoutMs); + } + + private async pollUntilComplete( + updateId: string, + intervalMs: number, + timeoutMs: number, + ): Promise { + const deadline = Date.now() + timeoutMs; + while (Date.now() < deadline) { + await new Promise((r) => setTimeout(r, intervalMs)); + const update = (await this.rawRpc("read", "GetUpdate", { + id: updateId, + })) as Update; + if (update.status === "Complete") { + return update; + } + } + throw new Error( + `Timed out polling update ${updateId} after ${timeoutMs}ms`, + ); + } } diff --git a/src/types.ts b/src/types.ts index 6862c09..47559b8 100644 --- a/src/types.ts +++ b/src/types.ts @@ -22,343 +22,300 @@ export const ResourceType = z.enum([ export type ResourceType = z.infer; export const ExecuteOperation = z.enum([ - // Build & Deploy - "run_build", "deploy_stack", - "deploy_stack_service", - "deploy", + "batch_deploy_stack", "deploy_stack_if_changed", - "destroy_stack", - "destroy_container", - "destroy_deployment", + "batch_deploy_stack_if_changed", "pull_stack", - "pull_deployment", - // Repo operations - "build_repo", - "clone_repo", - "pull_repo", - "cancel_repo_build", - "clear_repo_cache", - // Procedure & Action - "run_procedure", - "run_action", - "run_sync", - "cancel_action", - "cancel_build", - "cancel_procedure", - // Stack lifecycle + "batch_pull_stack", "start_stack", - "stop_stack", "restart_stack", + "stop_stack", "pause_stack", "unpause_stack", - // Container lifecycle - "start_container", - "stop_container", - "restart_container", - "pause_container", - "unpause_container", - "start_all_containers", - "stop_all_containers", - "restart_all_containers", - "pause_all_containers", - "unpause_all_containers", - // Deployment lifecycle + "destroy_stack", + "batch_destroy_stack", + "run_stack_service", + "deploy", + "batch_deploy", + "pull_deployment", "start_deployment", - "stop_deployment", "restart_deployment", "pause_deployment", "unpause_deployment", - // Server operations - "run_server_prune_images", - "run_server_prune_containers", - "run_server_prune_networks", - "rotate_all_server_keys", - // Docker prune & cleanup - "prune_buildx", - "prune_docker_builders", - "prune_system", - "prune_volumes", - "delete_image", - "delete_network", - "delete_volume", - // Swarm operations - "remove_swarm_nodes", - "remove_swarm_services", - "remove_swarm_configs", - "remove_swarm_secrets", - "remove_swarm_stacks", - "create_swarm_config", - "create_swarm_secret", - "rotate_swarm_config", - "rotate_swarm_secret", - "update_swarm_node", - // Batch operations - "batch_run_build", - "batch_deploy_stack", - "batch_deploy_stack_if_changed", - "batch_deploy", - "batch_pull_stack", - "batch_pull_repo", - "batch_build_repo", - "batch_clone_repo", - "batch_run_procedure", - "batch_run_action", + "stop_deployment", + "destroy_deployment", "batch_destroy_deployment", - "batch_destroy_stack", - // Admin & misc - "backup_core_database", - "rotate_core_keys", - "global_auto_update", - "send_alert", + "run_build", + "batch_run_build", + "cancel_build", + "clone_repo", + "batch_clone_repo", + "pull_repo", + "batch_pull_repo", + "build_repo", + "batch_build_repo", + "cancel_repo_build", + "run_procedure", + "batch_run_procedure", + "cancel_procedure", + "run_action", + "batch_run_action", + "cancel_action", + "run_sync", "test_alerter", + "send_alert", + "start_container", + "restart_container", + "pause_container", + "unpause_container", + "stop_container", + "destroy_container", + "start_all_containers", + "restart_all_containers", + "pause_all_containers", + "unpause_all_containers", + "stop_all_containers", + "prune_containers", + "delete_network", + "prune_networks", + "delete_image", + "prune_images", + "delete_volume", + "prune_volumes", + "prune_docker_builders", + "prune_buildx", + "prune_system", + "remove_swarm_nodes", + "update_swarm_node", + "remove_swarm_stacks", + "remove_swarm_services", + "create_swarm_config", + "rotate_swarm_config", + "remove_swarm_configs", + "create_swarm_secret", + "rotate_swarm_secret", + "remove_swarm_secrets", + "clear_repo_cache", + "backup_core_database", + "global_auto_update", + "rotate_all_server_keys", + "rotate_core_keys" ]); export type ExecuteOperation = z.infer; -// Maps resource_type to the list RPC request name +// ── Auto-generated from komodo_client package types ──────────────────────── +// Run `npm run generate-types` to re-sync with upstream API. + export const LIST_REQUEST_MAP: Record = { - stack: "ListStacks", - build: "ListBuilds", - server: "ListServers", - procedure: "ListProcedures", - deployment: "ListDeployments", - alerter: "ListAlerters", - image_registry_account: "ListImageRegistryAccounts", - sync_resource: "ListResourceSyncs", - user: "ListUsers", - tag: "ListTags", action: "ListActions", - repo: "ListRepos", - builder: "ListBuilders", - swarm: "ListSwarms", - variable: "ListVariables", - user_group: "ListUserGroups", - git_provider_account: "ListGitProviderAccounts", -}; - -// Maps resource_type to the search RPC request name -// Note: Search* endpoints exist for core resource types only. -// Types not listed here fall back to their List* endpoint. -export const SEARCH_REQUEST_MAP: Partial> = { - stack: "ListStacks", - build: "ListBuilds", - server: "ListServers", - procedure: "ListProcedures", - deployment: "ListDeployments", alerter: "ListAlerters", + build: "ListBuilds", + builder: "ListBuilders", + deployment: "ListDeployments", + git_provider_account: "ListGitProviderAccounts", image_registry_account: "ListImageRegistryAccounts", + procedure: "ListProcedures", + repo: "ListRepos", + server: "ListServers", + stack: "ListStacks", + swarm: "ListSwarms", sync_resource: "ListResourceSyncs", + tag: "ListTags", + user: "ListUsers", + user_group: "ListUserGroups", + variable: "ListVariables" +}; + +export const SEARCH_REQUEST_MAP: Partial> = { + alerter: "ListAlerters", + build: "ListBuilds", + deployment: "ListDeployments", + image_registry_account: "ListImageRegistryAccounts", + procedure: "ListProcedures", + server: "ListServers", + stack: "ListStacks", + sync_resource: "ListResourceSyncs" }; -// Maps resource_type to the get RPC request name export const GET_REQUEST_MAP: Partial> = { - stack: "GetStack", - build: "GetBuild", - server: "GetServer", - procedure: "GetProcedure", - deployment: "GetDeployment", + action: "GetAction", alerter: "GetAlerter", + build: "GetBuild", + builder: "GetBuilder", + deployment: "GetDeployment", + git_provider_account: "GetGitProviderAccount", image_registry_account: "GetImageRegistryAccount", + procedure: "GetProcedure", + repo: "GetRepo", + server: "GetServer", + stack: "GetStack", + swarm: "GetSwarm", sync_resource: "GetResourceSync", tag: "GetTag", - action: "GetAction", - repo: "GetRepo", - builder: "GetBuilder", - swarm: "GetSwarm", - variable: "GetVariable", user_group: "GetUserGroup", - git_provider_account: "GetGitProviderAccount", + variable: "GetVariable" }; -// Maps resource_type to the create RPC request name export const CREATE_REQUEST_MAP: Partial> = { - stack: "CreateStack", - build: "CreateBuild", - server: "CreateServer", - procedure: "CreateProcedure", - deployment: "CreateDeployment", + action: "CreateAction", alerter: "CreateAlerter", + build: "CreateBuild", + builder: "CreateBuilder", + deployment: "CreateDeployment", + git_provider_account: "CreateGitProviderAccount", image_registry_account: "CreateImageRegistryAccount", + procedure: "CreateProcedure", + repo: "CreateRepo", + server: "CreateServer", + stack: "CreateStack", + swarm: "CreateSwarm", sync_resource: "CreateResourceSync", tag: "CreateTag", - action: "CreateAction", - repo: "CreateRepo", - builder: "CreateBuilder", - swarm: "CreateSwarm", - variable: "CreateVariable", user_group: "CreateUserGroup", - git_provider_account: "CreateGitProviderAccount", + variable: "CreateVariable" }; -// Maps resource_type to the update RPC request name export const UPDATE_REQUEST_MAP: Partial> = { - stack: "UpdateStack", - build: "UpdateBuild", - server: "UpdateServer", - procedure: "UpdateProcedure", - deployment: "UpdateDeployment", + action: "UpdateAction", alerter: "UpdateAlerter", - image_registry_account: "UpdateImageRegistryAccount", + build: "UpdateBuild", + builder: "UpdateBuilder", + deployment: "UpdateDeployment", + procedure: "UpdateProcedure", + repo: "UpdateRepo", + server: "UpdateServer", + stack: "UpdateStack", + swarm: "UpdateSwarm", sync_resource: "UpdateResourceSync", tag: "UpdateTagColor", - action: "UpdateAction", - repo: "UpdateRepo", - builder: "UpdateBuilder", - swarm: "UpdateSwarm", - variable: "UpdateVariableValue", - git_provider_account: "UpdateGitProviderAccount", + variable: "UpdateVariableValue" }; -// Maps resource_type to the delete RPC request name export const DELETE_REQUEST_MAP: Partial> = { - stack: "DeleteStack", - build: "DeleteBuild", - server: "DeleteServer", - procedure: "DeleteProcedure", - deployment: "DeleteDeployment", + action: "DeleteAction", alerter: "DeleteAlerter", + build: "DeleteBuild", + builder: "DeleteBuilder", + deployment: "DeleteDeployment", + git_provider_account: "DeleteGitProviderAccount", + image_registry_account: "DeleteImageRegistryAccount", + procedure: "DeleteProcedure", + repo: "DeleteRepo", + server: "DeleteServer", + stack: "DeleteStack", + swarm: "DeleteSwarm", sync_resource: "DeleteResourceSync", tag: "DeleteTag", - action: "DeleteAction", - repo: "DeleteRepo", - builder: "DeleteBuilder", - swarm: "DeleteSwarm", - variable: "DeleteVariable", + user: "DeleteUser", user_group: "DeleteUserGroup", - git_provider_account: "DeleteGitProviderAccount", + variable: "DeleteVariable" }; -// Maps execute operation to the RPC request name and the route export const EXECUTE_REQUEST_MAP: Record< ExecuteOperation, { route: "execute" | "read" | "write"; name: string } > = { - // Build & Deploy - run_build: { route: "execute", name: "RunBuild" }, - deploy_stack: { route: "execute", name: "DeployStack" }, - deploy_stack_service: { route: "execute", name: "RunStackService" }, - deploy: { route: "execute", name: "Deploy" }, - deploy_stack_if_changed: { route: "execute", name: "DeployStackIfChanged" }, - destroy_stack: { route: "execute", name: "DestroyStack" }, - destroy_container: { route: "execute", name: "DestroyContainer" }, - destroy_deployment: { route: "execute", name: "DestroyDeployment" }, - pull_stack: { route: "execute", name: "PullStack" }, - pull_deployment: { route: "execute", name: "PullDeployment" }, - // Repo operations - build_repo: { route: "execute", name: "BuildRepo" }, - clone_repo: { route: "execute", name: "CloneRepo" }, - pull_repo: { route: "execute", name: "PullRepo" }, - cancel_repo_build: { route: "execute", name: "CancelRepoBuild" }, - clear_repo_cache: { route: "execute", name: "ClearRepoCache" }, - // Procedure & Action - run_procedure: { route: "execute", name: "RunProcedure" }, - run_action: { route: "execute", name: "RunAction" }, - run_sync: { route: "execute", name: "RunSync" }, - cancel_action: { route: "execute", name: "CancelAction" }, - cancel_build: { route: "execute", name: "CancelBuild" }, - cancel_procedure: { route: "execute", name: "CancelProcedure" }, - // Stack lifecycle - start_stack: { route: "execute", name: "StartStack" }, - stop_stack: { route: "execute", name: "StopStack" }, - restart_stack: { route: "execute", name: "RestartStack" }, - pause_stack: { route: "execute", name: "PauseStack" }, - unpause_stack: { route: "execute", name: "UnpauseStack" }, - // Container lifecycle - start_container: { route: "execute", name: "StartContainer" }, - stop_container: { route: "execute", name: "StopContainer" }, - restart_container: { route: "execute", name: "RestartContainer" }, - pause_container: { route: "execute", name: "PauseContainer" }, - unpause_container: { route: "execute", name: "UnpauseContainer" }, - start_all_containers: { route: "execute", name: "StartAllContainers" }, - stop_all_containers: { route: "execute", name: "StopAllContainers" }, - restart_all_containers: { route: "execute", name: "RestartAllContainers" }, - pause_all_containers: { route: "execute", name: "PauseAllContainers" }, - unpause_all_containers: { route: "execute", name: "UnpauseAllContainers" }, - // Deployment lifecycle - start_deployment: { route: "execute", name: "StartDeployment" }, - stop_deployment: { route: "execute", name: "StopDeployment" }, - restart_deployment: { route: "execute", name: "RestartDeployment" }, - pause_deployment: { route: "execute", name: "PauseDeployment" }, - unpause_deployment: { route: "execute", name: "UnpauseDeployment" }, - // Server operations - run_server_prune_images: { route: "execute", name: "PruneImages" }, - run_server_prune_containers: { route: "execute", name: "PruneContainers" }, - run_server_prune_networks: { route: "execute", name: "PruneNetworks" }, - rotate_all_server_keys: { - route: "execute", - name: "RotateAllServerKeys", - }, - // Docker prune & cleanup - prune_buildx: { route: "execute", name: "PruneBuildx" }, - prune_docker_builders: { route: "execute", name: "PruneDockerBuilders" }, - prune_system: { route: "execute", name: "PruneSystem" }, - prune_volumes: { route: "execute", name: "PruneVolumes" }, - delete_image: { route: "execute", name: "DeleteImage" }, - delete_network: { route: "execute", name: "DeleteNetwork" }, - delete_volume: { route: "execute", name: "DeleteVolume" }, - // Swarm operations - remove_swarm_nodes: { route: "execute", name: "RemoveSwarmNodes" }, - remove_swarm_services: { route: "execute", name: "RemoveSwarmServices" }, - remove_swarm_configs: { route: "execute", name: "RemoveSwarmConfigs" }, - remove_swarm_secrets: { route: "execute", name: "RemoveSwarmSecrets" }, - remove_swarm_stacks: { route: "execute", name: "RemoveSwarmStacks" }, - create_swarm_config: { route: "execute", name: "CreateSwarmConfig" }, - create_swarm_secret: { route: "execute", name: "CreateSwarmSecret" }, - rotate_swarm_config: { route: "execute", name: "RotateSwarmConfig" }, - rotate_swarm_secret: { route: "execute", name: "RotateSwarmSecret" }, - update_swarm_node: { route: "execute", name: "UpdateSwarmNode" }, - // Batch operations - batch_run_build: { route: "execute", name: "BatchRunBuild" }, - batch_deploy_stack: { route: "execute", name: "BatchDeployStack" }, - batch_deploy_stack_if_changed: { - route: "execute", - name: "BatchDeployStackIfChanged", - }, - batch_deploy: { route: "execute", name: "BatchDeploy" }, - batch_pull_stack: { route: "execute", name: "BatchPullStack" }, - batch_pull_repo: { route: "execute", name: "BatchPullRepo" }, - batch_build_repo: { route: "execute", name: "BatchBuildRepo" }, - batch_clone_repo: { route: "execute", name: "BatchCloneRepo" }, - batch_run_procedure: { route: "execute", name: "BatchRunProcedure" }, - batch_run_action: { route: "execute", name: "BatchRunAction" }, - batch_destroy_deployment: { - route: "execute", - name: "BatchDestroyDeployment", - }, - batch_destroy_stack: { route: "execute", name: "BatchDestroyStack" }, - // Admin & misc - backup_core_database: { route: "execute", name: "BackupCoreDatabase" }, - rotate_core_keys: { route: "execute", name: "RotateCoreKeys" }, - global_auto_update: { route: "execute", name: "GlobalAutoUpdate" }, - send_alert: { route: "execute", name: "SendAlert" }, - test_alerter: { route: "execute", name: "TestAlerter" }, + backup_core_database: {"route":"execute","name":"BackupCoreDatabase"}, + batch_build_repo: {"route":"execute","name":"BatchBuildRepo"}, + batch_clone_repo: {"route":"execute","name":"BatchCloneRepo"}, + batch_deploy: {"route":"execute","name":"BatchDeploy"}, + batch_deploy_stack: {"route":"execute","name":"BatchDeployStack"}, + batch_deploy_stack_if_changed: {"route":"execute","name":"BatchDeployStackIfChanged"}, + batch_destroy_deployment: {"route":"execute","name":"BatchDestroyDeployment"}, + batch_destroy_stack: {"route":"execute","name":"BatchDestroyStack"}, + batch_pull_repo: {"route":"execute","name":"BatchPullRepo"}, + batch_pull_stack: {"route":"execute","name":"BatchPullStack"}, + batch_run_action: {"route":"execute","name":"BatchRunAction"}, + batch_run_build: {"route":"execute","name":"BatchRunBuild"}, + batch_run_procedure: {"route":"execute","name":"BatchRunProcedure"}, + build_repo: {"route":"execute","name":"BuildRepo"}, + cancel_action: {"route":"execute","name":"CancelAction"}, + cancel_build: {"route":"execute","name":"CancelBuild"}, + cancel_procedure: {"route":"execute","name":"CancelProcedure"}, + cancel_repo_build: {"route":"execute","name":"CancelRepoBuild"}, + clear_repo_cache: {"route":"execute","name":"ClearRepoCache"}, + clone_repo: {"route":"execute","name":"CloneRepo"}, + create_swarm_config: {"route":"execute","name":"CreateSwarmConfig"}, + create_swarm_secret: {"route":"execute","name":"CreateSwarmSecret"}, + delete_image: {"route":"execute","name":"DeleteImage"}, + delete_network: {"route":"execute","name":"DeleteNetwork"}, + delete_volume: {"route":"execute","name":"DeleteVolume"}, + deploy: {"route":"execute","name":"Deploy"}, + deploy_stack: {"route":"execute","name":"DeployStack"}, + deploy_stack_if_changed: {"route":"execute","name":"DeployStackIfChanged"}, + destroy_container: {"route":"execute","name":"DestroyContainer"}, + destroy_deployment: {"route":"execute","name":"DestroyDeployment"}, + destroy_stack: {"route":"execute","name":"DestroyStack"}, + global_auto_update: {"route":"execute","name":"GlobalAutoUpdate"}, + pause_all_containers: {"route":"execute","name":"PauseAllContainers"}, + pause_container: {"route":"execute","name":"PauseContainer"}, + pause_deployment: {"route":"execute","name":"PauseDeployment"}, + pause_stack: {"route":"execute","name":"PauseStack"}, + prune_buildx: {"route":"execute","name":"PruneBuildx"}, + prune_containers: {"route":"execute","name":"PruneContainers"}, + prune_docker_builders: {"route":"execute","name":"PruneDockerBuilders"}, + prune_images: {"route":"execute","name":"PruneImages"}, + prune_networks: {"route":"execute","name":"PruneNetworks"}, + prune_system: {"route":"execute","name":"PruneSystem"}, + prune_volumes: {"route":"execute","name":"PruneVolumes"}, + pull_deployment: {"route":"execute","name":"PullDeployment"}, + pull_repo: {"route":"execute","name":"PullRepo"}, + pull_stack: {"route":"execute","name":"PullStack"}, + remove_swarm_configs: {"route":"execute","name":"RemoveSwarmConfigs"}, + remove_swarm_nodes: {"route":"execute","name":"RemoveSwarmNodes"}, + remove_swarm_secrets: {"route":"execute","name":"RemoveSwarmSecrets"}, + remove_swarm_services: {"route":"execute","name":"RemoveSwarmServices"}, + remove_swarm_stacks: {"route":"execute","name":"RemoveSwarmStacks"}, + restart_all_containers: {"route":"execute","name":"RestartAllContainers"}, + restart_container: {"route":"execute","name":"RestartContainer"}, + restart_deployment: {"route":"execute","name":"RestartDeployment"}, + restart_stack: {"route":"execute","name":"RestartStack"}, + rotate_all_server_keys: {"route":"execute","name":"RotateAllServerKeys"}, + rotate_core_keys: {"route":"execute","name":"RotateCoreKeys"}, + rotate_swarm_config: {"route":"execute","name":"RotateSwarmConfig"}, + rotate_swarm_secret: {"route":"execute","name":"RotateSwarmSecret"}, + run_action: {"route":"execute","name":"RunAction"}, + run_build: {"route":"execute","name":"RunBuild"}, + run_procedure: {"route":"execute","name":"RunProcedure"}, + run_stack_service: {"route":"execute","name":"RunStackService"}, + run_sync: {"route":"execute","name":"RunSync"}, + send_alert: {"route":"execute","name":"SendAlert"}, + start_all_containers: {"route":"execute","name":"StartAllContainers"}, + start_container: {"route":"execute","name":"StartContainer"}, + start_deployment: {"route":"execute","name":"StartDeployment"}, + start_stack: {"route":"execute","name":"StartStack"}, + stop_all_containers: {"route":"execute","name":"StopAllContainers"}, + stop_container: {"route":"execute","name":"StopContainer"}, + stop_deployment: {"route":"execute","name":"StopDeployment"}, + stop_stack: {"route":"execute","name":"StopStack"}, + test_alerter: {"route":"execute","name":"TestAlerter"}, + unpause_all_containers: {"route":"execute","name":"UnpauseAllContainers"}, + unpause_container: {"route":"execute","name":"UnpauseContainer"}, + unpause_deployment: {"route":"execute","name":"UnpauseDeployment"}, + unpause_stack: {"route":"execute","name":"UnpauseStack"}, + update_swarm_node: {"route":"execute","name":"UpdateSwarmNode"} }; -// Maps resource_type to the summary RPC request name export const SUMMARY_REQUEST_MAP: Partial> = { + action: "GetActionsSummary", + alerter: "GetAlertersSummary", build: "GetBuildsSummary", + builder: "GetBuildersSummary", deployment: "GetDeploymentsSummary", + procedure: "GetProceduresSummary", + repo: "GetReposSummary", server: "GetServersSummary", stack: "GetStacksSummary", - alerter: "GetAlertersSummary", - procedure: "GetProceduresSummary", - sync_resource: "GetResourceSyncsSummary", - repo: "GetReposSummary", - builder: "GetBuildersSummary", swarm: "GetSwarmsSummary", - action: "GetActionsSummary", + sync_resource: "GetResourceSyncsSummary" }; -// Maps inspect type to the inspect RPC request name. -// Server-scoped Docker objects also need an object param key (see -// INSPECT_PARAM_KEY below): Core v2.3.3 shapes are {server, container}, -// {server, image}, {server, network}, {server, volume} — sending -// {id, server} fails with "duplicate field `server`". export const INSPECT_PARAM_KEY: Record = { container: "container", image: "image", network: "network", - volume: "volume", + volume: "volume" }; export const INSPECT_REQUEST_MAP: Record = { @@ -377,18 +334,16 @@ export const INSPECT_REQUEST_MAP: Record = { swarm_secret: "InspectSwarmSecret", swarm_service: "InspectSwarmService", swarm_stack: "InspectSwarmStack", - swarm_task: "InspectSwarmTask", + swarm_task: "InspectSwarmTask" }; -// Maps search log type to the RPC request name export const SEARCH_LOG_REQUEST_MAP: Record = { container: "SearchContainerLog", deployment: "SearchDeploymentLog", stack: "SearchStackLog", - swarm_service: "SearchSwarmServiceLog", + swarm_service: "SearchSwarmServiceLog" }; -// Maps list detail type to the RPC request name export const LIST_DETAIL_REQUEST_MAP: Record = { containers: "ListContainers", all_containers: "ListAllContainers", @@ -417,7 +372,6 @@ export const LIST_DETAIL_REQUEST_MAP: Record = { full_actions: "ListFullActions", full_alerters: "ListFullAlerters", full_resource_syncs: "ListFullResourceSyncs", - // Swarm detail lists swarm_configs: "ListSwarmConfigs", swarm_networks: "ListSwarmNetworks", swarm_nodes: "ListSwarmNodes", @@ -425,46 +379,42 @@ export const LIST_DETAIL_REQUEST_MAP: Record = { swarm_services: "ListSwarmServices", swarm_stacks: "ListSwarmStacks", swarm_tasks: "ListSwarmTasks", - // Config lists git_providers_from_config: "ListGitProvidersFromConfig", image_registries_from_config: "ListImageRegistriesFromConfig", - // Extra args common_build_extra_args: "ListCommonBuildExtraArgs", common_deployment_extra_args: "ListCommonDeploymentExtraArgs", common_stack_build_extra_args: "ListCommonStackBuildExtraArgs", common_stack_extra_args: "ListCommonStackExtraArgs", - // Other image_history: "ListImageHistory", - all_stack_services: "ListAllStackServices", + all_stack_services: "ListAllStackServices" }; -// Maps copy resource type to the Copy* RPC request name export const COPY_REQUEST_MAP: Record = { - stack: "CopyStack", + action: "CopyAction", + alerter: "CopyAlerter", build: "CopyBuild", - server: "CopyServer", + builder: "CopyBuilder", deployment: "CopyDeployment", procedure: "CopyProcedure", - alerter: "CopyAlerter", - action: "CopyAction", repo: "CopyRepo", - builder: "CopyBuilder", + server: "CopyServer", + stack: "CopyStack", swarm: "CopySwarm", - sync_resource: "CopyResourceSync", + sync_resource: "CopyResourceSync" }; -// Maps rename resource type to the Rename* RPC request name export const RENAME_REQUEST_MAP: Record = { - stack: "RenameStack", + action: "RenameAction", + alerter: "RenameAlerter", build: "RenameBuild", - server: "RenameServer", + builder: "RenameBuilder", deployment: "RenameDeployment", procedure: "RenameProcedure", - alerter: "RenameAlerter", - action: "RenameAction", repo: "RenameRepo", - builder: "RenameBuilder", + server: "RenameServer", + stack: "RenameStack", swarm: "RenameSwarm", + sync_resource: "RenameResourceSync", tag: "RenameTag", - user_group: "RenameUserGroup", + user_group: "RenameUserGroup" };