@hoststack.dev/mcp 0.25.0 → 0.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/hoststack-mcp.js +181 -16
- package/dist/hoststack-mcp.js.map +1 -1
- package/dist/index.js +181 -16
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/server-factory.ts","../src/version.ts","../src/api-client.ts","../src/prompts/registry.ts","../src/resources/registry.ts","../src/registry.ts","../src/prompts/deploy-prompts.ts","../src/lib/shape.ts","../src/resources/hoststack-resources.ts","../src/tools/activity-log.ts","../src/lib/respond.ts","../src/tools/alerts.ts","../src/tools/cron.ts","../src/tools/databases.ts","../src/tools/machines.ts","../src/tools/deploys.ts","../src/tools/dns-records.ts","../src/tools/domains.ts","../src/tools/env-vars.ts","../src/tools/environments.ts","../src/tools/analytics.ts","../src/lib/format.ts","../src/tools/errors.ts","../src/tools/github.ts","../src/tools/issue-reports.ts","../src/tools/meta.ts","../src/tools/notifications.ts","../src/tools/projects.ts","../src/tools/dev-tasks.ts","../src/tools/resource-links.ts","../src/tools/services.ts","../src/lib/app-templates.ts","../src/tools/uptime.ts","../src/tools/volumes.ts"],"sourcesContent":["import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { HostStack } from '@hoststack.dev/sdk';\n\nimport { ApiClient } from './api-client.js';\nimport { attachPrompts } from './prompts/registry.js';\nimport { attachResources } from './resources/registry.js';\nimport { attachTools, type ToolCallSink, type ToolContext } from './registry.js';\nimport { MCP_VERSION } from './version.js';\n\nexport type { ToolCallEvent, ToolCallSink } from './registry.js';\n\n// Importing each tool/prompt/resource module runs its top-level define*()\n// calls and populates the shared registries. ESM caches modules, so each\n// registry is built exactly once per process.\nimport './prompts/deploy-prompts.js';\nimport './resources/hoststack-resources.js';\nimport './tools/activity-log.js';\nimport './tools/alerts.js';\nimport './tools/cron.js';\nimport './tools/databases.js';\nimport './tools/deploys.js';\nimport './tools/dns-records.js';\nimport './tools/domains.js';\nimport './tools/env-vars.js';\nimport './tools/environments.js';\nimport './tools/analytics.js';\nimport './tools/errors.js';\nimport './tools/github.js';\nimport './tools/issue-reports.js';\nimport './tools/machines.js';\nimport './tools/meta.js';\nimport './tools/notifications.js';\nimport './tools/projects.js';\nimport './tools/dev-tasks.js';\nimport './tools/resource-links.js';\nimport './tools/services.js';\nimport './tools/uptime.js';\nimport './tools/volumes.js';\n\nexport interface CreateServerOptions {\n\t/** HostStack API key (hs_live_… or hs_test_…). Required. */\n\tapiKey: string;\n\t/** Base URL for the HostStack API. Defaults to https://hoststack.dev */\n\tbaseUrl?: string;\n\t/**\n\t * Server identity advertised in the MCP `initialize` response. Defaults\n\t * to `{ name: \"hoststack\", version: PACKAGE_VERSION }`.\n\t */\n\tserverInfo?: { name: string; version: string };\n\t/**\n\t * Optional telemetry sink invoked once per tool call (success or failure).\n\t * Runs on the microtask queue; sink errors are swallowed so tool execution\n\t * never blocks on telemetry.\n\t */\n\tonToolCall?: ToolCallSink;\n}\n\nexport const PACKAGE_NAME = 'hoststack';\n// Injected from package.json at build time (see tsup.config.ts `define`) so the\n// MCP `initialize` handshake advertises the real published version.\nexport const PACKAGE_VERSION = MCP_VERSION;\n\ninterface MeResponse {\n\tuser: { id: number };\n\tteam?: { id: number };\n}\n\n/**\n * Build a fully-wired MCP server. Used by both the stdio CLI entry-point and\n * the HTTP transport mounted by the API server. A new server (and its\n * underlying SDK + ApiClient) is created per call, so the HTTP transport can\n * scope each connection to a single API key without cross-contamination.\n */\nexport function createMcpServer(options: CreateServerOptions): McpServer {\n\tconst baseUrl = (options.baseUrl ?? 'https://hoststack.dev').replace(/\\/$/, '');\n\tconst hoststack = new HostStack({ apiKey: options.apiKey, baseUrl });\n\tconst api = new ApiClient(options.apiKey, baseUrl);\n\n\tconst server = new McpServer({\n\t\tname: options.serverInfo?.name ?? PACKAGE_NAME,\n\t\tversion: options.serverInfo?.version ?? PACKAGE_VERSION,\n\t});\n\n\t// Cache the team lookup. The first tool call pays one /auth/me round-trip;\n\t// every subsequent call reuses the cached promise. We store the in-flight\n\t// promise (not the resolved value) so concurrent first-calls don't fan out\n\t// into duplicate /auth/me requests.\n\tlet teamIdPromise: Promise<number> | null = null;\n\tconst resolveTeamId = (): Promise<number> => {\n\t\tif (!teamIdPromise) {\n\t\t\tteamIdPromise = api.get<MeResponse>('/api/auth/me').then((me) => {\n\t\t\t\tif (!me.team?.id) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t'No team bound to API key. Generate a team-scoped key in the dashboard.',\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn me.team.id;\n\t\t\t});\n\t\t\t// If the lookup fails, clear the cache so a retry can succeed once\n\t\t\t// the user fixes their key. Without this, every subsequent tool\n\t\t\t// call would re-throw the same cached rejection.\n\t\t\tteamIdPromise.catch(() => {\n\t\t\t\tteamIdPromise = null;\n\t\t\t});\n\t\t}\n\t\treturn teamIdPromise;\n\t};\n\n\tconst ctx: ToolContext = { hoststack, api, resolveTeamId };\n\tattachTools(server, ctx, options.onToolCall);\n\tattachPrompts(server, ctx);\n\tattachResources(server, ctx);\n\n\treturn server;\n}\n","/**\n * Package version, injected at build time by tsup's `define`\n * (`__MCP_VERSION__` ← package.json `version`). The `declare` keeps the source\n * typecheckable without a build; the `?? '0.0.0-dev'` fallback keeps the\n * un-bundled source (tests, ts-node) working when the define is absent.\n */\ndeclare const __MCP_VERSION__: string | undefined;\n\nexport const MCP_VERSION: string =\n\ttypeof __MCP_VERSION__ === 'string' ? __MCP_VERSION__ : '0.0.0-dev';\n\n/** User-Agent sent on every MCP-originated request, e.g. `hoststack-mcp/0.13.1`. */\nexport const USER_AGENT = `hoststack-mcp/${MCP_VERSION}`;\n","import { USER_AGENT } from './version.js';\n\n/**\n * Lightweight API client for HostStack endpoints not yet covered by the SDK\n * (logs snapshots, activity log, runtime metrics, /me lookup). Uses the same\n * auth and base URL as the SDK instance.\n */\nexport class ApiClient {\n\tconstructor(\n\t\tprivate readonly apiKey: string,\n\t\tprivate readonly baseUrl: string,\n\t) {}\n\n\tprivate get headers(): Record<string, string> {\n\t\treturn {\n\t\t\tAuthorization: `Bearer ${this.apiKey}`,\n\t\t\t'Content-Type': 'application/json',\n\t\t\t'User-Agent': USER_AGENT,\n\t\t};\n\t}\n\n\tasync get<T>(path: string, params?: Record<string, string | number | undefined>): Promise<T> {\n\t\tconst url = new URL(`${this.baseUrl}${path}`);\n\t\tif (params) {\n\t\t\tfor (const [key, value] of Object.entries(params)) {\n\t\t\t\tif (value !== undefined) url.searchParams.set(key, String(value));\n\t\t\t}\n\t\t}\n\t\tconst res = await fetch(url.toString(), { headers: this.headers });\n\t\treturn this.handle<T>(res);\n\t}\n\n\tasync post<T>(path: string, body?: unknown): Promise<T> {\n\t\tconst init: RequestInit = {\n\t\t\tmethod: 'POST',\n\t\t\theaders: this.headers,\n\t\t};\n\t\tif (body !== undefined) init.body = JSON.stringify(body);\n\t\tconst res = await fetch(`${this.baseUrl}${path}`, init);\n\t\treturn this.handle<T>(res);\n\t}\n\n\tasync patch<T>(path: string, body: unknown): Promise<T> {\n\t\tconst res = await fetch(`${this.baseUrl}${path}`, {\n\t\t\tmethod: 'PATCH',\n\t\t\theaders: this.headers,\n\t\t\tbody: JSON.stringify(body),\n\t\t});\n\t\treturn this.handle<T>(res);\n\t}\n\n\tasync put<T>(path: string, body: unknown): Promise<T> {\n\t\tconst res = await fetch(`${this.baseUrl}${path}`, {\n\t\t\tmethod: 'PUT',\n\t\t\theaders: this.headers,\n\t\t\tbody: JSON.stringify(body),\n\t\t});\n\t\treturn this.handle<T>(res);\n\t}\n\n\tasync delete<T>(path: string): Promise<T> {\n\t\tconst res = await fetch(`${this.baseUrl}${path}`, {\n\t\t\tmethod: 'DELETE',\n\t\t\theaders: this.headers,\n\t\t});\n\t\treturn this.handle<T>(res);\n\t}\n\n\tprivate async handle<T>(res: Response): Promise<T> {\n\t\tif (!res.ok) {\n\t\t\tconst body = (await res.json().catch(() => ({ error: res.statusText }))) as unknown;\n\t\t\tthrow new Error(formatApiError(body, res.status));\n\t\t}\n\t\tif (res.status === 204) {\n\t\t\treturn undefined as T;\n\t\t}\n\t\treturn res.json() as Promise<T>;\n\t}\n}\n\n/**\n * Turn an API error body into something an operator can act on.\n *\n * The service layer returns `{ error: \"<string>\" }`, but a request rejected by\n * `zValidator` returns `{ success: false, error: <ZodError> }` — an OBJECT.\n * The old code typed `error` as `string | undefined` and passed it straight to\n * `new Error(...)`, which stringified the ZodError to the literal text\n * `[object Object]`. Every schema rejection therefore surfaced with no\n * indication of which field was wrong or why: `create_volume({ size_gb: 2 })`\n * reported `[object Object]` when the real answer was \"size_gb must be at\n * least 10\". Flatten the issues into `field: message` instead.\n */\nexport function formatApiError(body: unknown, status: number): string {\n\tconst fallback = `API error: ${status}`;\n\tif (!body || typeof body !== 'object') return fallback;\n\tconst err = (body as { error?: unknown }).error;\n\n\t// A string error is the service layer's `{ error: \"...\" }` shape. Blank or\n\t// whitespace-only carries no information, so fall through to the status\n\t// rather than quoting an empty string back at the caller.\n\tif (typeof err === 'string') return err.trim() ? err : fallback;\n\n\t// Zod rejection: `issues` is either on the error object or (older shapes)\n\t// the error IS the issue array.\n\tconst issues = Array.isArray(err)\n\t\t? err\n\t\t: err && typeof err === 'object' && Array.isArray((err as { issues?: unknown }).issues)\n\t\t\t? (err as { issues: unknown[] }).issues\n\t\t\t: null;\n\n\tif (issues && issues.length > 0) {\n\t\tconst rendered = issues\n\t\t\t.map((issue) => {\n\t\t\t\tif (!issue || typeof issue !== 'object') return String(issue);\n\t\t\t\tconst { path, message } = issue as { path?: unknown; message?: unknown };\n\t\t\t\tconst field = Array.isArray(path) && path.length > 0 ? path.join('.') : null;\n\t\t\t\tconst text = typeof message === 'string' ? message : 'invalid value';\n\t\t\t\treturn field ? `${field}: ${text}` : text;\n\t\t\t})\n\t\t\t.join('; ');\n\t\treturn `Invalid request — ${rendered}`;\n\t}\n\n\t// Anything else structured: show it rather than collapsing it to\n\t// `[object Object]`.\n\tif (err !== undefined) {\n\t\ttry {\n\t\t\treturn `${fallback}: ${JSON.stringify(err)}`;\n\t\t} catch {\n\t\t\treturn fallback;\n\t\t}\n\t}\n\treturn fallback;\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { GetPromptResult } from '@modelcontextprotocol/sdk/types.js';\nimport type { HostStack } from '@hoststack.dev/sdk';\nimport type { ZodRawShape, z } from 'zod';\n\nimport type { ApiClient } from '../api-client.js';\n\nexport interface PromptContext {\n\thoststack: HostStack;\n\tapi: ApiClient;\n\tresolveTeamId(): Promise<number>;\n}\n\ninterface InternalPromptDefinition {\n\tname: string;\n\tdescription: string;\n\targs: ZodRawShape;\n\thandler: (\n\t\targs: Record<string, string | undefined>,\n\t\tctx: PromptContext,\n\t) => Promise<GetPromptResult>;\n}\n\nexport type PromptDefinition = Readonly<InternalPromptDefinition>;\n\nconst prompts: InternalPromptDefinition[] = [];\n\n/**\n * Register a single prompt. The generic over `TArgs` lets the handler receive\n * precisely-typed args while the registry stores the widened shape — same\n * trick as `defineTool` to keep the compiler's instantiation budget in check.\n */\nexport function definePrompt<TArgs extends ZodRawShape>(def: {\n\tname: string;\n\tdescription: string;\n\targs: TArgs;\n\thandler: (args: z.infer<z.ZodObject<TArgs>>, ctx: PromptContext) => Promise<GetPromptResult>;\n}): void {\n\tprompts.push({\n\t\tname: def.name,\n\t\tdescription: def.description,\n\t\targs: def.args,\n\t\thandler: def.handler as InternalPromptDefinition['handler'],\n\t});\n}\n\nexport function listPromptDefinitions(): ReadonlyArray<PromptDefinition> {\n\treturn prompts;\n}\n\n/** Iterate the registry and wire each prompt into the MCP server. */\nexport function attachPrompts(server: McpServer, ctx: PromptContext): void {\n\ttype PromptMethod = (\n\t\tname: string,\n\t\tdescription: string,\n\t\targsSchema: ZodRawShape,\n\t\tcb: (args: Record<string, string | undefined>) => Promise<GetPromptResult>,\n\t) => unknown;\n\n\tconst register = server.prompt.bind(server) as unknown as PromptMethod;\n\n\tfor (const def of prompts) {\n\t\tregister(def.name, def.description, def.args, (args) => def.handler(args, ctx));\n\t}\n}\n\n/**\n * Helper to build a single user-role text message — the most common shape for\n * these prompts (we're guiding the agent, not modeling assistant responses).\n */\nexport function userMessage(text: string): GetPromptResult['messages'][number] {\n\treturn { role: 'user', content: { type: 'text', text } };\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { ReadResourceResult } from '@modelcontextprotocol/sdk/types.js';\nimport type { HostStack } from '@hoststack.dev/sdk';\n\nimport type { ApiClient } from '../api-client.js';\n\ninterface ResourceContext {\n\thoststack: HostStack;\n\tapi: ApiClient;\n\tresolveTeamId(): Promise<number>;\n}\n\ninterface StaticResourceDef {\n\tkind: 'static';\n\tname: string;\n\turi: string;\n\tdescription: string;\n\tmimeType?: string;\n\tread: (uri: URL, ctx: ResourceContext) => Promise<ReadResourceResult>;\n}\n\nexport type ResourceDefinition = Readonly<StaticResourceDef>;\n\nconst resources: StaticResourceDef[] = [];\n\nexport function defineResource(def: StaticResourceDef): void {\n\tresources.push(def);\n}\n\nexport function listResourceDefinitions(): ReadonlyArray<ResourceDefinition> {\n\treturn resources;\n}\n\nexport function attachResources(server: McpServer, ctx: ResourceContext): void {\n\tfor (const def of resources) {\n\t\tserver.resource(\n\t\t\tdef.name,\n\t\t\tdef.uri,\n\t\t\t{\n\t\t\t\tdescription: def.description,\n\t\t\t\t...(def.mimeType ? { mimeType: def.mimeType } : {}),\n\t\t\t},\n\t\t\t(uri) => def.read(uri, ctx),\n\t\t);\n\t}\n}\n\n/**\n * Pack a JSON value into the resource read-result envelope. The MCP spec\n * requires `text` for application/json resources — we serialise here so each\n * resource handler stays a one-liner.\n */\nexport function jsonResource(uri: URL, data: unknown): ReadResourceResult {\n\treturn {\n\t\tcontents: [\n\t\t\t{\n\t\t\t\turi: uri.toString(),\n\t\t\t\tmimeType: 'application/json',\n\t\t\t\ttext: JSON.stringify(data, null, 2),\n\t\t\t},\n\t\t],\n\t};\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';\nimport type { HostStack } from '@hoststack.dev/sdk';\nimport type { ZodRawShape, z } from 'zod';\n\nimport type { ApiClient } from './api-client.js';\n\n/**\n * Every category a tool may declare, as a runtime value.\n *\n * The type below is DERIVED from this rather than written beside it, because\n * the two had already drifted: `output-contract.test.ts` kept its own hand-copy\n * of this list, 'support' was added to the type and not to the copy, and the\n * test only failed on the runs where a sibling test file happened to have\n * imported `tools/issue-reports.ts` into the same process first. One list, one\n * source of truth, and the test reads it.\n */\nexport const TOOL_CATEGORIES = [\n\t'projects',\n\t'services',\n\t'deploys',\n\t'databases',\n\t'domains',\n\t'dns',\n\t'volumes',\n\t'machines',\n\t'env-vars',\n\t'environments',\n\t'resource-links',\n\t'cron',\n\t'dev-tasks',\n\t'logs',\n\t'activity-log',\n\t'alerts',\n\t'errors',\n\t'uptime',\n\t'analytics',\n\t'github',\n\t'support',\n\t'meta',\n] as const;\n\nexport type ToolCategory = (typeof TOOL_CATEGORIES)[number];\n\nexport interface ToolContext {\n\thoststack: HostStack;\n\tapi: ApiClient;\n\t/**\n\t * Lazy resolver for the team ID bound to the API key. The agent never has\n\t * to pass `teamId` to a tool — every SDK route is team-scoped, so the MCP\n\t * looks it up once via `/api/auth/me` and caches it for the rest of the\n\t * server's lifetime. Throws if the API key is invalid or unbound.\n\t */\n\tresolveTeamId(): Promise<number>;\n}\n\ninterface InternalToolDefinition {\n\tname: string;\n\tcategory: ToolCategory;\n\tdescription: string;\n\tinput: ZodRawShape;\n\thandler: (args: Record<string, unknown>, ctx: ToolContext) => Promise<CallToolResult>;\n}\n\nexport type ToolDefinition = Readonly<InternalToolDefinition>;\n\nconst tools: InternalToolDefinition[] = [];\n\n/**\n * Register a single tool definition. The generic over `TInput` lets the\n * handler receive precisely-typed arguments while the registry stores the\n * widened shape.\n */\nexport function defineTool<TInput extends ZodRawShape>(def: {\n\tname: string;\n\tcategory: ToolCategory;\n\tdescription: string;\n\tinput: TInput;\n\thandler: (args: z.infer<z.ZodObject<TInput>>, ctx: ToolContext) => Promise<CallToolResult>;\n}): void {\n\ttools.push({\n\t\tname: def.name,\n\t\tcategory: def.category,\n\t\tdescription: def.description,\n\t\tinput: def.input,\n\t\thandler: def.handler as InternalToolDefinition['handler'],\n\t});\n}\n\n/** Snapshot the currently-registered tool definitions (for tests / docs). */\nexport function listToolDefinitions(): ReadonlyArray<ToolDefinition> {\n\treturn tools;\n}\n\n/**\n * Telemetry event emitted once per tool invocation. The MCP package never\n * persists these — it just hands them to the optional `onToolCall` sink so\n * the host (HTTP transport, CLI, tests) can decide where they go.\n *\n * Privacy: `inputHash` is a hash over the JSON-stringified args; we never\n * expose the args themselves so PII from tool inputs (env-var values, domain\n * names) never reaches a sink.\n */\nexport interface ToolCallEvent {\n\ttool: string;\n\tdurationMs: number;\n\tok: boolean;\n\terrorCode: string | null;\n\tinputHash: string | null;\n\tstartedAt: Date;\n}\n\nexport type ToolCallSink = (event: ToolCallEvent) => void | Promise<void>;\n\n/** Iterate the registry and wire each tool into the MCP server. */\nexport function attachTools(server: McpServer, ctx: ToolContext, sink?: ToolCallSink): void {\n\ttype ToolMethod = (\n\t\tname: string,\n\t\tdescription: string,\n\t\tschema: ZodRawShape,\n\t\tcb: (args: Record<string, unknown>) => Promise<CallToolResult>,\n\t) => unknown;\n\n\tconst register = server.tool.bind(server) as unknown as ToolMethod;\n\n\tfor (const def of tools) {\n\t\tregister(def.name, def.description, def.input, async (args) => {\n\t\t\tconst startedAt = new Date();\n\t\t\tconst start = performance.now();\n\t\t\tlet ok = true;\n\t\t\tlet errorCode: string | null = null;\n\t\t\ttry {\n\t\t\t\tconst result = await def.handler(args, ctx);\n\t\t\t\tif (result.isError) {\n\t\t\t\t\tok = false;\n\t\t\t\t\terrorCode = 'tool_error';\n\t\t\t\t}\n\t\t\t\treturn result;\n\t\t\t} catch (err) {\n\t\t\t\tok = false;\n\t\t\t\terrorCode =\n\t\t\t\t\terr instanceof Error ? (err.name || 'Error').slice(0, 64) : 'unknown_error';\n\t\t\t\tthrow err;\n\t\t\t} finally {\n\t\t\t\tif (sink) {\n\t\t\t\t\tconst durationMs = Math.round(performance.now() - start);\n\t\t\t\t\tconst event: ToolCallEvent = {\n\t\t\t\t\t\ttool: def.name,\n\t\t\t\t\t\tdurationMs,\n\t\t\t\t\t\tok,\n\t\t\t\t\t\terrorCode,\n\t\t\t\t\t\tinputHash: hashArgs(args),\n\t\t\t\t\t\tstartedAt,\n\t\t\t\t\t};\n\t\t\t\t\tPromise.resolve()\n\t\t\t\t\t\t.then(() => sink(event))\n\t\t\t\t\t\t.catch(() => undefined);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n}\n\nfunction hashArgs(args: Record<string, unknown>): string | null {\n\ttry {\n\t\tconst json = JSON.stringify(args);\n\t\tconst bunGlobal = (globalThis as { Bun?: { hash?: (data: string) => bigint } }).Bun;\n\t\tif (bunGlobal?.hash) {\n\t\t\treturn bunGlobal.hash(json).toString(16);\n\t\t}\n\t\treturn `len:${json.length}`;\n\t} catch {\n\t\treturn null;\n\t}\n}\n","import { z } from 'zod';\n\nimport { definePrompt, userMessage } from './registry.js';\n\ndefinePrompt({\n\tname: 'diagnose_failed_deploy',\n\tdescription:\n\t\t'Walk the agent through diagnosing a failed deploy: pull the latest deploys for the named service, find the most recent failure, fetch its build/deploy logs, and propose a fix. Stops at suggestion — does not retrigger automatically.',\n\targs: {\n\t\tservice_id: z.string().describe('Service publicId (e.g. svc_abc123).'),\n\t},\n\thandler: async ({ service_id }) => {\n\t\tconst text = `Goal: diagnose the most recent failed deploy on service ${service_id} and propose a fix.\n\nPlan (use these tools in order):\n1. \\`list_deploys({ service_id: \"${service_id}\" })\\` — pull recent deploys, newest first. Identify the most recent deploy with status \\`failed\\` (or \\`cancelled\\` if the user wants to investigate that too). Capture its publicId, branch, and commitHash.\n2. \\`get_deploy_logs({ service_id: \"${service_id}\", deploy_id: \"<dpl_…>\" })\\` — read the full build output. Scan for: stack traces, \"ERROR\" / \"error:\" lines, exit codes, missing env vars, OOM-killed signals, network failures pulling dependencies.\n3. \\`get_service({ service_id: \"${service_id}\" })\\` — confirm the service's runtime, plan, and whether autoDeploy is on. Plan tier matters because OOMs at small tiers point at \\`maxMemoryMb\\`.\n4. (Optional) \\`list_env_vars({ service_id: \"${service_id}\" })\\` — only if the build error mentions a missing variable. Don't fetch otherwise; values are masked anyway.\n\nThen write a short diagnosis for the user with three sections:\n- **Root cause** — one sentence pointing at the actual failure line.\n- **Evidence** — 3–6 line excerpt from the logs that proves the diagnosis.\n- **Suggested fix** — concrete next steps, including which tool to call (e.g. \"set MISSING_KEY with set_env_var, then trigger_deploy\"). Do NOT call trigger_deploy automatically; the user decides.\n\nIf the deploy logs are empty or only contain queued/pending markers, surface that — the build never started, which is its own class of bug (likely a builder-pool or quota issue).`;\n\n\t\treturn { messages: [userMessage(text)] };\n\t},\n});\n\ndefinePrompt({\n\tname: 'deploy_status_check',\n\tdescription:\n\t\t\"Quick orientation prompt: who am I, what's running, and is anything currently deploying? Useful at the start of a session.\",\n\targs: {},\n\thandler: async () => {\n\t\tconst text = `Goal: produce a 30-second status check on this HostStack team.\n\nPlan:\n1. \\`get_me()\\` — confirm which user + team the API key belongs to.\n2. \\`list_services()\\` — every service with current status (running, suspended, building, deploying, failed). Note any service in a non-running state.\n3. For each service whose status is \\`building\\` or \\`deploying\\`, call \\`list_deploys({ service_id })\\` and surface the latest deploy's commitMessage + author.\n4. \\`list_activity_log({ per_page: 10 })\\` — last 10 audit events to spot recent changes (deploys triggered, env vars edited, services restarted).\n\nWrite a status report shaped like:\n\n> **Team**: <name>\n> **Services**: <n> total — <breakdown by status>\n> **In flight**: <list of building/deploying services with commit info, or \"none\">\n> **Recent activity**: <3-5 most relevant events from the audit log, newest first>\n\nKeep it tight (under 200 words). The user can drill in with follow-up tools.`;\n\n\t\treturn { messages: [userMessage(text)] };\n\t},\n});\n\ndefinePrompt({\n\tname: 'rotate_secret',\n\tdescription:\n\t\t'Guide the agent through rotating an env-var secret on a service. Lists current vars to confirm the target key exists, asks the user for the new value, sets it, and offers to trigger a deploy.',\n\targs: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t\tkey: z.string().describe('Env-var key to rotate (e.g. DATABASE_URL).'),\n\t},\n\thandler: async ({ service_id, key }) => {\n\t\tconst text = `Goal: rotate the secret \\`${key}\\` on service ${service_id}.\n\nPlan:\n1. \\`list_env_vars({ service_id: \"${service_id}\" })\\` — confirm \\`${key}\\` exists. Note its \\`isSecret\\` flag (it should be true; if not, flag this — you're about to mark it secret on rotation, which is a behaviour change).\n2. **Ask the user for the new value before calling any write tool.** The agent must NOT invent or generate the secret; it has to come from the user's clipboard / vault. Phrase the ask explicitly: \"Paste the new value for ${key} (it will be encrypted at rest and masked everywhere except the running container).\"\n3. Once the user provides the value:\n - \\`set_env_var({ service_id: \"${service_id}\", key: \"${key}\", value: \"<new value>\", is_secret: true })\\` — upserts by key.\n4. After the write succeeds, ask the user whether to redeploy (so the new value lands in the running container). If yes:\n - \\`trigger_deploy({ service_id: \"${service_id}\" })\\` — kicks off a build with the rotated secret.\n - \\`get_deploy({ service_id: \"${service_id}\", deploy_id: \"<dpl_…>\" })\\` — poll until status is \\`live\\` or \\`failed\\`. On failure, hand off to \\`diagnose_failed_deploy\\`.\n\nSafety rails:\n- Never echo the new value back in your reply; the user should verify it from their own source of truth, not from your context window.\n- If the user says \"generate a new one\", suggest \\`openssl rand -hex 32\\` (or similar) for them to run locally and paste in. Do NOT generate it for them — secrets that pass through an LLM context are no longer secrets.\n- If \\`set_env_var\\` returned \\`action: \"created\"\\` instead of \\`\"updated\"\\`, the key didn't exist before. Pause and confirm with the user — they may have typo'd the key name.`;\n\n\t\treturn { messages: [userMessage(text)] };\n\t},\n});\n","/**\n * Per-resource shape pickers. Each function takes a raw API/SDK response and\n * returns the agent-friendly subset: drops internal foreign keys (teamId,\n * userId, ...) and null-valued columns, leaves ISO-8601 timestamps as-is.\n *\n * Pickers must be permissive about input shape — different SDK methods may\n * return slightly different fields (e.g. list vs get) and the API may grow\n * fields over time. We never throw on missing fields.\n */\n\nconst INTERNAL_FIELDS = new Set([\n\t'teamId',\n\t'team_id',\n\t'userId',\n\t'user_id',\n\t'accountId',\n\t'account_id',\n\t'tenantId',\n\t'tenant_id',\n\t'createdById',\n\t'updatedById',\n]);\n\ntype Obj = Record<string, unknown>;\n\nfunction isObject(value: unknown): value is Obj {\n\treturn value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\nfunction dropNullsAndInternals(obj: Obj): Obj {\n\tconst out: Obj = {};\n\tfor (const [key, value] of Object.entries(obj)) {\n\t\tif (INTERNAL_FIELDS.has(key)) continue;\n\t\tif (value === null || value === undefined) continue;\n\t\tout[key] = value;\n\t}\n\treturn out;\n}\n\n/** Generic shape — drop internals, drop nulls, keep everything else. */\nexport function shape(value: unknown): unknown {\n\tif (Array.isArray(value)) return value.map(shape);\n\tif (isObject(value)) return dropNullsAndInternals(value);\n\treturn value;\n}\n\nfunction shapeAll<T>(arr: unknown, shaper: (item: unknown) => T): T[] {\n\tif (!Array.isArray(arr)) return [];\n\treturn arr.map(shaper);\n}\n\n/**\n * Unwrap a HostStack list response. The API returns shapes like\n * `{ projects: [...] }`, `{ services: [...] }`, etc. — the key varies by\n * resource. Pass the key explicitly so the wrapper stays type-loose.\n */\nexport function shapeList<T>(\n\tresponse: unknown,\n\tkey: string,\n\tshaper: (item: unknown) => T,\n): { items: T[] } {\n\tif (!isObject(response)) return { items: [] };\n\treturn { items: shapeAll(response[key], shaper) };\n}\n\n// ──────────────────────────────────────────────────────────────────────\n// Per-resource pickers — typed convenience wrappers around shape().\n// All return a plain Obj (never null), keeping the registry handlers simple.\n// ──────────────────────────────────────────────────────────────────────\n\nexport function shapeProject(value: unknown): Obj {\n\treturn isObject(value) ? dropNullsAndInternals(value) : {};\n}\n\nexport function shapeService(value: unknown): Obj {\n\treturn isObject(value) ? dropNullsAndInternals(value) : {};\n}\n\nexport function shapeDeploy(value: unknown): Obj {\n\treturn isObject(value) ? dropNullsAndInternals(value) : {};\n}\n\nexport function shapeDatabase(value: unknown): Obj {\n\treturn isObject(value) ? dropNullsAndInternals(value) : {};\n}\n\nexport function shapeDomain(value: unknown): Obj {\n\treturn isObject(value) ? dropNullsAndInternals(value) : {};\n}\n\nexport function shapeEnvVar(value: unknown): Obj {\n\treturn isObject(value) ? dropNullsAndInternals(value) : {};\n}\n\nexport function shapeCronExecution(value: unknown): Obj {\n\treturn isObject(value) ? dropNullsAndInternals(value) : {};\n}\n\nexport function shapeActivity(value: unknown): Obj {\n\treturn isObject(value) ? dropNullsAndInternals(value) : {};\n}\n\nexport function shapeUser(value: unknown): Obj {\n\treturn isObject(value) ? dropNullsAndInternals(value) : {};\n}\n\nexport function shapeTeam(value: unknown): Obj {\n\treturn isObject(value) ? dropNullsAndInternals(value) : {};\n}\n\nexport function shapeVolume(value: unknown): Obj {\n\treturn isObject(value) ? dropNullsAndInternals(value) : {};\n}\n","import { shapeProject, shapeService, shapeTeam, shapeUser } from '../lib/shape.js';\nimport { defineResource, jsonResource } from './registry.js';\n\ninterface MeResponse {\n\tuser: unknown;\n\tteam?: unknown;\n}\n\ndefineResource({\n\tkind: 'static',\n\tname: 'team',\n\turi: 'hoststack://team',\n\tdescription:\n\t\t'Authenticated team identity + a quick orientation snapshot. Includes the team record, the user behind the API key, and counts of projects and services so an agent can decide whether to fan out into list_projects / list_services without an extra round-trip.',\n\tmimeType: 'application/json',\n\tread: async (uri, { hoststack, api, resolveTeamId }) => {\n\t\tconst teamId = await resolveTeamId();\n\n\t\t// Resource handlers must not throw on transient downstream failures —\n\t\t// clients call resources/read for orientation and a network blip\n\t\t// shouldn't poison the entire session. Tolerate partial failure and\n\t\t// surface what we can.\n\t\tconst [me, projectsResult, servicesResult] = await Promise.all([\n\t\t\tapi.get<MeResponse>('/api/auth/me').catch(() => null),\n\t\t\thoststack.projects.list(teamId).catch(() => ({ projects: [] as unknown[] })),\n\t\t\thoststack.services.list(teamId).catch(() => ({ services: [] as unknown[] })),\n\t\t]);\n\n\t\tconst projects = (projectsResult.projects as unknown[]).slice(0, 10).map(shapeProject);\n\t\tconst services = (servicesResult.services as unknown[]).slice(0, 10).map(shapeService);\n\n\t\treturn jsonResource(uri, {\n\t\t\tuser: me?.user ? shapeUser(me.user) : null,\n\t\t\tteam: me?.team ? shapeTeam(me.team) : null,\n\t\t\tproject_count: (projectsResult.projects as unknown[]).length,\n\t\t\tservice_count: (servicesResult.services as unknown[]).length,\n\t\t\t// First 10 of each so the resource stays small while still useful\n\t\t\t// for orientation. Use list_projects / list_services for the full set.\n\t\t\tprojects_preview: projects,\n\t\t\tservices_preview: services,\n\t\t});\n\t},\n});\n","import { z } from 'zod';\n\nimport { defineTool } from '../registry.js';\nimport { respond } from '../lib/respond.js';\nimport { shape, shapeActivity } from '../lib/shape.js';\n\ninterface ActivityLogResponse {\n\tdata?: unknown[];\n\t// Older API style: pagination meta as a single nested object. Some\n\t// callers still serve this shape, so keep accepting it.\n\tmeta?: unknown;\n\t// Current API style: pagination meta fields are spread flat on the\n\t// response root (`{ data, page, perPage, total, totalPages }`).\n\tpage?: number;\n\tperPage?: number;\n\ttotal?: number;\n\ttotalPages?: number;\n}\n\ndefineTool({\n\tname: 'list_activity_log',\n\tcategory: 'activity-log',\n\tdescription: [\n\t\t'List the team activity audit log: who did what, when. Backed by /api/activity-log/:teamId.',\n\t\t'',\n\t\t'When to use: investigate why a service was changed unexpectedly (\"who deleted that env var?\"), correlate a deploy with a user, or pull recent admin events for a status update.',\n\t\t'',\n\t\t'Inputs (all optional):',\n\t\t' - page: 1-based page index (default 1).',\n\t\t' - per_page: items per page (default 25, hard cap 100).',\n\t\t' - action: filter to a specific action (e.g. \"service.created\", \"deploy.triggered\").',\n\t\t' - resource_type: filter by resource type (e.g. \"service\", \"deploy\", \"domain\").',\n\t\t' - user_id: filter by acting user numeric ID.',\n\t\t' - since/until: ISO-8601 timestamp OR a relative offset like \"-15m\" / \"-2h\" / \"-7d\" — narrow to a time window (handy for correlating a deploy with a log spike).',\n\t\t'',\n\t\t'Returns: { items: ActivityLogEntry[], meta: { page, perPage, total, totalPages } } — each entry has id, action, resourceType, resourceId, actorEmail, ipAddress, createdAt, and a context payload. Use meta.totalPages to tell whether you have more.',\n\t\t'',\n\t\t'Example: list_activity_log({ resource_type: \"deploy\", per_page: 10 }) → { items: [{ action: \"deploy.triggered\", actorEmail: \"ada@…\", … }], meta: { total: 47, page: 1 } }',\n\t].join('\\n'),\n\tinput: {\n\t\tpage: z.number().int().positive().optional().describe('Page index, 1-based.'),\n\t\tper_page: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.positive()\n\t\t\t.max(100)\n\t\t\t.optional()\n\t\t\t.describe('Items per page, hard cap 100.'),\n\t\taction: z.string().optional().describe('Action filter, e.g. \"service.created\".'),\n\t\tresource_type: z.string().optional().describe('Resource type filter.'),\n\t\tuser_id: z.number().int().positive().optional().describe('Numeric acting-user filter.'),\n\t\tsince: z\n\t\t\t.string()\n\t\t\t.optional()\n\t\t\t.describe('ISO-8601 timestamp or relative offset (e.g. \"-15m\", \"-2h\", \"-7d\").'),\n\t\tuntil: z.string().optional().describe('ISO-8601 timestamp or relative offset upper bound.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst params: Record<string, string | number | undefined> = {};\n\t\tif (args.page !== undefined) params['page'] = String(args.page);\n\t\tif (args.per_page !== undefined) params['perPage'] = String(args.per_page);\n\t\tif (args.action !== undefined) params['action'] = args.action;\n\t\tif (args.resource_type !== undefined) params['resourceType'] = args.resource_type;\n\t\tif (args.user_id !== undefined) params['userId'] = String(args.user_id);\n\t\tif (args.since !== undefined) params['since'] = args.since;\n\t\tif (args.until !== undefined) params['until'] = args.until;\n\n\t\tconst response = await ctx.api.get<ActivityLogResponse>(\n\t\t\t`/api/activity-log/${teamId}`,\n\t\t\tparams,\n\t\t);\n\t\tconst items = Array.isArray(response.data) ? response.data.map(shapeActivity) : [];\n\t\tconst data: { items: unknown[]; meta?: unknown } = { items };\n\t\t// The API spreads pagination at the top level (`{ data, page, perPage,\n\t\t// total, totalPages }`). Older variants nested it under `meta`.\n\t\t// Surface a single `meta` object regardless so MCP callers don't have\n\t\t// to learn two shapes.\n\t\tif (response.meta !== undefined) {\n\t\t\tdata.meta = shape(response.meta);\n\t\t} else if (\n\t\t\tresponse.page !== undefined ||\n\t\t\tresponse.perPage !== undefined ||\n\t\t\tresponse.total !== undefined ||\n\t\t\tresponse.totalPages !== undefined\n\t\t) {\n\t\t\tdata.meta = shape({\n\t\t\t\tpage: response.page,\n\t\t\t\tperPage: response.perPage,\n\t\t\t\ttotal: response.total,\n\t\t\t\ttotalPages: response.totalPages,\n\t\t\t});\n\t\t}\n\n\t\treturn respond({\n\t\t\tsummary:\n\t\t\t\titems.length === 0\n\t\t\t\t\t? 'No activity log entries match the given filters.'\n\t\t\t\t\t: `Returned ${items.length} activity log entr${items.length === 1 ? 'y' : 'ies'}.`,\n\t\t\tdata,\n\t\t});\n\t},\n});\n","import type { CallToolResult, ContentBlock } from '@modelcontextprotocol/sdk/types.js';\n\ninterface RespondInput {\n\tsummary: string;\n\tdata?: unknown;\n}\n\nexport const SUMMARY_MAX_LEN = 500;\n\n/**\n * Build an MCP tool response with a 1–3 line plain-text summary, a fenced\n * JSON block for human readability, and a structuredContent object that\n * agents can parse without re-tokenising the JSON text.\n */\nexport function respond({ summary, data }: RespondInput): CallToolResult {\n\tconst trimmed = summary.trim();\n\tif (trimmed.length === 0) {\n\t\tthrow new Error('respond(): summary must be non-empty');\n\t}\n\tif (trimmed.length > SUMMARY_MAX_LEN) {\n\t\tthrow new Error(`respond(): summary too long (${trimmed.length} > ${SUMMARY_MAX_LEN})`);\n\t}\n\n\tconst blocks: ContentBlock[] = [{ type: 'text', text: trimmed }];\n\n\tif (data !== undefined) {\n\t\tblocks.push({\n\t\t\ttype: 'text',\n\t\t\ttext: '```json\\n' + JSON.stringify(data, null, 2) + '\\n```',\n\t\t});\n\t}\n\n\tconst result: CallToolResult = { content: blocks };\n\tif (data !== undefined) {\n\t\tresult.structuredContent = toStructuredContent(data);\n\t}\n\treturn result;\n}\n\nexport function respondError(message: string, data?: unknown): CallToolResult {\n\tconst blocks: ContentBlock[] = [{ type: 'text', text: `Error: ${message}` }];\n\tif (data !== undefined) {\n\t\tblocks.push({\n\t\t\ttype: 'text',\n\t\t\ttext: '```json\\n' + JSON.stringify(data, null, 2) + '\\n```',\n\t\t});\n\t}\n\tconst result: CallToolResult = { content: blocks, isError: true };\n\tif (data !== undefined) {\n\t\tresult.structuredContent = toStructuredContent(data);\n\t}\n\treturn result;\n}\n\nfunction toStructuredContent(data: unknown): Record<string, unknown> {\n\tif (data !== null && typeof data === 'object' && !Array.isArray(data)) {\n\t\treturn data as Record<string, unknown>;\n\t}\n\treturn { result: data };\n}\n","import { z } from 'zod';\n\nimport { defineTool } from '../registry.js';\nimport { respond } from '../lib/respond.js';\nimport { shape } from '../lib/shape.js';\n\ninterface AlertsResponse {\n\talerts: unknown[];\n\taggregated: boolean;\n}\n\ndefineTool({\n\tname: 'list_alerts',\n\tcategory: 'alerts',\n\tdescription: [\n\t\t'List recent alert-shaped events for the team: deploy failures, git auth losses, service health failures, uptime failures (a service stopped answering its public URL), new and regressed error issues (an exception the app reported), auto-restarts, ACME cert failures, resource alerts (high CPU), database backup/restore failures, registrant verification lapses.',\n\t\t'',\n\t\t\"When to use: triage 'what's currently broken or recently broke for this team'. Pairs with list_activity_log for the full audit feed; this tool is the alert-shaped subset. For an `error.issue_new` / `error.issue_regressed` entry, `list_error_issues` and `get_error_issue` carry the stack behind it.\",\n\t\t'',\n\t\t'By default events are AGGREGATED by (action, resourceId) so flapping events collapse to one row with a fire count + first/last timestamps — e.g. \"service.auto_restarted on service 31, 8 times in the last hour, last at 14:22\". Pass aggregate=false to see every raw row.',\n\t\t'',\n\t\t'ACTIVE vs HISTORICAL (v89): each aggregated entry now has an `active` boolean and a `lastResolvedAt` timestamp. `active=true` means the most recent occurrence has NOT been resolved — the alert is still on fire. The default `active=true` filter hides resolved/historical alerts so triage starts with \"what is broken right now\". Pass `active=false` to include resolved entries too (useful for post-mortems). Resolution is automatic for some alert kinds — e.g. `deploy.failed_consecutive` is cleared when the next deploy of the same service goes live.',\n\t\t'',\n\t\t'Database backup_failed entries (v89) include `containerStatus` / `containerHealth` / `containerExitCode` in lastMetadata when the agent could resolve the container at failure time, so you can tell \"Redis is overloaded\" apart from \"Redis is restarting\" without a second tool call.',\n\t\t'',\n\t\t'Deploy.failed_consecutive entries (v89) carry the offending `commitHash` in lastMetadata, and the streak now dedupes per (service, commitHash) — one critical per bad commit, not one every 6 retries.',\n\t\t'',\n\t\t'Inputs (all optional):',\n\t\t' - since: ISO-8601 timestamp OR relative offset like \"-1h\" / \"-2d\". Default: -24h.',\n\t\t' - until: ISO-8601 upper bound (ignored when aggregating — aggregated view always extends to now).',\n\t\t' - limit: max rows (default 100, hard cap 500).',\n\t\t' - aggregate: true (default) collapses by (action, resourceId); false returns raw rows.',\n\t\t' - active: true (default) shows only alerts that are still on fire (resolved_at IS NULL on the most recent row). false includes resolved historical alerts.',\n\t\t'',\n\t\t'Returns: { alerts: Array, aggregated: boolean, activeOnly: boolean }. Each aggregated entry includes action, resourceType, resourceId, severity, count, firstFiredAt, lastFiredAt, lastResolvedAt (nullable), active (boolean), lastMetadata. Each raw entry adds resolvedAt (nullable).',\n\t\t'',\n\t\t\"Example: list_alerts({ since: '-1h' }) → { alerts: [{ action: 'deploy.failed_consecutive', resourceId: 31, severity: 'critical', active: true, count: 3, lastFiredAt: '…', lastResolvedAt: null, lastMetadata: { commitHash: 'abc1234' }, … }] }.\",\n\t].join('\\n'),\n\tinput: {\n\t\tsince: z\n\t\t\t.string()\n\t\t\t.optional()\n\t\t\t.describe('ISO-8601 timestamp or relative offset (e.g. \"-1h\", \"-2d\"). Default: -24h.'),\n\t\tuntil: z\n\t\t\t.string()\n\t\t\t.optional()\n\t\t\t.describe('ISO-8601 upper bound. Only honored when aggregate=false.'),\n\t\tlimit: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.positive()\n\t\t\t.max(500)\n\t\t\t.optional()\n\t\t\t.describe('Max rows (default 100, hard cap 500).'),\n\t\taggregate: z\n\t\t\t.boolean()\n\t\t\t.optional()\n\t\t\t.describe('Collapse by (action, resourceId). Default true.'),\n\t\tactive: z\n\t\t\t.boolean()\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t'Only return alerts still on fire (resolved_at IS NULL on the most recent row). Default true. Pass false to include resolved historical alerts.',\n\t\t\t),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst params: Record<string, string | number | undefined> = {};\n\t\tif (args.since !== undefined) params['since'] = args.since;\n\t\tif (args.until !== undefined) params['until'] = args.until;\n\t\tif (args.limit !== undefined) params['limit'] = String(args.limit);\n\t\t// API expects aggregate=1|0; default behavior on the API side is\n\t\t// aggregate=true. Pass explicit \"0\" only when caller opts out so\n\t\t// we don't ship spurious query params.\n\t\tif (args.aggregate === false) params['aggregate'] = '0';\n\t\t// v89: same convention for active=1|0. Default true on the API\n\t\t// side, so we only ship `active=0` when the caller opts in to\n\t\t// historical entries.\n\t\tif (args.active === false) params['active'] = '0';\n\n\t\tconst response = await ctx.api.get<AlertsResponse>(`/api/alerts/${teamId}`, params);\n\t\tconst items = Array.isArray(response.alerts) ? response.alerts.map(shape) : [];\n\t\tconst aggregated = Boolean(response.aggregated);\n\t\tconst activeOnly = args.active !== false;\n\t\tconst summary =\n\t\t\titems.length === 0\n\t\t\t\t? activeOnly\n\t\t\t\t\t? 'No active alerts in the requested window — everything is operating normally.'\n\t\t\t\t\t: 'No alerts in the requested window (active or resolved).'\n\t\t\t\t: aggregated\n\t\t\t\t\t? `Returned ${items.length} alert group${items.length === 1 ? '' : 's'}${activeOnly ? ' (active only — pass active=false for resolved history)' : ''} (flapping events collapsed).`\n\t\t\t\t\t: `Returned ${items.length} raw alert event${items.length === 1 ? '' : 's'}.`;\n\t\treturn respond({ summary, data: { alerts: items, aggregated, activeOnly } });\n\t},\n});\n","import { z } from 'zod';\n\nimport { defineTool } from '../registry.js';\nimport { respond } from '../lib/respond.js';\nimport { shapeCronExecution, shapeList } from '../lib/shape.js';\n\ndefineTool({\n\tname: 'list_cron_executions',\n\tcategory: 'cron',\n\tdescription: [\n\t\t'List recent execution records for a cron service (newest first).',\n\t\t'',\n\t\t'When to use: investigating whether a scheduled job ran on time, finding the most recent failure, or auditing cron run-times. Only works on services of type \"cron\"; for web services this returns an error.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the cron service.',\n\t\t' - limit (optional): how many executions to fetch (default 50, max enforced server-side).',\n\t\t'',\n\t\t'Returns: { items: CronExecution[] } — id, publicId, status (succeeded|failed|running|queued), startedAt, finishedAt, exitCode, triggeredBy.',\n\t\t'',\n\t\t'Example: list_cron_executions({ service_id: \"svc_cron\" }) → { items: [{ status: \"succeeded\", exitCode: 0, … }, …] }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Cron service publicId.'),\n\t\tlimit: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.positive()\n\t\t\t.max(200)\n\t\t\t.optional()\n\t\t\t.describe('Max executions to return (default 50).'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst opts = args.limit !== undefined ? { limit: args.limit } : undefined;\n\t\tconst response = await ctx.hoststack.cron.list(teamId, args.service_id, opts);\n\t\tconst data = shapeList(response, 'executions', shapeCronExecution);\n\t\tconst summary =\n\t\t\tdata.items.length === 0\n\t\t\t\t? `No cron executions yet for ${args.service_id}.`\n\t\t\t\t: `Found ${data.items.length} cron execution${data.items.length === 1 ? '' : 's'} for ${args.service_id}.`;\n\t\treturn respond({ summary, data });\n\t},\n});\n\ndefineTool({\n\tname: 'get_cron_execution',\n\tcategory: 'cron',\n\tdescription: [\n\t\t'Fetch a single cron execution record. Includes status, exit code, and timing.',\n\t\t'',\n\t\t'When to use: drilling into a specific run after list_cron_executions surfaced a failure, or correlating an execution timestamp with logs.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the cron service.',\n\t\t' - execution_id: publicId of the execution record.',\n\t\t'',\n\t\t'Returns: { execution: CronExecution }.',\n\t\t'',\n\t\t'Example: get_cron_execution({ service_id: \"svc_cron\", execution_id: \"exe_xyz\" }) → { execution: { status: \"failed\", exitCode: 1, … } }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Cron service publicId.'),\n\t\texecution_id: z.string().describe('Execution publicId.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.cron.get(teamId, args.service_id, args.execution_id);\n\t\tconst data = { execution: shapeCronExecution(response.execution) };\n\t\tconst status =\n\t\t\tdata.execution && 'status' in data.execution\n\t\t\t\t? (data.execution as { status: string }).status\n\t\t\t\t: 'unknown';\n\t\treturn respond({ summary: `Execution ${args.execution_id} ${status}.`, data });\n\t},\n});\n","import { z } from 'zod';\n\nimport { defineTool } from '../registry.js';\nimport { respond } from '../lib/respond.js';\nimport { shapeDatabase, shapeList } from '../lib/shape.js';\nimport { machineInput, resolveMachineId } from './machines.js';\n\n/**\n * Supported + default engine versions. DUPLICATED from\n * packages/shared/src/constants/database-versions.ts on purpose — the MCP\n * package must not depend on the private @hoststack/shared workspace package\n * (see the same note in tools/services.ts). Kept in sync by review.\n */\nconst DATABASE_VERSIONS = {\n\tpostgres: { default: '18', supported: ['18', '17', '16', '15'] },\n\tredis: { default: '8', supported: ['8', '7', '6'] },\n\tmysql: { default: '8.4', supported: ['8.4', '8.0', '5.7'] },\n\tmariadb: { default: '11.4', supported: ['11.4', '10.11'] },\n\tmongodb: { default: '8', supported: ['8', '7', '6'] },\n} as const;\n\nconst DB_ENGINES = ['postgres', 'redis', 'mysql', 'mariadb', 'mongodb'] as const;\n\nconst VERSION_HELP = (Object.keys(DATABASE_VERSIONS) as (keyof typeof DATABASE_VERSIONS)[])\n\t.map(\n\t\t(e) =>\n\t\t\t`${e}: ${DATABASE_VERSIONS[e].supported.join('/')} (default ${DATABASE_VERSIONS[e].default})`,\n\t)\n\t.join('; ');\n\ndefineTool({\n\tname: 'create_database',\n\tcategory: 'databases',\n\tdescription: [\n\t\t'Provision a MANAGED database (Postgres, Redis, MySQL, MariaDB, MongoDB) in a project. This is the correct and ONLY supported way to add a database to a HostStack app.',\n\t\t'',\n\t\t'When to use: any time an app needs a datastore — \"add a database\", \"I need Postgres\", \"set up Redis\", \"give this service a DB\". Also the right call when scaffolding a new app that will need persistence.',\n\t\t'',\n\t\t'*** Do NOT hand-roll a database as a service. *** Deploying `postgres:16` (or redis/mysql/mongo) via create_service with a docker_image is NOT how databases work on this platform: it gets no managed backups, no automated version upgrades, no HA/failover path, no credential rotation, no metrics, no persistent volume wired up, and nothing will inject its connection URL into your app. If the user says \"add a database\", \"I need Postgres\", \"set up Redis\" — call THIS tool.',\n\t\t'',\n\t\t'How a database reaches your app (the whole flow — 3 calls, no secrets handled):',\n\t\t' 1. create_database({ project_id, name, engine }) — provisions it.',\n\t\t' 2. link_resource_to_service({ service_id, resource_type: \"database\", resource_id: <database.id>, alias: \"APP_DB\" }) — binds it to the service that needs it.',\n\t\t' 3. trigger_deploy({ service_id }) — on that deploy the platform injects the connection info as env vars.',\n\t\t'After step 3 the container has `DATABASE_URL` (postgres/mysql/mariadb), `REDIS_URL` (redis) or `MONGO_URL` (mongodb) set automatically, plus alias-prefixed vars. An app reading `process.env.DATABASE_URL` needs zero code changes. You never have to fetch, generate or paste a password.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - project_id: numeric id or publicId (\"prj_…\") of the target project.',\n\t\t' - name: database name (1–100 chars).',\n\t\t` - engine: ${DB_ENGINES.join(' | ')}.`,\n\t\t` - version (optional): engine version. Supported — ${VERSION_HELP}. Omit to get the default (recommended).`,\n\t\t' - plan (optional): \"micro\" | \"starter\" | \"standard\" | \"pro\" (default \"starter\"). \"starter\" is the smallest always-on tier that includes backups.',\n\t\t' - environment_id (optional): bind to a specific environment; defaults to the project Production env.',\n\t\t' - postgis (optional): provision the PostGIS image variant so `CREATE EXTENSION postgis` works. Postgres only.',\n\t\t' - pgvector (optional): provision the pgvector image variant so `CREATE EXTENSION vector` works. Postgres only, mutually exclusive with postgis.',\n\t\t\" - machine (optional): put it on one of the team's OWN enrolled machines (name or id, see list_machines) instead of HostStack compute. It costs nothing there and is still backed up off-site, but it is reachable ONLY from that same machine — the service that queries it has to run there too, and linking it to a service anywhere else is refused. No HA and no external access on a machine you own.\",\n\t\t'',\n\t\t'Returns: { database: Database } — note BOTH `id` (numeric — this is what link_resource_to_service wants) and `publicId` (\"db_…\" — what the other database tools want).',\n\t\t'',\n\t\t'Provisioning is async: the row comes back immediately, usually as `creating`. Poll get_database until `status === \"available\"` before linking or connecting.',\n\t\t'',\n\t\t'Example: create_database({ project_id: \"prj_abc\", name: \"app-db\", engine: \"postgres\" }) → { database: { id: 42, publicId: \"db_…\", status: \"creating\" } }',\n\t].join('\\n'),\n\tinput: {\n\t\tproject_id: z\n\t\t\t.union([z.number().int().positive(), z.string()])\n\t\t\t.describe('Target project — numeric id or publicId (\"prj_…\").'),\n\t\tname: z.string().min(1).max(100).describe('Database name (1–100 chars).'),\n\t\tengine: z.enum(DB_ENGINES).describe('Database engine.'),\n\t\tversion: z\n\t\t\t.string()\n\t\t\t.max(20)\n\t\t\t.optional()\n\t\t\t.describe(`Engine version. ${VERSION_HELP}. Omit for the engine default.`),\n\t\tplan: z\n\t\t\t.enum(['micro', 'starter', 'standard', 'pro'])\n\t\t\t.optional()\n\t\t\t.describe('Plan tier (memory/CPU). Default \"starter\".'),\n\t\tenvironment_id: z\n\t\t\t.union([z.number().int().positive(), z.string()])\n\t\t\t.optional()\n\t\t\t.describe('Environment to bind to. Defaults to the project Production env.'),\n\t\tpostgis: z\n\t\t\t.boolean()\n\t\t\t.optional()\n\t\t\t.describe('Postgres only — enable the PostGIS extension image.'),\n\t\tpgvector: z\n\t\t\t.boolean()\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t'Postgres only — enable the pgvector extension image. Exclusive with postgis.',\n\t\t\t),\n\t\tmachine: machineInput,\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst projectId = await ctx.hoststack.resolveId(args.project_id, {\n\t\t\tkind: 'project',\n\t\t\tteamId,\n\t\t});\n\t\ttype CreateInput = Parameters<typeof ctx.hoststack.databases.create>[1];\n\t\tconst input: CreateInput = {\n\t\t\tname: args.name,\n\t\t\tengine: args.engine,\n\t\t\tprojectId,\n\t\t};\n\t\tif (args.version !== undefined) input.version = args.version;\n\t\tif (args.plan !== undefined) input.plan = args.plan;\n\t\tif (args.environment_id !== undefined) {\n\t\t\tinput.environmentId = await ctx.hoststack.resolveId(args.environment_id, {\n\t\t\t\tkind: 'environment',\n\t\t\t\tteamId,\n\t\t\t});\n\t\t}\n\t\tif (args.postgis !== undefined) input.postgis = args.postgis;\n\t\tif (args.pgvector !== undefined) input.pgvector = args.pgvector;\n\t\tif (args.machine !== undefined) {\n\t\t\tinput.machineId = await resolveMachineId(ctx, teamId, args.machine);\n\t\t}\n\n\t\tconst response = await ctx.hoststack.databases.create(teamId, input);\n\t\tconst data = { database: shapeDatabase(response.database) };\n\t\tconst db = response.database as { id?: number; publicId?: string; status?: string };\n\t\treturn respond({\n\t\t\tsummary: `Created ${args.engine} database \"${args.name}\" (${db.publicId ?? 'unknown'}, numeric id ${db.id ?? '?'}) — status ${db.status ?? 'creating'}. Poll get_database until status=available, then link_resource_to_service to inject its URL into a service.`,\n\t\t\tdata,\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'delete_database',\n\tcategory: 'databases',\n\tdescription: [\n\t\t'Permanently delete a managed database — the container, its volume, and ALL data it holds.',\n\t\t'',\n\t\t'When to use: the user has explicitly asked to destroy a database they no longer want. This is irreversible and takes the data with it — confirm with the user before calling, and never call it to \"clean up\" as a side effect of another task. If the goal is to stop paying for an idle database while keeping the data, use suspend_database instead.',\n\t\t'',\n\t\t\"Any service still linked to this database will lose the injected connection vars on its next deploy, and will fail at runtime if it depends on them. Check which services consume it first — the dashboard shows this under the database's Linked Services tab.\",\n\t\t'',\n\t\t'Inputs:',\n\t\t' - database_id: publicId of the database (e.g. \"db_…\").',\n\t\t'',\n\t\t'Returns: { ok: true }.',\n\t\t'',\n\t\t'Example: delete_database({ database_id: \"db_abc\" }) → { ok: true }',\n\t].join('\\n'),\n\tinput: {\n\t\tdatabase_id: z.string().describe('Database publicId (e.g. db_abc) to permanently delete.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tawait ctx.hoststack.databases.delete(teamId, args.database_id);\n\t\treturn respond({\n\t\t\tsummary: `Deleted database ${args.database_id} and all of its data. This cannot be undone.`,\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'suspend_database',\n\tcategory: 'databases',\n\tdescription: [\n\t\t'Suspend a managed database — stops its container while KEEPING the volume and all data. The reversible alternative to delete_database.',\n\t\t'',\n\t\t'When to use: a staging or preview database that nobody is using, to stop burning its compute tier. Connections fail while suspended; resume_database brings it back on the same connection URL.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - database_id: publicId of the database (e.g. \"db_…\").',\n\t\t'',\n\t\t'Returns: { ok: true }.',\n\t\t'',\n\t\t'Example: suspend_database({ database_id: \"db_abc\" }) → { ok: true }',\n\t].join('\\n'),\n\tinput: {\n\t\tdatabase_id: z.string().describe('Database publicId (e.g. db_abc) to suspend.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tawait ctx.hoststack.databases.suspend(teamId, args.database_id);\n\t\treturn respond({\n\t\t\tsummary: `Suspended database ${args.database_id}. Data is preserved; resume_database restores it on the same URL.`,\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'resume_database',\n\tcategory: 'databases',\n\tdescription: [\n\t\t'Resume a suspended managed database — restarts its container against the existing volume. The connection URL is unchanged, so linked services do NOT need a redeploy.',\n\t\t'',\n\t\t'When to use: undoing a suspend_database, or bringing back a database that was suspended for non-payment once billing is resolved.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - database_id: publicId of the database (e.g. \"db_…\").',\n\t\t'',\n\t\t'Returns: { ok: true }. Poll get_database until `status === \"available\"`.',\n\t\t'',\n\t\t'Example: resume_database({ database_id: \"db_abc\" }) → { ok: true }',\n\t].join('\\n'),\n\tinput: {\n\t\tdatabase_id: z.string().describe('Database publicId (e.g. db_abc) to resume.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tawait ctx.hoststack.databases.resume(teamId, args.database_id);\n\t\treturn respond({\n\t\t\tsummary: `Resume dispatched for ${args.database_id}. Poll get_database until status=available.`,\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'list_databases',\n\tcategory: 'databases',\n\tdescription: [\n\t\t'List managed databases (Postgres, Redis, MySQL, MariaDB, MongoDB) inside a project. Meilisearch and NATS are NOT databases — they are separate managed resources (search / queue) and do not appear here; see list_service_resources for what a service consumes.',\n\t\t'',\n\t\t'When to use: an agent needs to know what data stores exist in a project before connecting a service or running a migration. Pair with list_projects to discover project IDs.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - project_id: numeric project ID (from list_projects).',\n\t\t'',\n\t\t'Returns: { items: Database[] } — id, publicId, name, type, status, version, projectId, createdAt.',\n\t\t'',\n\t\t'Example: list_databases({ project_id: 12 }) → { items: [{ publicId: \"db_…\", type: \"postgres\", status: \"running\", … }] }',\n\t].join('\\n'),\n\tinput: {\n\t\tproject_id: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.positive()\n\t\t\t.describe('Numeric project ID (from list_projects).'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.databases.list(teamId, args.project_id);\n\t\tconst data = shapeList(response, 'databases', shapeDatabase);\n\t\tconst summary =\n\t\t\tdata.items.length === 0\n\t\t\t\t? `No databases in project ${args.project_id}.`\n\t\t\t\t: `Found ${data.items.length} database${data.items.length === 1 ? '' : 's'} in project ${args.project_id}.`;\n\t\treturn respond({ summary, data });\n\t},\n});\n\ndefineTool({\n\tname: 'update_database',\n\tcategory: 'databases',\n\tdescription: [\n\t\t'Update a managed database — rename, change plan tier (memory/CPU), or grow disk size.',\n\t\t'',\n\t\t'When to use: the user wants to resize a database (disk or plan), or rename it. Disk can only grow — shrinking is rejected because the underlying filesystem would orphan data.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - database_id: publicId of the database (e.g. \"db_…\").',\n\t\t' - name (optional): new database name.',\n\t\t' - plan (optional): \"free\" | \"micro\" | \"starter\" | \"standard\" | \"pro\" — changes memory tier.',\n\t\t' - disk_size_gb (optional): new disk size in GB (must be ≥ current).',\n\t\t'',\n\t\t'Returns: { database: Database } — the updated record.',\n\t\t'',\n\t\t'Example: update_database({ database_id: \"db_xyz\", disk_size_gb: 50 }) → { database: { diskSizeGb: 50, … } }',\n\t].join('\\n'),\n\tinput: {\n\t\tdatabase_id: z.string().describe('Database publicId (e.g. db_xyz).'),\n\t\tname: z.string().min(1).max(100).optional().describe('New database name.'),\n\t\tplan: z\n\t\t\t.enum(['free', 'micro', 'starter', 'standard', 'pro'])\n\t\t\t.optional()\n\t\t\t.describe('Plan tier (memory/CPU).'),\n\t\tdisk_size_gb: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(1)\n\t\t\t.max(1024)\n\t\t\t.optional()\n\t\t\t.describe('New disk size in GB. Must be ≥ current.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst input: {\n\t\t\tname?: string;\n\t\t\tplan?: 'free' | 'micro' | 'starter' | 'standard' | 'pro';\n\t\t\tdiskSizeGb?: number;\n\t\t} = {};\n\t\tif (args.name !== undefined) input.name = args.name;\n\t\tif (args.plan !== undefined) input.plan = args.plan;\n\t\tif (args.disk_size_gb !== undefined) input.diskSizeGb = args.disk_size_gb;\n\t\tif (Object.keys(input).length === 0) {\n\t\t\treturn respond({ summary: 'No fields to update.', data: {} });\n\t\t}\n\t\tconst response = await ctx.hoststack.databases.update(teamId, args.database_id, input);\n\t\tconst data = { database: shapeDatabase(response.database) };\n\t\tconst fields = Object.keys(input).join(', ');\n\t\treturn respond({ summary: `Updated ${fields} on database ${args.database_id}.`, data });\n\t},\n});\n\ndefineTool({\n\tname: 'get_database',\n\tcategory: 'databases',\n\tdescription: [\n\t\t'Fetch a single managed database by ID. Includes status, type, version, and project link.',\n\t\t'',\n\t\t'When to use: confirming the version of a database before generating connection strings, or checking the status of a recently created database. To retrieve the actual connection credentials, use the dashboard — credentials are intentionally not exposed via this MCP tool.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - database_id: publicId of the database (e.g. \"db_…\").',\n\t\t'',\n\t\t'Returns: { database: Database }.',\n\t\t'',\n\t\t'Example: get_database({ database_id: \"db_xyz\" }) → { database: { type: \"postgres\", version: \"16\", status: \"running\", … } }',\n\t].join('\\n'),\n\tinput: {\n\t\tdatabase_id: z.string().describe('Database publicId (e.g. db_xyz).'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.databases.get(teamId, args.database_id);\n\t\tconst data = { database: shapeDatabase(response.database) };\n\t\tconst type =\n\t\t\tdata.database && 'type' in data.database\n\t\t\t\t? (data.database as { type: string }).type\n\t\t\t\t: 'unknown';\n\t\tconst status =\n\t\t\tdata.database && 'status' in data.database\n\t\t\t\t? (data.database as { status: string }).status\n\t\t\t\t: 'unknown';\n\t\treturn respond({ summary: `Database ${args.database_id} (${type}) is ${status}.`, data });\n\t},\n});\n\ndefineTool({\n\tname: 'query_database',\n\tcategory: 'databases',\n\tdescription: [\n\t\t'Run a single READ-ONLY SQL statement against a managed Postgres database.',\n\t\t'',\n\t\t'When to use: ad-hoc data inspection — checking row counts, sampling rows, debugging a constraint. For schema changes or seed data, ship a Drizzle migration instead.',\n\t\t'',\n\t\t'Safety model (server-side, not bypassable):',\n\t\t' - Wrapped in `BEGIN TRANSACTION READ ONLY` — INSERT/UPDATE/DELETE/DDL fail with a clear \"cannot execute … in a read-only transaction\" error.',\n\t\t' - 30s statement_timeout — runaway queries are killed.',\n\t\t' - 1000-row cap; result CSV is also size-capped at 1 MiB. Both surface via `truncated: true`.',\n\t\t' - Postgres engine only (v1). Other engines return a 400.',\n\t\t' - Single statement only; no embedded `;` and no psql meta-commands (`\\\\…`).',\n\t\t' - Every call is audit-logged (success or failure) as `database.query` so an admin can answer \"who queried this DB, when, with what SQL\".',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - database_id: publicId of the postgres database (e.g. \"db_…\"). Resolved via the SDK\\'s publicId helper.',\n\t\t' - sql: a single SELECT/WITH/SHOW/EXPLAIN statement, no trailing `;`. Max 16 KiB.',\n\t\t'',\n\t\t'Returns: { columns: string[], rows: string[][], rowCount: number, truncated: boolean, durationMs: number }. Values come back as strings (psql --csv); cast on the caller side when needed.',\n\t\t'',\n\t\t'Example:',\n\t\t' - query_database({ database_id: \"db_abc\", sql: \"SELECT count(*) FROM users\" }) → { columns: [\"count\"], rows: [[\"1234\"]], … }',\n\t\t' - query_database({ database_id: \"db_abc\", sql: \"SELECT id, email FROM users WHERE created_at > now() - interval \\'1 day\\' LIMIT 50\" })',\n\t].join('\\n'),\n\tinput: {\n\t\tdatabase_id: z.string().describe('Database publicId (e.g. db_abc).'),\n\t\tsql: z\n\t\t\t.string()\n\t\t\t.min(1)\n\t\t\t.max(16_384)\n\t\t\t.describe(\n\t\t\t\t'A single READ-ONLY SQL statement. No trailing `;`, no embedded `;`, no psql meta-commands.',\n\t\t\t),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst result = await ctx.hoststack.databases.query(teamId, args.database_id, args.sql);\n\t\tconst summary = result.truncated\n\t\t\t? `Query returned ${result.rowCount} row${result.rowCount === 1 ? '' : 's'} (truncated; raise LIMIT if needed) in ${result.durationMs}ms.`\n\t\t\t: `Query returned ${result.rowCount} row${result.rowCount === 1 ? '' : 's'} in ${result.durationMs}ms.`;\n\t\treturn respond({ summary, data: result });\n\t},\n});\n\ndefineTool({\n\tname: 'upgrade_database_to_ha',\n\tcategory: 'databases',\n\tdescription: [\n\t\t'Trigger a single-node → HA migration on a managed Postgres database. The agent runs pg_dump → bootstrap 3-node Patroni cluster (1 leader + 2 sync replicas + HAProxy) → pg_restore. Customer connections to this database are briefly read-only during the cutover; the standalone container stays running (read-only) for 24 h as a rollback target before automatic decommission.',\n\t\t'',\n\t\t'Requires: PATRONI_ENABLED env on the HostStack deployment + `teams.ha_beta = true` on the calling team. Returns 403 otherwise. Returns 400 if the database is already HA, not Postgres, not on the standard or pro tier, or has a pg_database_size over 1 GB (in-memory archive transfer cap).',\n\t\t'',\n\t\t'When to use: a customer needs failover protection on an existing standalone Postgres. For new databases on an HA-eligible team, just call create_database — the cluster path is automatic.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - database_id: publicId of the postgres database to upgrade.',\n\t\t'',\n\t\t'Returns: 202 Accepted. The upgrade is async — poll get_database until `pg_engine_type === \"patroni\"` and `status === \"available\"` to confirm completion. The Cluster tab on the dashboard mirrors the same data.',\n\t\t'',\n\t\t'Example: upgrade_database_to_ha({ database_id: \"db_abc\" }) → { ok: true }',\n\t].join('\\n'),\n\tinput: {\n\t\tdatabase_id: z.string().describe('Database publicId (e.g. db_abc) to upgrade to HA.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tawait ctx.hoststack.databases.upgradeToHa(teamId, args.database_id);\n\t\treturn respond({\n\t\t\tsummary: `HA migration started for ${args.database_id}. Poll get_database until pg_engine_type=patroni to confirm.`,\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'upgrade_database_version',\n\tcategory: 'databases',\n\tdescription: [\n\t\t'Upgrade a standalone managed database (postgres or redis) to a newer engine version in place. The agent dumps the live data, recreates the container at the target version under the same DNS name, and restores — so connection URLs stay valid and services do NOT need a redeploy. The database is briefly unavailable (seconds) during the cut-over; on any failure the original container is rolled back automatically.',\n\t\t'',\n\t\t'`version` must be a supported, strictly-newer version for the engine (postgres: 18/17/16/15, redis: 8/7/6). Downgrades are rejected. Returns 400 for unsupported engines (mysql/mariadb/mongodb upgrades are not implemented yet), for HA (Patroni) clusters, or if the database is not currently `available`.',\n\t\t'',\n\t\t'When to use: a customer wants to move to a newer Postgres/Redis major version. Check get_database first — the `version` field shows the current version; compare against the engine latest.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - database_id: publicId of the postgres/redis database to upgrade.',\n\t\t' - version: target engine version (e.g. \"18\" for postgres, \"8\" for redis).',\n\t\t'',\n\t\t'Returns: 202 Accepted. The upgrade is async — poll get_database until `version` matches the target and `status === \"available\"` to confirm.',\n\t\t'',\n\t\t'Example: upgrade_database_version({ database_id: \"db_abc\", version: \"18\" }) → { ok: true }',\n\t].join('\\n'),\n\tinput: {\n\t\tdatabase_id: z.string().describe('Database publicId (e.g. db_abc) to upgrade.'),\n\t\tversion: z\n\t\t\t.string()\n\t\t\t.describe('Target engine version, e.g. \"18\" for postgres or \"8\" for redis.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tawait ctx.hoststack.databases.upgradeVersion(teamId, args.database_id, args.version);\n\t\treturn respond({\n\t\t\tsummary: `Version upgrade to v${args.version} started for ${args.database_id}. Poll get_database until version=${args.version} and status=available to confirm.`,\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'restart_database',\n\tcategory: 'databases',\n\tdescription: [\n\t\t'Restart a managed database in place — `docker restart` of its container: same container, same volume, same connection URL. The database is briefly unavailable (a few seconds) while it bounces; no redeploy of connected services is needed.',\n\t\t'',\n\t\t'When to use: a database is wedged (stuck connection, needs to reload a config change, leaking memory) and a clean bounce is the fix — the database-equivalent of restarting a service.',\n\t\t'',\n\t\t'The database must be `available`. A suspended database has no running container (resume it first → 409); a creating/migrating one is mid-operation (409). For a Patroni HA cluster this bounces the current leader, which triggers a normal failover while replicas keep serving reads.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - database_id: publicId of the database to restart.',\n\t\t'',\n\t\t'Returns: { ok: true } once the restart command reaches an agent (dispatched optimistically, like a service restart). Poll get_service_logs / get_database to confirm it came back.',\n\t\t'',\n\t\t'Example: restart_database({ database_id: \"db_abc\" }) → { ok: true }',\n\t].join('\\n'),\n\tinput: {\n\t\tdatabase_id: z.string().describe('Database publicId (e.g. db_abc) to restart.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tawait ctx.hoststack.databases.restart(teamId, args.database_id);\n\t\treturn respond({\n\t\t\tsummary: `Restart dispatched for ${args.database_id}. It will be briefly unavailable while the container bounces.`,\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'get_database_cluster',\n\tcategory: 'databases',\n\tdescription: [\n\t\t'Get the live + retired cluster members and failover history for a Patroni HA Postgres database.',\n\t\t'',\n\t\t'When to use: confirm an HA cluster is healthy (1 leader + 2 sync replicas + 1 proxy + 1 etcd live) or investigate a failover that fired. For standalone databases this returns 400 — call get_database first if you need to branch.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - database_id: publicId of the postgres database (must be HA).',\n\t\t'',\n\t\t\"Returns: { members: { id, memberRole: 'etcd' | 'primary-candidate' | 'replica' | 'proxy', containerId, workerHostId, joinedAt, leftAt }[], failovers: { id, createdAt, oldLeader, newLeader }[] }.\",\n\t\t'',\n\t\t'Example: get_database_cluster({ database_id: \"db_abc\" }) → { members: [...5 live rows], failovers: [] }',\n\t].join('\\n'),\n\tinput: {\n\t\tdatabase_id: z.string().describe('Database publicId for the HA cluster.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst result = await ctx.hoststack.databases.getCluster(teamId, args.database_id);\n\t\tconst live = result.members.filter((m) => m.leftAt === null);\n\t\tconst summary = `${live.length} live members (${live.map((m) => m.memberRole).join(', ')}); ${result.failovers.length} historical failovers.`;\n\t\treturn respond({ summary, data: result });\n\t},\n});\n","import type { Machine } from '@hoststack.dev/sdk';\nimport { z } from 'zod';\n\nimport { respond } from '../lib/respond.js';\nimport { shape, shapeList } from '../lib/shape.js';\nimport { defineTool, type ToolContext } from '../registry.js';\n\n/**\n * The `machine` input every \"create something\" tool accepts.\n *\n * A name rather than an id, because that is what the user says (\"put it on my\n * desktop\") and the numeric id is an internal one an agent has no other reason\n * to hold. A numeric id is still accepted for callers that already have one.\n */\nexport const machineInput = z\n\t.union([z.number().int().positive(), z.string()])\n\t.optional()\n\t.describe(\n\t\t'Run this on one of the team\\'s OWN enrolled machines instead of HostStack compute — the machine\\'s name (e.g. \"desktop\") or its numeric id. List them with list_machines. Omit for HostStack compute, which is the default and the right answer unless the user asked for their own hardware.',\n\t);\n\n/**\n * Resolve a machine name (or id) to the numeric `machineId` the API pins on.\n *\n * Fails loudly and with the list of real names rather than guessing. Placement\n * is pinned at creation and never changed afterwards — the container and its\n * disk live on that machine — so a wrong guess here is not a setting somebody\n * corrects later, it is a rebuild.\n */\nexport async function resolveMachineId(\n\tctx: ToolContext,\n\tteamId: number,\n\tmachine: number | string,\n): Promise<number> {\n\tconst { machines } = await ctx.hoststack.machines.list(teamId);\n\tif (machines.length === 0) {\n\t\tthrow new Error(\n\t\t\t'This team has no enrolled machines. Enrol one from the dashboard (Machines → Add machine) or with `hoststack machines add`, then run the install command it prints on that machine.',\n\t\t);\n\t}\n\n\tconst known = machines.map((m) => `\"${m.name}\" (id ${m.id})`).join(', ');\n\tif (typeof machine === 'number') {\n\t\tconst byId = machines.find((m) => m.id === machine);\n\t\tif (!byId) throw new Error(`No machine with id ${machine}. This team has: ${known}.`);\n\t\treturn byId.id;\n\t}\n\n\tconst needle = machine.trim().toLowerCase();\n\tconst matches = machines.filter(\n\t\t(m) => m.name.toLowerCase() === needle || m.hostname.toLowerCase() === needle,\n\t);\n\tif (matches.length === 0) {\n\t\tthrow new Error(`No machine named \"${machine}\". This team has: ${known}.`);\n\t}\n\t// Names are not unique — two machines can both be called \"desktop\". Refuse\n\t// rather than pick one, because the two are different physical computers and\n\t// the work cannot be moved between them afterwards.\n\tif (matches.length > 1) {\n\t\tthrow new Error(\n\t\t\t`More than one machine is named \"${machine}\" (ids ${matches.map((m) => m.id).join(', ')}). Pass the numeric id.`,\n\t\t);\n\t}\n\treturn matches[0]!.id;\n}\n\n/** A machine's state in one sentence, for the summary line. */\nfunction describeMachine(m: Machine): string {\n\tif (!m.enrolled) return `${m.name}: registered but never paired`;\n\tif (m.status !== 'active') return `${m.name}: offline`;\n\tif (m.agentBuild === 'from-source') return `${m.name}: online, agent running from source`;\n\tif (m.agentBuild === 'unknown') return `${m.name}: online, agent too old to report its build`;\n\treturn `${m.name}: online`;\n}\n\ndefineTool({\n\tname: 'list_machines',\n\tcategory: 'machines',\n\tdescription: [\n\t\t\"List the team's OWN enrolled machines — hardware they supply (a spare desktop, a home server, a VPS they already pay for) that HostStack runs work on. These are NOT HostStack's servers; you cannot enrol, resize or reboot them from here.\",\n\t\t'',\n\t\t'When to use: before creating a service, database or dev box that the user wants on their own hardware (pass the machine name to create_service / create_database / create_standalone_dev_environment), or to explain why something pinned to a machine is down — a machine that is switched off takes everything on it with it.',\n\t\t'',\n\t\t'Inputs: none.',\n\t\t'',\n\t\t'Returns: { items: Machine[] } — each entry has id (pass this as `machine`), name, status (active|offline|provisioning), enrolled, agentBuild (current|behind|from-source|unknown), agentVersion, lastHeartbeatAt, totalMemoryMb, totalCpuCores, and workloads { devBoxes, services, databases }.',\n\t\t'',\n\t\t'Notes: a machine is only reachable while its owner has it switched on, and a database pinned to one is reachable ONLY from that same machine — put the app that queries it there too. Enrolment needs a person at the machine to run an installer, so there is no tool for it: point the user at the dashboard (Machines → Add machine) or `hoststack machines add`.',\n\t\t'',\n\t\t'Example: list_machines() → { items: [{ id: 12452, name: \"desktop\", status: \"active\", workloads: { devBoxes: 8, services: 0, databases: 2 } }] }',\n\t].join('\\n'),\n\tinput: {},\n\thandler: async (_args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.machines.list(teamId);\n\t\tconst data = shapeList(response, 'machines', shape);\n\t\tconst summary =\n\t\t\tresponse.machines.length === 0\n\t\t\t\t? 'This team has no enrolled machines. Everything runs on HostStack compute.'\n\t\t\t\t: `${response.machines.length} machine${response.machines.length === 1 ? '' : 's'} — ${response.machines.map(describeMachine).join('; ')}.`;\n\t\treturn respond({ summary, data });\n\t},\n});\n\ndefineTool({\n\tname: 'get_machine',\n\tcategory: 'machines',\n\tdescription: [\n\t\t\"One of the team's own enrolled machines, with what is actually running on it — rather than the counts list_machines carries.\",\n\t\t'',\n\t\t'When to use: before advising the user to switch a machine off, move work off it, or remove it — `running` is the list of things that go away with the machine. Also the fastest answer to \"what is on my desktop?\".',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - machine: the machine name (e.g. \"desktop\") or its numeric id.',\n\t\t'',\n\t\t'Returns: { machine: Machine, running: Workload[] } — each workload has kind (\"dev_box\" | \"service\" | \"database\"), name, status and projectId. The kinds differ in consequence: a dev box waits for its machine to come back, a service is down while it is off, and a database takes down everything reading from it.',\n\t\t'',\n\t\t'Example: get_machine({ machine: \"desktop\" }) → { machine: { name: \"desktop\", status: \"active\" }, running: [{ kind: \"database\", name: \"postgres-58\" }] }',\n\t].join('\\n'),\n\tinput: {\n\t\tmachine: z\n\t\t\t.union([z.number().int().positive(), z.string()])\n\t\t\t.describe('Machine name (e.g. \"desktop\") or numeric id. List them with list_machines.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst machineId = await resolveMachineId(ctx, teamId, args.machine);\n\t\tconst response = await ctx.hoststack.machines.get(teamId, machineId);\n\t\tconst data = {\n\t\t\tmachine: shape(response.machine),\n\t\t\trunning: shapeList({ running: response.running }, 'running', shape).items,\n\t\t};\n\t\tconst running = response.running ?? [];\n\t\tconst summary =\n\t\t\trunning.length === 0\n\t\t\t\t? `${describeMachine(response.machine)}. Nothing is running on it.`\n\t\t\t\t: `${describeMachine(response.machine)}. Running ${running.length} thing${running.length === 1 ? '' : 's'}: ${running.map((w) => `${w.name} (${w.kind})`).join(', ')}.`;\n\t\treturn respond({ summary, data });\n\t},\n});\n","import { z } from 'zod';\n\nimport { defineTool } from '../registry.js';\nimport { respond } from '../lib/respond.js';\nimport { shape, shapeDeploy, shapeList } from '../lib/shape.js';\n\ndefineTool({\n\tname: 'list_deploys',\n\tcategory: 'deploys',\n\tdescription: [\n\t\t'List deploys for a service in reverse-chronological order (newest first).',\n\t\t'',\n\t\t'When to use: investigating a recent deploy outcome, finding the last successful deploy ID, comparing run-times, or just checking deploy history before triggering a new one.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service (e.g. \"svc_abc123\"). Use list_services to find it.',\n\t\t'',\n\t\t'Returns: { items: Deploy[] } — each deploy includes id, publicId, status (pending|building|deploying|live|failed|cancelled), commitHash (the resolved commit SHA; the key is OMITTED entirely when the deploy recorded none, so a missing key means \"no commit\", not \"wrong key\"), commitMessage, branch, triggeredBy, startedAt, finishedAt, imageBuildMs (docker build / image-pull only; null on skip-build redeploys — v89), containerBootMs (deploying → live wall-clock; null on builds that failed before container start — v89), buildDurationMs (legacy alias of imageBuildMs kept for back-compat), totalDurationMs (full deploy wall-clock = finishedAt − startedAt). Use imageBuildMs + containerBootMs together to tell \"build is slow\" apart from \"boot is slow\".',\n\t\t'',\n\t\t'Example: list_deploys({ service_id: \"svc_abc\" }) → { items: [{ publicId: \"dpl_…\", status: \"live\", commitMessage: \"Fix login\", … }, …] }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId (e.g. svc_abc123).'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.deploys.list(teamId, args.service_id);\n\t\t// Deploys list is paginated and returns `{ data, page, ... }` — not the\n\t\t// bare `{ deploys: [] }` shape every other list endpoint uses.\n\t\tconst data = shapeList(response, 'data', shapeDeploy);\n\t\tconst summary =\n\t\t\tdata.items.length === 0\n\t\t\t\t? `No deploys yet for service ${args.service_id}.`\n\t\t\t\t: `Found ${data.items.length} deploy${data.items.length === 1 ? '' : 's'} for service ${args.service_id} (page ${response.page} of ${response.totalPages}, ${response.total} total).`;\n\t\treturn respond({ summary, data });\n\t},\n});\n\ndefineTool({\n\tname: 'get_deploy',\n\tcategory: 'deploys',\n\tdescription: [\n\t\t'Fetch a single deploy by ID, including its current status, commit metadata, and timestamps.',\n\t\t'',\n\t\t\"When to use: drilling into the details of a specific deploy after list_deploys, polling a build's status, or grabbing the commit SHA so you can correlate logs with code.\",\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t' - deploy_id: publicId of the deploy (e.g. \"dpl_…\").',\n\t\t'',\n\t\t'Returns: { deploy: Deploy } — full deploy record (status, commitHash (omitted when the deploy recorded no commit), commitMessage, branch, imageBuildMs + containerBootMs (v89 split timings; legacy buildDurationMs preserved as alias), totalDurationMs, finishedAt, etc).',\n\t\t'',\n\t\t'Example: get_deploy({ service_id: \"svc_abc\", deploy_id: \"dpl_xyz\" }) → { deploy: { status: \"live\", … } }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t\tdeploy_id: z.string().describe('Deploy publicId.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.deploys.get(teamId, args.service_id, args.deploy_id);\n\t\tconst data = { deploy: shapeDeploy(response.deploy) };\n\t\tconst status =\n\t\t\tdata.deploy && typeof data.deploy === 'object' && 'status' in data.deploy\n\t\t\t\t? (data.deploy as { status: string }).status\n\t\t\t\t: 'unknown';\n\t\treturn respond({ summary: `Deploy ${args.deploy_id} is ${status}.`, data });\n\t},\n});\n\ndefineTool({\n\tname: 'trigger_deploy',\n\tcategory: 'deploys',\n\tdescription: [\n\t\t'Kick off a new deploy. Defaults to the latest commit on the service branch; pass commit_hash or branch to deploy something else.',\n\t\t'',\n\t\t'When to use: the user explicitly asks to deploy (\"ship it\", \"redeploy\", \"kick off a new build\"). For services with auto-deploy enabled, a fresh git push already triggers a deploy automatically — only call this for manual triggers or after a config change.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t' - commit_hash (optional): build a specific commit instead of HEAD on the configured branch.',\n\t\t' - branch (optional): build the tip of a non-default branch.',\n\t\t'',\n\t\t'Returns: { deploy: Deploy } — the new deploy record. Status will start as \"pending\" then transition through \"building\" → \"deploying\" → \"live\" or \"failed\".',\n\t\t'',\n\t\t'Example: trigger_deploy({ service_id: \"svc_abc\" }) → { deploy: { publicId: \"dpl_…\", status: \"pending\", … } }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t\tcommit_hash: z\n\t\t\t.string()\n\t\t\t.max(40)\n\t\t\t.optional()\n\t\t\t.describe('Specific commit to build (default: branch HEAD).'),\n\t\tbranch: z\n\t\t\t.string()\n\t\t\t.max(200)\n\t\t\t.optional()\n\t\t\t.describe('Branch to build (default: service configured branch).'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst input: { commitHash?: string; branch?: string } = {};\n\t\tif (args.commit_hash !== undefined) input.commitHash = args.commit_hash;\n\t\tif (args.branch !== undefined) input.branch = args.branch;\n\t\tconst response = await ctx.hoststack.deploys.trigger(teamId, args.service_id, input);\n\t\tconst data = { deploy: shapeDeploy(response.deploy) };\n\t\tconst publicId =\n\t\t\tdata.deploy && 'publicId' in data.deploy\n\t\t\t\t? (data.deploy as { publicId: string }).publicId\n\t\t\t\t: 'unknown';\n\t\treturn respond({\n\t\t\tsummary: `Triggered deploy ${publicId} on service ${args.service_id}.`,\n\t\t\tdata,\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'cancel_deploy',\n\tcategory: 'deploys',\n\tdescription: [\n\t\t'Cancel a running deploy. Stops the build/deploy pipeline mid-flight; the previously live revision keeps serving traffic.',\n\t\t'',\n\t\t'When to use: the user notices a bad commit was pushed and wants to abort before it lands, or a build is hanging. Also the right call for a deploy stuck in `deploying` because the new container is crash-looping against its health check — that state is cancellable and cancelling it frees the service (and the build queue) immediately instead of waiting out the grace period. Has no effect on already-finished deploys.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t' - deploy_id: publicId of the deploy to cancel.',\n\t\t'',\n\t\t'Returns: { ok: true }.',\n\t\t'',\n\t\t'Example: cancel_deploy({ service_id: \"svc_abc\", deploy_id: \"dpl_xyz\" }) → { ok: true }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t\tdeploy_id: z.string().describe('Deploy publicId.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tawait ctx.hoststack.deploys.cancel(teamId, args.service_id, args.deploy_id);\n\t\treturn respond({\n\t\t\tsummary: `Cancelled deploy ${args.deploy_id} on service ${args.service_id}.`,\n\t\t\tdata: { ok: true },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'get_deploy_logs',\n\tcategory: 'deploys',\n\tdescription: [\n\t\t'Fetch the build/deploy logs for a single deploy. Returns the entire log buffer the agent can then search for errors.',\n\t\t'',\n\t\t'When to use: a deploy failed and you need to read the build output to diagnose. Pair with get_deploy to find a failed deploy_id, then call this. For runtime (post-deploy) logs of the running container, use get_service_logs instead.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t' - deploy_id: publicId of the deploy.',\n\t\t'',\n\t\t'Returns: { logs: string, lineCount: number, nextAfterId: number | null } — flattened build output joined with newlines, line count, and a cursor for pagination (pass back as after_id to fetch the next page).',\n\t\t'',\n\t\t'Example: get_deploy_logs({ service_id: \"svc_abc\", deploy_id: \"dpl_xyz\" }) → { logs: \"Building…\\\\nStep 1/8…\\\\n…\", lineCount: 42, nextAfterId: 1234 }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t\tdeploy_id: z.string().describe('Deploy publicId.'),\n\t\tafter_id: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.optional()\n\t\t\t.describe('Pagination cursor from a previous response’s nextAfterId.'),\n\t\tlimit: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(1)\n\t\t\t.max(5000)\n\t\t\t.optional()\n\t\t\t.describe('Max log entries to fetch (default 500, hard cap 5000).'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.deploys.getLogs(\n\t\t\tteamId,\n\t\t\targs.service_id,\n\t\t\targs.deploy_id,\n\t\t\t{\n\t\t\t\t...(args.after_id !== undefined ? { afterId: args.after_id } : {}),\n\t\t\t\t...(args.limit !== undefined ? { limit: args.limit } : {}),\n\t\t\t},\n\t\t);\n\t\tconst entries = response.logs;\n\t\tconst logs = entries.map((e) => e.message).join('\\n');\n\t\treturn respond({\n\t\t\tsummary: `Fetched ${entries.length} log line${entries.length === 1 ? '' : 's'} for deploy ${args.deploy_id}.`,\n\t\t\tdata: { logs, lineCount: entries.length, nextAfterId: response.nextAfterId },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'diagnose_deploy',\n\tcategory: 'deploys',\n\tdescription: [\n\t\t\"One-shot diagnostic bundle for a deploy: full deploy record + tail of build logs + tail of runtime logs starting at the deploy's start time. Lets you investigate any deploy failure mode (build-time failure, health-check timeout, post-deploy runtime crash) without juggling two log endpoints and two IDs.\",\n\t\t'',\n\t\t'When to use: a deploy is failed/superseded/stuck and you want the whole story in one call. For just the build output, use get_deploy_logs. For just runtime logs of the running service, use get_service_logs.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t' - deploy_id: publicId of the deploy to diagnose.',\n\t\t' - build_log_lines (optional): how many trailing build log lines to include. Default 200, max 2000.',\n\t\t' - runtime_log_lines (optional): how many trailing runtime log lines (since the deploy started) to include. Default 100, max 1000. Set to 0 to skip runtime logs (saves a round-trip when investigating a pure build-time failure).',\n\t\t'',\n\t\t'Returns: { deploy, build: { logs, lineCount }, runtime: { logs, lineCount } } — runtime is omitted when runtime_log_lines is 0 or the deploy never started a container.',\n\t\t'',\n\t\t'Example: diagnose_deploy({ service_id: \"svc_abc\", deploy_id: \"dpl_xyz\" }) → all three sections in one call.',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t\tdeploy_id: z.string().describe('Deploy publicId.'),\n\t\tbuild_log_lines: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(1)\n\t\t\t.max(2000)\n\t\t\t.optional()\n\t\t\t.describe('Trailing build log lines. Default 200, max 2000.'),\n\t\truntime_log_lines: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(0)\n\t\t\t.max(1000)\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t'Trailing runtime log lines since deploy start. Default 100, max 1000. 0 = skip.',\n\t\t\t),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst buildLimit = args.build_log_lines ?? 200;\n\t\tconst runtimeLimit = args.runtime_log_lines ?? 100;\n\n\t\t// Pull deploy + build logs in parallel — build logs don't depend on\n\t\t// the deploy record's metadata. Runtime logs need the deploy's\n\t\t// startedAt for the `since` window so they wait for the deploy fetch.\n\t\tconst [deployResp, buildResp] = await Promise.all([\n\t\t\tctx.hoststack.deploys.get(teamId, args.service_id, args.deploy_id),\n\t\t\tctx.hoststack.deploys.getLogs(teamId, args.service_id, args.deploy_id, {\n\t\t\t\tlimit: buildLimit,\n\t\t\t}),\n\t\t]);\n\n\t\tconst buildEntries = buildResp.logs;\n\t\tconst buildLogs = buildEntries.map((e) => e.message).join('\\n');\n\n\t\tconst deployShape = shapeDeploy(deployResp.deploy);\n\t\tconst deployStatus =\n\t\t\tdeployShape && 'status' in deployShape\n\t\t\t\t? (deployShape as { status: string }).status\n\t\t\t\t: 'unknown';\n\t\tconst startedAtRaw =\n\t\t\tdeployShape && 'startedAt' in deployShape\n\t\t\t\t? (deployShape as { startedAt: unknown }).startedAt\n\t\t\t\t: undefined;\n\t\tconst startedAt = typeof startedAtRaw === 'string' ? startedAtRaw : undefined;\n\n\t\tconst data: Record<string, unknown> = {\n\t\t\tdeploy: deployShape,\n\t\t\tbuild: { logs: buildLogs, lineCount: buildEntries.length },\n\t\t};\n\n\t\t// Skip the runtime tail when caller asked us to OR when the deploy\n\t\t// never reached the container-start phase (build-time failure).\n\t\t// `startedAt` is set by the API when the deploy row is created, so\n\t\t// it's almost always present; the more reliable container-started\n\t\t// signal is whether status ever passed the build phase.\n\t\tconst hadContainer = deployStatus !== 'pending' && deployStatus !== 'building';\n\t\tif (runtimeLimit > 0 && hadContainer) {\n\t\t\tconst runtimeResp = await ctx.hoststack.services.getRuntimeLogs(\n\t\t\t\tteamId,\n\t\t\t\targs.service_id,\n\t\t\t\t{\n\t\t\t\t\tlines: runtimeLimit,\n\t\t\t\t\t...(startedAt ? { since: startedAt } : {}),\n\t\t\t\t},\n\t\t\t);\n\t\t\tif ('count' in runtimeResp) {\n\t\t\t\t// getRuntimeLogs without countOnly should never return this\n\t\t\t\t// shape; defensive branch for future-proofing.\n\t\t\t\tdata['runtime'] = { logs: '', lineCount: runtimeResp.count };\n\t\t\t} else {\n\t\t\t\tconst runtimeEntries = Array.isArray(runtimeResp.logs)\n\t\t\t\t\t? runtimeResp.logs\n\t\t\t\t\t: typeof runtimeResp.logs === 'string'\n\t\t\t\t\t\t? runtimeResp.logs.split('\\n').map((line) => ({ message: line }))\n\t\t\t\t\t\t: [];\n\t\t\t\tconst runtimeText = runtimeEntries\n\t\t\t\t\t.map((e) =>\n\t\t\t\t\t\ttypeof e === 'object' && 'message' in e ? String(e.message) : String(e),\n\t\t\t\t\t)\n\t\t\t\t\t.join('\\n');\n\t\t\t\tdata['runtime'] = {\n\t\t\t\t\tlogs: runtimeText,\n\t\t\t\t\tlineCount: runtimeEntries.length,\n\t\t\t\t\tsinceWindow: startedAt ?? null,\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\t// Surface anything that might explain a failure — these field names\n\t\t// vary slightly across deploy phases (build, deploy, healthcheck) so\n\t\t// we look at the deploy shape directly rather than hard-coding.\n\t\tconst failureSummary = shape({\n\t\t\tstatus: deployStatus,\n\t\t\terrorMessage:\n\t\t\t\tdeployShape && 'errorMessage' in deployShape\n\t\t\t\t\t? (deployShape as { errorMessage: unknown }).errorMessage\n\t\t\t\t\t: undefined,\n\t\t});\n\t\tdata['summary'] = failureSummary;\n\n\t\treturn respond({\n\t\t\tsummary: `Diagnostic bundle for deploy ${args.deploy_id} (${deployStatus}): ${buildEntries.length} build log line${buildEntries.length === 1 ? '' : 's'}${runtimeLimit > 0 && hadContainer ? ', runtime log tail included' : ''}.`,\n\t\t\tdata,\n\t\t});\n\t},\n});\n","import { z } from 'zod';\nimport type { DnsRecord, DnsRecordType, DnsZone, HostStack } from '@hoststack.dev/sdk';\n\nimport { defineTool } from '../registry.js';\nimport { respond, respondError } from '../lib/respond.js';\nimport { shape } from '../lib/shape.js';\n\n/**\n * DNS-zone + record management. These tools delegate to the SDK's\n * `DnsResource` (`ctx.hoststack.dns`), which wraps the same HTTP routes\n * the dashboard uses — `/api/dns-zones/:teamId/...` — so tenancy, audit\n * logging, PowerDNS sync, and idempotency behavior all match the\n * dashboard exactly. We never bypass the service layer.\n *\n * The HTTP-domain tools (list_domains, add_domain, verify_domain,\n * remove_domain) are about binding a hostname to a service for HTTP\n * routing + Let's Encrypt; they don't touch the underlying DNS zone.\n * Use these tools for actual record CRUD (TXT for verification, A/AAAA\n * for custom routing, MX for email, etc.).\n */\n// Source of truth for record types is `@hoststack/shared`'s\n// `dnsRecordTypeSchema`, surfaced through the SDK's `DnsRecordType`. This array\n// drives the zod input shape (a value, so it can't be the bare type); the\n// `satisfies` clause makes the build fail if it ever drifts from the SDK union.\nconst DNS_RECORD_TYPES = [\n\t'A',\n\t'AAAA',\n\t'CNAME',\n\t'MX',\n\t'TXT',\n\t'NS',\n\t'SRV',\n\t'CAA',\n\t'ALIAS',\n] as const satisfies readonly DnsRecordType[];\n\n// Exhaustiveness guard the other direction: every DnsRecordType must appear in\n// the array above. If the SDK adds a record type, this errors until it's added.\nconst _dnsTypesExhaustive: Record<DnsRecordType, true> = {\n\tA: true,\n\tAAAA: true,\n\tCNAME: true,\n\tMX: true,\n\tTXT: true,\n\tNS: true,\n\tSRV: true,\n\tCAA: true,\n\tALIAS: true,\n};\nvoid _dnsTypesExhaustive;\n\n/**\n * Resolve `{ zone_id?, domain? }` to the zone's `publicId`. Accepts\n * either the zone publicId directly or a domain name (apex or\n * subdomain) and walks parent labels to find the longest-matching\n * hosted zone, same suffix-match semantics as\n * `DnsService.findHostedZoneForDomain` (apps/api/src/services/dns.service.ts:193).\n *\n * Returns the zone's publicId so the caller can address subsequent\n * routes by it, plus a human-readable hint about which zone matched.\n */\nasync function resolveZonePublicId(\n\thoststack: HostStack,\n\tteamId: number,\n\tinput: { zone_id?: string; domain?: string },\n): Promise<{ publicId: string; domainName: string }> {\n\tif (input.zone_id) {\n\t\tconst { zones } = await hoststack.dns.listZones(teamId);\n\t\tconst match = zones.find((z) => z.publicId === input.zone_id);\n\t\tif (!match) {\n\t\t\tthrow new Error(`Zone ${input.zone_id} not found on this team.`);\n\t\t}\n\t\treturn { publicId: match.publicId, domainName: match.domainName };\n\t}\n\tif (!input.domain) {\n\t\tthrow new Error('Provide either zone_id or domain.');\n\t}\n\tconst { zones } = await hoststack.dns.listZones(teamId);\n\tconst fqdn = input.domain.toLowerCase().replace(/\\.$/, '');\n\tconst labels = fqdn.split('.');\n\tfor (let i = 0; i < labels.length - 1; i++) {\n\t\tconst candidate = labels.slice(i).join('.');\n\t\tconst match = zones.find((z) => z.domainName.toLowerCase() === candidate);\n\t\tif (match && match.status !== 'deleting') {\n\t\t\treturn { publicId: match.publicId, domainName: match.domainName };\n\t\t}\n\t}\n\tthrow new Error(\n\t\t`No hosted zone matches ${input.domain}. Create the zone in the dashboard (Domains → DNS) first, then retry.`,\n\t);\n}\n\ndefineTool({\n\tname: 'list_dns_zones',\n\tcategory: 'dns',\n\tdescription: [\n\t\t\"List authoritative DNS zones the team owns on HostStack's PowerDNS infrastructure. Each zone is the apex domain (e.g. example.com) under which DNS records live.\",\n\t\t'',\n\t\t'When to use: discover which apex domains support record management before calling create_dns_record / list_dns_records. The dashboard equivalent is Domains → DNS.',\n\t\t'',\n\t\t'Returns: { items: Zone[] } — each zone exposes publicId, domainName, status (\"active\" | \"syncing\" | \"failed\" | \"deleting\"), nsRecords (the nameservers the parent registry must delegate to), provider, createdAt.',\n\t\t'',\n\t\t'Example: list_dns_zones() → { items: [{ publicId: \"dnz_abc\", domainName: \"micci.dk\", status: \"active\", nsRecords: [\"ns1.hoststack.dev\",\"ns2.hoststack.dev\"] }] }',\n\t].join('\\n'),\n\tinput: {},\n\thandler: async (_args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.dns.listZones(teamId);\n\t\tconst items = Array.isArray(response.zones) ? response.zones.map(shape) : [];\n\t\tconst summary =\n\t\t\titems.length === 0\n\t\t\t\t? 'No DNS zones hosted on this team. Create one in the dashboard (Domains → DNS).'\n\t\t\t\t: `Found ${items.length} hosted DNS zone${items.length === 1 ? '' : 's'}.`;\n\t\treturn respond({ summary, data: { items } });\n\t},\n});\n\ndefineTool({\n\tname: 'list_dns_records',\n\tcategory: 'dns',\n\tdescription: [\n\t\t'List every active record in a hosted DNS zone. Records are addressed by their type + name + value tuple; PowerDNS gathers records with the same (name, type) into an RRset on the wire.',\n\t\t'',\n\t\t'When to use: audit what is currently published for a zone before editing, verify a recently-created record landed, or read existing TXT verification strings.',\n\t\t'',\n\t\t'Provide EITHER zone_id (the zone publicId, e.g. \"dnz_abc\") OR domain (an apex or subdomain — the longest-matching hosted apex wins; \"app.example.com\" matches a hosted \"example.com\" zone).',\n\t\t'',\n\t\t'Returns: { zone: { publicId, domainName }, items: Record[] } — each record includes publicId, type, name (\"@\" for apex, or label), value, ttl, priority?, status (\"active\" | \"syncing\" | \"failed\"), managedBy (\"user\" | \"hoststack\" | \"poststack\"), createdAt.',\n\t\t'',\n\t\t'Example: list_dns_records({ domain: \"micci.dk\" }) → { zone: { domainName: \"micci.dk\", … }, items: [{ type: \"TXT\", name: \"@\", value: \"google-site-verification=…\", ttl: 300, … }] }',\n\t].join('\\n'),\n\tinput: {\n\t\tzone_id: z.string().optional().describe('Zone publicId (e.g. \"dnz_abc\").'),\n\t\tdomain: z\n\t\t\t.string()\n\t\t\t.optional()\n\t\t\t.describe('Apex or subdomain — resolves to the longest-matching hosted zone.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst zoneInput: { zone_id?: string; domain?: string } = {};\n\t\tif (args.zone_id !== undefined) zoneInput.zone_id = args.zone_id;\n\t\tif (args.domain !== undefined) zoneInput.domain = args.domain;\n\t\tconst zone = await resolveZonePublicId(ctx.hoststack, teamId, zoneInput);\n\t\tconst response = await ctx.hoststack.dns.listRecords(teamId, zone.publicId);\n\t\tconst items = Array.isArray(response.records) ? response.records.map(shape) : [];\n\t\tconst summary =\n\t\t\titems.length === 0\n\t\t\t\t? `Zone ${zone.domainName} has no records yet.`\n\t\t\t\t: `Returned ${items.length} record${items.length === 1 ? '' : 's'} for ${zone.domainName}.`;\n\t\treturn respond({\n\t\t\tsummary,\n\t\t\tdata: { zone: { publicId: zone.publicId, domainName: zone.domainName }, items },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'get_dns_record',\n\tcategory: 'dns',\n\tdescription: [\n\t\t'Fetch a single DNS record by its publicId. Useful for confirming the record landed after create_dns_record / update_dns_record (status transitions from \"syncing\" to \"active\" once PowerDNS acknowledges).',\n\t\t'',\n\t\t'When to use: verify a record exists, inspect its current ttl/value, or check the sync status of an in-flight write.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - record_id: record publicId (e.g. \"dnr_abc\"). Returned by list_dns_records and create_dns_record.',\n\t\t'',\n\t\t'Returns: { record: Record }. Mirrors the shape returned by list_dns_records.',\n\t\t'',\n\t\t'Example: get_dns_record({ record_id: \"dnr_abc\" }) → { record: { type: \"TXT\", name: \"@\", value: \"…\", status: \"active\", … } }',\n\t].join('\\n'),\n\tinput: {\n\t\trecord_id: z.string().describe('Record publicId.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\t// Records have no direct GET-by-id route — walk the team's zones\n\t\t// and scan each for the matching publicId. Zones-per-team is tiny\n\t\t// (≤ a few dozen even on heavy users) so this is cheap.\n\t\tconst { zones } = await ctx.hoststack.dns.listZones(teamId);\n\t\tfor (const zone of zones) {\n\t\t\tconst { records } = await ctx.hoststack.dns.listRecords(teamId, zone.publicId);\n\t\t\tconst match = records.find((r) => r.publicId === args.record_id);\n\t\t\tif (match) {\n\t\t\t\treturn respond({\n\t\t\t\t\tsummary: `Record ${match.type} ${match.name} on ${zone.domainName}.`,\n\t\t\t\t\tdata: {\n\t\t\t\t\t\tzone: { publicId: zone.publicId, domainName: zone.domainName },\n\t\t\t\t\t\trecord: shape(match),\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\treturn respondError(`Record ${args.record_id} not found on any zone owned by this team.`);\n\t},\n});\n\ndefineTool({\n\tname: 'create_dns_record',\n\tcategory: 'dns',\n\tdescription: [\n\t\t'Create a DNS record on a hosted zone. Writes to the DB first, then mirrors to PowerDNS — record returns with status=\"syncing\" until the provider acknowledges, then flips to \"active\". Re-creating the same (zone, type, name, value) tuple is idempotent and returns the existing row.',\n\t\t'',\n\t\t'When to use: add TXT verification records (Google Search Console, domain ownership challenges, DKIM), A/AAAA records pointing a subdomain at an IP, CNAME aliases, MX records, etc.',\n\t\t'',\n\t\t'Provide EITHER zone_id (zone publicId) OR domain (resolves via longest-suffix match against hosted zones).',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - type: \"A\" | \"AAAA\" | \"CNAME\" | \"MX\" | \"TXT\" | \"NS\" | \"SRV\" | \"CAA\" | \"ALIAS\".',\n\t\t' - name: \"@\" for the zone apex, or a subdomain label (\"www\", \"api\", \"_acme-challenge\"). Do NOT include the zone suffix — the backend prepends it. \"bounce\" on zone \"micci.dk\" publishes \"bounce.micci.dk\"; passing \"bounce.micci.dk\" works (the backend strips the suffix) but is non-idiomatic.',\n\t\t' - content: the literal record value. For TXT pass the unquoted string — the PowerDNS layer handles quoting. For CNAME / MX / NS / SRV / ALIAS the target may be passed with or without a trailing dot; the adapter FQDN-izes it.',\n\t\t' - ttl (optional): 60–86400 seconds, default 3600.',\n\t\t' - priority (optional, required for MX and SRV): 0–65535.',\n\t\t'',\n\t\t'Returns: { record: Record } — see get_dns_record for the shape.',\n\t\t'',\n\t\t'Notes:',\n\t\t' - CNAME at the apex is rejected (RFC 1034 forbids CNAME coexisting with NS/SOA). Use type=\"ALIAS\" for apex aliasing.',\n\t\t'',\n\t\t'Example: create_dns_record({ domain: \"micci.dk\", type: \"TXT\", name: \"@\", content: \"google-site-verification=C0V0scK48g2…\", ttl: 300 })',\n\t].join('\\n'),\n\tinput: {\n\t\tzone_id: z.string().optional().describe('Zone publicId.'),\n\t\tdomain: z.string().optional().describe('Apex or subdomain to resolve a hosted zone.'),\n\t\ttype: z.enum(DNS_RECORD_TYPES).describe('DNS record type.'),\n\t\tname: z\n\t\t\t.string()\n\t\t\t.min(1)\n\t\t\t.max(253)\n\t\t\t.describe('\"@\" for apex, or a bare label. Do NOT include the zone suffix.'),\n\t\tcontent: z.string().min(1).max(2000).describe('Record value; TXT values are unquoted.'),\n\t\tttl: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(60)\n\t\t\t.max(86_400)\n\t\t\t.optional()\n\t\t\t.describe('TTL in seconds (default 3600).'),\n\t\tpriority: z.number().int().min(0).max(65_535).optional().describe('Required for MX / SRV.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\t// MX/SRV records carry priority on the wire; the shared upsert\n\t\t// schema enforces this with a superRefine. The MCP `input` shape\n\t\t// is a flat ZodRawShape so we can't express cross-field rules\n\t\t// there — surface a clean error from the handler instead of\n\t\t// letting the backend's 400 bubble up.\n\t\tif ((args.type === 'MX' || args.type === 'SRV') && args.priority === undefined) {\n\t\t\treturn respondError(`Priority is required for ${args.type} records (0–65535).`);\n\t\t}\n\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst zoneInput: { zone_id?: string; domain?: string } = {};\n\t\tif (args.zone_id !== undefined) zoneInput.zone_id = args.zone_id;\n\t\tif (args.domain !== undefined) zoneInput.domain = args.domain;\n\t\tconst zone = await resolveZonePublicId(ctx.hoststack, teamId, zoneInput);\n\n\t\tconst body: {\n\t\t\ttype: DnsRecordType;\n\t\t\tname: string;\n\t\t\tvalue: string;\n\t\t\tttl?: number;\n\t\t\tpriority?: number;\n\t\t} = {\n\t\t\ttype: args.type,\n\t\t\tname: args.name,\n\t\t\tvalue: args.content,\n\t\t};\n\t\tif (args.ttl !== undefined) body.ttl = args.ttl;\n\t\tif (args.priority !== undefined) body.priority = args.priority;\n\n\t\tconst response = await ctx.hoststack.dns.createRecord(teamId, zone.publicId, body);\n\t\treturn respond({\n\t\t\tsummary: `Created ${args.type} ${args.name} on ${zone.domainName}.`,\n\t\t\tdata: {\n\t\t\t\tzone: { publicId: zone.publicId, domainName: zone.domainName },\n\t\t\t\trecord: shape(response.record),\n\t\t\t},\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'update_dns_record',\n\tcategory: 'dns',\n\tdescription: [\n\t\t'Update an existing DNS record by its publicId. Use this to change a TTL, swap a value, or move the record to a different (name, type) bucket. Full-record PUT semantics: every mutable field must be provided.',\n\t\t'',\n\t\t'When to use: rotate a DKIM key, repoint an A record at a new IP, bump TTL ahead of a planned change.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - record_id: record publicId.',\n\t\t' - type, name, content: the new values (the route is full-replace, not patch — fetch with get_dns_record first if you only want to tweak one field).',\n\t\t' - ttl (optional), priority (optional, required for MX / SRV).',\n\t\t'',\n\t\t'Returns: { record: Record } reflecting the new state.',\n\t\t'',\n\t\t'Example: update_dns_record({ record_id: \"dnr_abc\", type: \"A\", name: \"api\", content: \"203.0.113.42\", ttl: 300 })',\n\t].join('\\n'),\n\tinput: {\n\t\trecord_id: z.string().describe('Record publicId.'),\n\t\ttype: z.enum(DNS_RECORD_TYPES).describe('DNS record type.'),\n\t\tname: z\n\t\t\t.string()\n\t\t\t.min(1)\n\t\t\t.max(253)\n\t\t\t.describe('\"@\" for apex, or a bare label. Do NOT include the zone suffix.'),\n\t\tcontent: z.string().min(1).max(2000).describe('Record value; TXT unquoted.'),\n\t\tttl: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(60)\n\t\t\t.max(86_400)\n\t\t\t.optional()\n\t\t\t.describe('TTL in seconds (default 3600).'),\n\t\tpriority: z.number().int().min(0).max(65_535).optional().describe('Required for MX / SRV.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tif ((args.type === 'MX' || args.type === 'SRV') && args.priority === undefined) {\n\t\t\treturn respondError(`Priority is required for ${args.type} records (0–65535).`);\n\t\t}\n\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst target = await findRecordZone(ctx.hoststack, teamId, args.record_id);\n\t\tif (!target) {\n\t\t\treturn respondError(\n\t\t\t\t`Record ${args.record_id} not found on any zone owned by this team.`,\n\t\t\t);\n\t\t}\n\n\t\tconst body: {\n\t\t\ttype: DnsRecordType;\n\t\t\tname: string;\n\t\t\tvalue: string;\n\t\t\tttl?: number;\n\t\t\tpriority?: number;\n\t\t} = {\n\t\t\ttype: args.type,\n\t\t\tname: args.name,\n\t\t\tvalue: args.content,\n\t\t};\n\t\tif (args.ttl !== undefined) body.ttl = args.ttl;\n\t\tif (args.priority !== undefined) body.priority = args.priority;\n\n\t\tconst response = await ctx.hoststack.dns.updateRecord(\n\t\t\tteamId,\n\t\t\ttarget.zone.publicId,\n\t\t\targs.record_id,\n\t\t\tbody,\n\t\t);\n\t\treturn respond({\n\t\t\tsummary: `Updated ${args.type} ${args.name} on ${target.zone.domainName}.`,\n\t\t\tdata: {\n\t\t\t\tzone: {\n\t\t\t\t\tpublicId: target.zone.publicId,\n\t\t\t\t\tdomainName: target.zone.domainName,\n\t\t\t\t},\n\t\t\t\trecord: shape(response.record),\n\t\t\t},\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'delete_dns_record',\n\tcategory: 'dns',\n\tdescription: [\n\t\t'Soft-delete a DNS record and remove it from PowerDNS. DESTRUCTIVE: resolvers stop returning the value as soon as the change propagates. Same code path as the dashboard delete button — the row is marked deleted and the RRset is re-pushed without it.',\n\t\t'',\n\t\t'When to use: retire a TXT verification record once the challenge has been confirmed; drop an A record for a decommissioned subdomain.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - record_id: record publicId.',\n\t\t'',\n\t\t'Returns: { ok: true }.',\n\t\t'',\n\t\t'Example: delete_dns_record({ record_id: \"dnr_abc\" }) → { ok: true }',\n\t].join('\\n'),\n\tinput: {\n\t\trecord_id: z.string().describe('Record publicId.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst target = await findRecordZone(ctx.hoststack, teamId, args.record_id);\n\t\tif (!target) {\n\t\t\treturn respondError(\n\t\t\t\t`Record ${args.record_id} not found on any zone owned by this team.`,\n\t\t\t);\n\t\t}\n\t\tawait ctx.hoststack.dns.deleteRecord(teamId, target.zone.publicId, args.record_id);\n\t\treturn respond({\n\t\t\tsummary: `Deleted record ${args.record_id} from ${target.zone.domainName}.`,\n\t\t\tdata: { ok: true },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'resync_dns_record',\n\tcategory: 'dns',\n\tdescription: [\n\t\t'Re-push a DNS record to PowerDNS without changing its value. Recovers a record stuck at status=\"failed\" after a transient provider outage — including managedBy=\"hoststack\" records (auto-created for a service/domain) that create/update/delete refuse to touch.',\n\t\t'',\n\t\t'When to use: a record shows status=\"failed\" with a lastSyncError, or a freshly-attached domain\\'s auto-created A record never went active. Safe and idempotent — a rejected re-push leaves the live RRset untouched (PowerDNS PATCH is atomic), so this can never drop a working record.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - record_id: record publicId (e.g. \"dnr_abc\").',\n\t\t'',\n\t\t'Returns: { record: Record } — the record with its refreshed status (\"active\" on success).',\n\t\t'',\n\t\t'Example: resync_dns_record({ record_id: \"dnr_abc\" }) → { record: { status: \"active\", ... } }',\n\t].join('\\n'),\n\tinput: {\n\t\trecord_id: z.string().describe('Record publicId.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst target = await findRecordZone(ctx.hoststack, teamId, args.record_id);\n\t\tif (!target) {\n\t\t\treturn respondError(\n\t\t\t\t`Record ${args.record_id} not found on any zone owned by this team.`,\n\t\t\t);\n\t\t}\n\t\tconst response = await ctx.hoststack.dns.resyncRecord(\n\t\t\tteamId,\n\t\t\ttarget.zone.publicId,\n\t\t\targs.record_id,\n\t\t);\n\t\treturn respond({\n\t\t\tsummary: `Re-synced record ${args.record_id} on ${target.zone.domainName} (status: ${response.record.status}).`,\n\t\t\tdata: {\n\t\t\t\tzone: {\n\t\t\t\t\tpublicId: target.zone.publicId,\n\t\t\t\t\tdomainName: target.zone.domainName,\n\t\t\t\t},\n\t\t\t\trecord: shape(response.record),\n\t\t\t},\n\t\t});\n\t},\n});\n\n/**\n * The DNS HTTP routes nest records under their zone, so update/delete\n * by record publicId needs the owning zone's publicId too. Walk the\n * team's zones until we find a record match. Cheap — zone count is\n * bounded by team-tier limits.\n */\nasync function findRecordZone(\n\thoststack: HostStack,\n\tteamId: number,\n\trecordPublicId: string,\n): Promise<{ zone: DnsZone; record: DnsRecord } | null> {\n\tconst { zones } = await hoststack.dns.listZones(teamId);\n\tfor (const zone of zones) {\n\t\tconst { records } = await hoststack.dns.listRecords(teamId, zone.publicId);\n\t\tconst match = records.find((r) => r.publicId === recordPublicId);\n\t\tif (match) return { zone, record: match };\n\t}\n\treturn null;\n}\n","import { z } from 'zod';\n\nimport { defineTool } from '../registry.js';\nimport { respond } from '../lib/respond.js';\nimport { shapeDomain, shapeList } from '../lib/shape.js';\n\ndefineTool({\n\tname: 'list_domains',\n\tcategory: 'domains',\n\tdescription: [\n\t\t\"List every custom domain attached to the team's services.\",\n\t\t'',\n\t\t'When to use: enumerate live domains, audit DNS verification status, or find which service a hostname resolves to before troubleshooting routing.',\n\t\t'',\n\t\t'Returns: { items: Domain[] } — id, publicId, hostname, serviceId, verified, dnsTargets, sslStatus, isPrimary, createdAt.',\n\t\t'',\n\t\t\"`isPrimary` is the one the platform treats as the service's address: what `${service.url}` resolves to, what its uptime check probes, and what the monitoring page shows. When NO domain on a service is primary — the default, since nothing sets it automatically — that choice falls to the OLDEST domain on the service, which on a renamed host is usually the retired alias rather than the canonical name. Nominate one with update_domain.\",\n\t\t'',\n\t\t'Example: list_domains() → { items: [{ hostname: \"api.example.com\", verified: true, sslStatus: \"active\", isPrimary: false, … }] }',\n\t].join('\\n'),\n\tinput: {},\n\thandler: async (_args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.domains.list(teamId);\n\t\tconst data = shapeList(response, 'domains', shapeDomain);\n\t\tconst summary =\n\t\t\tdata.items.length === 0\n\t\t\t\t? 'No custom domains configured.'\n\t\t\t\t: `Found ${data.items.length} custom domain${data.items.length === 1 ? '' : 's'}.`;\n\t\treturn respond({ summary, data });\n\t},\n});\n\ndefineTool({\n\tname: 'add_domain',\n\tcategory: 'domains',\n\tdescription: [\n\t\t'Attach a custom hostname to the team — optionally pinned to a specific service. After adding, point your DNS at the targets returned and call verify_domain.',\n\t\t'',\n\t\t'When to use: the user wants to put a custom domain in front of a HostStack service (e.g. point api.example.com at svc_abc). The hostname must be unique across the team.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - hostname: the domain to add (e.g. \"api.example.com\").',\n\t\t' - service_id: publicId of the service to bind the domain to.',\n\t\t' - path_prefix (optional): when set, only requests under this URL prefix route to this service. Use for path-based fan-out (e.g. `/api` to an api service, `/` to a web service on the same hostname).',\n\t\t'',\n\t\t'Returns: { domain: Domain } — includes dnsTargets you must configure (CNAME / A records).',\n\t\t'',\n\t\t'Example: add_domain({ hostname: \"api.example.com\", service_id: \"svc_abc\" }) → { domain: { hostname: \"api.example.com\", dnsTargets: [...], verified: false, … } }',\n\t].join('\\n'),\n\tinput: {\n\t\thostname: z\n\t\t\t.string()\n\t\t\t.min(3)\n\t\t\t.max(253)\n\t\t\t.describe('Fully-qualified hostname (e.g. api.example.com).'),\n\t\tservice_id: z.string().describe('Service publicId to bind to.'),\n\t\tpath_prefix: z\n\t\t\t.string()\n\t\t\t.optional()\n\t\t\t.describe('Optional URL prefix for path-based routing (e.g. \"/api\").'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst input: { domain: string; serviceId: string; pathPrefix?: string } = {\n\t\t\tdomain: args.hostname,\n\t\t\tserviceId: args.service_id,\n\t\t};\n\t\tif (args.path_prefix !== undefined) input.pathPrefix = args.path_prefix;\n\t\tconst response = await ctx.hoststack.domains.add(teamId, input);\n\t\t// When HostStack hosts the zone it auto-creates the A record; if that\n\t\t// write can't reach the provider the API returns the domain plus a\n\t\t// dnsSyncWarning. Relay it so the caller doesn't read the bare pending\n\t\t// status as success. The record recovers via the reconcile sweep or a\n\t\t// manual resync_dns_record call.\n\t\tconst dnsSyncWarning = (response.domain as { dnsSyncWarning?: string }).dnsSyncWarning;\n\t\tconst data = dnsSyncWarning\n\t\t\t? { domain: shapeDomain(response.domain), dnsSyncWarning }\n\t\t\t: { domain: shapeDomain(response.domain) };\n\t\treturn respond({\n\t\t\tsummary: dnsSyncWarning\n\t\t\t\t? `Added domain ${args.hostname}, but its auto-created DNS record hasn't synced yet — ${dnsSyncWarning}`\n\t\t\t\t: `Added domain ${args.hostname}. Configure DNS, then call verify_domain.`,\n\t\t\tdata,\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'verify_domain',\n\tcategory: 'domains',\n\tdescription: [\n\t\t'Trigger DNS verification for a previously-added domain. HostStack checks that the dnsTargets returned by add_domain are configured at the registrar.',\n\t\t'',\n\t\t'When to use: the user has set up DNS records and is ready to flip the domain live. Returns success when verification passes; the domain stays in pending state if DNS is still propagating.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - domain_id: publicId of the domain (from list_domains or add_domain).',\n\t\t'',\n\t\t'Returns: { ok: true }. Re-call list_domains to inspect the updated verified flag and SSL status.',\n\t\t'',\n\t\t'Example: verify_domain({ domain_id: \"dom_xyz\" }) → { ok: true }',\n\t].join('\\n'),\n\tinput: {\n\t\tdomain_id: z.string().describe('Domain publicId.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tawait ctx.hoststack.domains.verify(teamId, args.domain_id);\n\t\treturn respond({\n\t\t\tsummary: `Triggered DNS verification for ${args.domain_id}.`,\n\t\t\tdata: { ok: true },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'update_domain',\n\tcategory: 'domains',\n\tdescription: [\n\t\t\"Change a domain's settings — most usefully, nominate it as the service's PRIMARY hostname.\",\n\t\t'',\n\t\t'When to use: a service answers on more than one hostname and you need to say which one is canonical — before setting an uptime check on it, after renaming a storefront, or when `${service.url}` is resolving to the wrong name. Also for toggling https on a domain or turning one into a redirect.',\n\t\t'',\n\t\t'WHY PRIMARY MATTERS. A service can answer on several hostnames that behave differently — the canonical one serves the page, a retired alias 301s to it — and three parts of the platform have to pick ONE of them: the address a service advertises to itself in `${service.url}`, the host its uptime check probes, and the hostname shown on the monitoring page. All three pick the primary, and fall back to the OLDEST domain when no primary is nominated. So a service with two hostnames and no primary has its uptime check pointed at whichever domain was added first, which may well be the alias that redirects — and `set_uptime_check` takes a path, never a host, so nominating the primary here is the only way to say which name is checked.',\n\t\t'',\n\t\t'One primary per service: promoting a domain demotes its sibling in the same write, so there is never a pair to choose between.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - domain_id: publicId of the domain (from list_domains).',\n\t\t\" - isPrimary: make this the service's primary hostname.\",\n\t\t' - sslEnabled: serve it over https. Applies on the next deploy, which this triggers.',\n\t\t' - redirectTo: send every request to an absolute http(s) URL instead of the service. Pass null to clear.',\n\t\t'',\n\t\t'Returns: { domain } — the updated row, including isPrimary.',\n\t\t'',\n\t\t'Example: update_domain({ domain_id: \"dom_xyz\", isPrimary: true }) → { domain: { hostname: \"shop.example.com\", isPrimary: true } }',\n\t].join('\\n'),\n\tinput: {\n\t\tdomain_id: z.string().describe('Domain publicId.'),\n\t\tisPrimary: z.boolean().optional(),\n\t\tsslEnabled: z.boolean().optional(),\n\t\tredirectTo: z\n\t\t\t.string()\n\t\t\t.max(2000)\n\t\t\t.nullable()\n\t\t\t.optional()\n\t\t\t.describe('Absolute http(s) URL, or null to clear.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst { domain_id: domainId, ...patch } = args;\n\t\tconst body = Object.fromEntries(\n\t\t\tObject.entries(patch).filter(([, v]) => v !== undefined),\n\t\t) as Parameters<typeof ctx.hoststack.domains.update>[2];\n\t\tif (Object.keys(body).length === 0) {\n\t\t\treturn respond({\n\t\t\t\tsummary: 'Nothing to change — pass isPrimary, sslEnabled or redirectTo.',\n\t\t\t\tdata: { ok: false },\n\t\t\t});\n\t\t}\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.domains.update(teamId, domainId, body);\n\t\tconst domain = shapeDomain(response.domain);\n\t\treturn respond({\n\t\t\tsummary:\n\t\t\t\tbody.isPrimary === true\n\t\t\t\t\t? `${String(domain.hostname ?? domainId)} is now the primary hostname for its service — it is what \\`\\${service.url}\\` resolves to and what the uptime check probes.`\n\t\t\t\t\t: `Updated ${String(domain.hostname ?? domainId)}.`,\n\t\t\tdata: { domain },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'remove_domain',\n\tcategory: 'domains',\n\tdescription: [\n\t\t'Detach a custom hostname from the team. DESTRUCTIVE: the domain stops routing immediately and any pinned service must fall back to its default hostname.',\n\t\t'',\n\t\t'When to use: the user wants to retire a custom domain. Confirm with the user first — the change is immediate and cannot be undone except by re-adding the domain and re-verifying DNS.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - domain_id: publicId of the domain to remove.',\n\t\t'',\n\t\t'Returns: { ok: true }.',\n\t\t'',\n\t\t'Example: remove_domain({ domain_id: \"dom_xyz\" }) → { ok: true }',\n\t].join('\\n'),\n\tinput: {\n\t\tdomain_id: z.string().describe('Domain publicId.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tawait ctx.hoststack.domains.remove(teamId, args.domain_id);\n\t\treturn respond({ summary: `Removed domain ${args.domain_id}.`, data: { ok: true } });\n\t},\n});\n","import { z } from 'zod';\n\nimport { defineTool } from '../registry.js';\nimport { respond, respondError } from '../lib/respond.js';\nimport { shapeEnvVar, shapeList } from '../lib/shape.js';\n\ninterface ExistingEnvVar {\n\tid: number;\n\tpublicId?: string;\n\tkey: string;\n\tisSecret: boolean;\n}\n\ndefineTool({\n\tname: 'list_env_vars',\n\tcategory: 'env-vars',\n\tdescription: [\n\t\t'List environment variables for a service. Secret values are masked server-side (\"••••••\"); only non-secret values come through in the clear.',\n\t\t'',\n\t\t'When to use: auditing what configuration a service has, finding the key for a value the user mentions by name, or confirming a variable was set after a write. Never assume the value field is plaintext for secret rows — it is masked.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t'',\n\t\t'Returns: { items: EnvVar[] } — id, publicId, key, value (masked if isSecret), isSecret, target, linkedDatabaseId, createdAt.',\n\t\t'',\n\t\t'Example: list_env_vars({ service_id: \"svc_abc\" }) → { items: [{ key: \"DATABASE_URL\", value: \"••••••\", isSecret: true }, { key: \"PORT\", value: \"3000\", isSecret: false }] }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.envVars.list(teamId, args.service_id);\n\t\tconst data = shapeList(response, 'envVars', shapeEnvVar);\n\t\tconst summary =\n\t\t\tdata.items.length === 0\n\t\t\t\t? `No env vars set on service ${args.service_id}.`\n\t\t\t\t: `Found ${data.items.length} env var${data.items.length === 1 ? '' : 's'} on service ${args.service_id}.`;\n\t\treturn respond({ summary, data });\n\t},\n});\n\ndefineTool({\n\tname: 'set_env_var',\n\tcategory: 'env-vars',\n\tdescription: [\n\t\t'Upsert a single environment variable on a service: creates the key if missing, updates the value if it already exists. Match is by key (case-sensitive).',\n\t\t'',\n\t\t'When to use: rotate a secret, add a new config knob, or change a value the user dictated. The MCP layer looks up the existing var by key first; you do not need to know the env-var ID.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t' - key: env-var name (e.g. \"DATABASE_URL\").',\n\t\t' - value: new value (will be encrypted at rest if is_secret=true).',\n\t\t' - is_secret (optional): true marks the value as secret (masked on read). On create, defaults to true for safety. On update, omitting it leaves the existing flag untouched — pass it explicitly only when you want to change classification.',\n\t\t' - target (optional): where the var is injected — \"build\", \"runtime\", or \"both\". On create, defaults to \"both\". On update, omitting it leaves the existing target untouched.',\n\t\t' IMPORTANT for build-time values (a frontend VITE_*/NEXT_PUBLIC_*, anything a bundler inlines): \"both\" and \"build\" reach the build, but a SECRET var never does on \"both\" — build args are baked into image history. Since is_secret defaults to TRUE here, set is_secret:false for a public build-time value or it will silently be missing from the built artifact. Use \"build\" to force a secret into the build anyway.',\n\t\t'',\n\t\t'Returns: { envVar: EnvVar, action: \"created\" | \"updated\" }.',\n\t\t'',\n\t\t'Example: set_env_var({ service_id: \"svc_abc\", key: \"DATABASE_URL\", value: \"postgres://…\", is_secret: true }) → { envVar: { key: \"DATABASE_URL\", value: \"••••••\", … }, action: \"updated\" }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t\tkey: z.string().min(1).max(128).describe('Env-var key.'),\n\t\tvalue: z.string().describe('New value.'),\n\t\tis_secret: z\n\t\t\t.boolean()\n\t\t\t.optional()\n\t\t\t.describe('Mark as secret (encrypted, masked on read). Default true.'),\n\t\ttarget: z\n\t\t\t.enum(['build', 'runtime', 'both'])\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t'Injection target: build, runtime, or both. Default both. A secret var on \"both\" is runtime-only (build args land in image history) — pass is_secret:false for a public build-time value, or use \"build\" to force it.',\n\t\t\t),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst existing = await ctx.hoststack.envVars.list(teamId, args.service_id);\n\t\tconst match = (existing.envVars as ExistingEnvVar[]).find((v) => v.key === args.key);\n\n\t\tif (match) {\n\t\t\t// On update, only forward `isSecret`/`target` when the caller\n\t\t\t// passed them explicitly. Defaulting `isSecret` to `true` here\n\t\t\t// would silently flip previously-plaintext keys (PORT, NODE_ENV,\n\t\t\t// …) to secret on every value change.\n\t\t\tconst updatePayload: {\n\t\t\t\tvalue: string;\n\t\t\t\tisSecret?: boolean;\n\t\t\t\ttarget?: 'build' | 'runtime' | 'both';\n\t\t\t} = { value: args.value };\n\t\t\tif (args.is_secret !== undefined) updatePayload.isSecret = args.is_secret;\n\t\t\tif (args.target !== undefined) updatePayload.target = args.target;\n\t\t\tconst response = await ctx.hoststack.envVars.update(\n\t\t\t\tteamId,\n\t\t\t\targs.service_id,\n\t\t\t\tString(match.id),\n\t\t\t\tupdatePayload,\n\t\t\t);\n\t\t\tconst data = {\n\t\t\t\tenvVar: shapeEnvVar(response.envVar),\n\t\t\t\taction: 'updated' as const,\n\t\t\t};\n\t\t\treturn respond({ summary: `Updated ${args.key} on service ${args.service_id}.`, data });\n\t\t}\n\n\t\tconst response = await ctx.hoststack.envVars.create(teamId, args.service_id, {\n\t\t\tkey: args.key,\n\t\t\tvalue: args.value,\n\t\t\tisSecret: args.is_secret ?? true,\n\t\t\t...(args.target !== undefined ? { target: args.target } : {}),\n\t\t});\n\t\tconst data = {\n\t\t\tenvVar: shapeEnvVar(response.envVar),\n\t\t\taction: 'created' as const,\n\t\t};\n\t\treturn respond({ summary: `Created ${args.key} on service ${args.service_id}.`, data });\n\t},\n});\n\ndefineTool({\n\tname: 'delete_env_var',\n\tcategory: 'env-vars',\n\tdescription: [\n\t\t'Remove an environment variable from a service by key (case-sensitive).',\n\t\t'',\n\t\t'When to use: cleaning up unused config, or rotating away from a leaked secret. The MCP looks up the env-var ID from the key automatically.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t' - key: env-var name to delete.',\n\t\t'',\n\t\t'Returns: { ok: true } on success. Returns an error if the key was not found.',\n\t\t'',\n\t\t'Example: delete_env_var({ service_id: \"svc_abc\", key: \"OLD_FLAG\" }) → { ok: true }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t\tkey: z.string().min(1).max(128).describe('Env-var key to delete.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst existing = await ctx.hoststack.envVars.list(teamId, args.service_id);\n\t\tconst match = (existing.envVars as ExistingEnvVar[]).find((v) => v.key === args.key);\n\t\tif (!match) {\n\t\t\treturn respondError(`Env var \"${args.key}\" not found on service ${args.service_id}.`, {\n\t\t\t\tkey: args.key,\n\t\t\t\tservice_id: args.service_id,\n\t\t\t});\n\t\t}\n\t\tawait ctx.hoststack.envVars.delete(teamId, args.service_id, String(match.id));\n\t\treturn respond({\n\t\t\tsummary: `Deleted ${args.key} from service ${args.service_id}.`,\n\t\t\tdata: { ok: true },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'bulk_set_env_vars',\n\tcategory: 'env-vars',\n\tdescription: [\n\t\t'Replace the entire env-var set on a service with the given list. Equivalent to deleting all current vars and creating the supplied ones — use with care.',\n\t\t'',\n\t\t'When to use: importing a .env file, mirroring config from a sibling service, or doing a clean reset. For incremental changes, use set_env_var per key — bulk_set is destructive for any key not in the supplied list.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t' - env_vars: array of { key, value, is_secret?, target? }. is_secret defaults to FALSE per row (a row is stored in the clear unless you set is_secret:true); target defaults to \"both\". Note this differs from set_env_var, which defaults a newly-created var to secret.',\n\t\t' A var reaches the image BUILD when target is \"build\", or when target is \"both\" and is_secret is false. A secret on \"both\" is runtime-only, because build args are visible in image history.',\n\t\t'',\n\t\t'Returns: { ok: true }. Re-list with list_env_vars to confirm the new state.',\n\t\t'',\n\t\t'Example: bulk_set_env_vars({ service_id: \"svc_abc\", env_vars: [{ key: \"PORT\", value: \"3000\", is_secret: false }, { key: \"DATABASE_URL\", value: \"…\", is_secret: true }] }) → { ok: true }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t\tenv_vars: z\n\t\t\t.array(\n\t\t\t\tz.object({\n\t\t\t\t\tkey: z.string().min(1).max(128),\n\t\t\t\t\tvalue: z.string(),\n\t\t\t\t\tis_secret: z.boolean().optional(),\n\t\t\t\t\ttarget: z.enum(['build', 'runtime', 'both']).optional(),\n\t\t\t\t}),\n\t\t\t)\n\t\t\t.max(500)\n\t\t\t.describe('Array of env-var rows. Hard cap 500.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst payload = {\n\t\t\tvars: args.env_vars.map((v) => {\n\t\t\t\tconst row: {\n\t\t\t\t\tkey: string;\n\t\t\t\t\tvalue: string;\n\t\t\t\t\tisSecret?: boolean;\n\t\t\t\t\ttarget?: 'build' | 'runtime' | 'both';\n\t\t\t\t} = {\n\t\t\t\t\tkey: v.key,\n\t\t\t\t\tvalue: v.value,\n\t\t\t\t};\n\t\t\t\tif (v.is_secret !== undefined) row.isSecret = v.is_secret;\n\t\t\t\tif (v.target !== undefined) row.target = v.target;\n\t\t\t\treturn row;\n\t\t\t}),\n\t\t};\n\t\tawait ctx.hoststack.envVars.bulkSet(teamId, args.service_id, payload);\n\t\treturn respond({\n\t\t\tsummary: `Replaced env-var set on service ${args.service_id} with ${args.env_vars.length} entr${args.env_vars.length === 1 ? 'y' : 'ies'}.`,\n\t\t\tdata: { ok: true, count: args.env_vars.length },\n\t\t});\n\t},\n});\n","import { z } from 'zod';\n\nimport { respond, respondError } from '../lib/respond.js';\nimport { shape, shapeList } from '../lib/shape.js';\nimport { defineTool } from '../registry.js';\n\n/**\n * v66 P5: MCP tools for project environment management.\n *\n * Environments isolate services in the same project — staging vs\n * production, for example. Each project automatically gets a Production\n * environment; users can add staging/development/preview envs to run\n * sibling services side-by-side and promote builds between them.\n */\n\ndefineTool({\n\tname: 'list_environments',\n\tcategory: 'environments',\n\tdescription: [\n\t\t'List every environment for a project (production / staging / development / preview).',\n\t\t'',\n\t\t'When to use: the user wants to see what envs exist before creating a service in one or promoting a deploy. Every project has at least Production.',\n\t\t'',\n\t\t// §2.2: these are project deployment environments, not cloud Dev Boxes — point agents at the\n\t\t// right surface so the two \"dev\" concepts stay distinct (bidirectional cross-reference).\n\t\t'NOTE — these are project deployment environments (Production / Staging / Preview), NOT agentic cloud Dev Boxes. To list Dev Boxes use list_dev_environments; to create one use create_dev_environment / create_standalone_dev_environment / spin_up_dev_environment.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - project_id: project publicId (e.g. \"prj_abc123\").',\n\t\t'',\n\t\t'Returns: { items: Environment[] } — id, publicId, name, type, isDefault, isProtected.',\n\t\t'',\n\t\t'Example: list_environments({ project_id: \"prj_abc\" }) → { items: [{ name: \"Production\", type: \"production\", isDefault: true }, { name: \"Staging\", type: \"staging\" }] }',\n\t].join('\\n'),\n\tinput: {\n\t\tproject_id: z.string().describe('Project publicId.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.environments.list(teamId, args.project_id);\n\t\tconst data = shapeList(response, 'environments', shape);\n\t\tconst summary =\n\t\t\tdata.items.length === 0\n\t\t\t\t? 'No environments yet — every project should have Production by default.'\n\t\t\t\t: `Found ${data.items.length} environment${data.items.length === 1 ? '' : 's'}.`;\n\t\treturn respond({ summary, data });\n\t},\n});\n\ndefineTool({\n\tname: 'create_environment',\n\tcategory: 'environments',\n\tdescription: [\n\t\t'Create a new environment in a project (e.g. Staging, QA, Preview).',\n\t\t'',\n\t\t'When to use: the user wants to add an env so they can run a sibling service alongside production for testing or staging before release.',\n\t\t'',\n\t\t// §2.1/§2.2: steer callers away from the agentic cloud Dev Box tools (which live in\n\t\t// services.ts) — an environment is a deployment target, NOT a Dev Box. Bidirectional with\n\t\t// the cross-references those dev-box tools point back here.\n\t\t'NOTE — this is NOT a cloud Dev Box. An environment is a project deployment target (Production / Staging / Preview); type=\"development\" just gives services a -dev hostname suffix. For an agentic cloud Dev Box (a terminal you drive coding agents in) use create_dev_environment / create_standalone_dev_environment / spin_up_dev_environment instead.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - project_id: project publicId.',\n\t\t' - name: human-readable name (1–64 chars). Shown in the env switcher.',\n\t\t' - type: \"production\" | \"staging\" | \"development\" | \"preview\". Determines the hostname suffix on services in this env (production stays clean; others get -staging / -dev / -preview). type=\"development\" creates a PROJECT deployment environment (a -dev hostname suffix), NOT an agentic Dev Box — for a cloud dev box use create_dev_environment / create_standalone_dev_environment / spin_up_dev_environment.',\n\t\t' - is_protected (optional): require admin role for destructive actions in this env. Default false.',\n\t\t'',\n\t\t'Returns: { environment: Environment }.',\n\t\t'',\n\t\t'Example: create_environment({ project_id: \"prj_abc\", name: \"Staging\", type: \"staging\" }) → { environment: { id: 7, publicId: \"env_…\", name: \"Staging\", type: \"staging\" } }',\n\t].join('\\n'),\n\tinput: {\n\t\tproject_id: z.string().describe('Project publicId.'),\n\t\tname: z.string().min(1).max(64).describe('Environment name (1–64 chars).'),\n\t\ttype: z.enum(['production', 'staging', 'development', 'preview']).describe(\n\t\t\t// §2.1: \"development\" here is a project deployment env, NOT a cloud Dev Box.\n\t\t\t'Environment type — drives the hostname suffix. type=\"development\" creates a PROJECT deployment environment (a -dev hostname suffix), NOT an agentic Dev Box — for a cloud dev box use create_dev_environment / create_standalone_dev_environment / spin_up_dev_environment.',\n\t\t),\n\t\tis_protected: z\n\t\t\t.boolean()\n\t\t\t.optional()\n\t\t\t.describe('Require admin role for destructive actions. Default false.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst input: {\n\t\t\tname: string;\n\t\t\ttype: 'production' | 'staging' | 'development' | 'preview';\n\t\t\tisProtected?: boolean;\n\t\t} = {\n\t\t\tname: args.name,\n\t\t\ttype: args.type,\n\t\t};\n\t\tif (args.is_protected !== undefined) input.isProtected = args.is_protected;\n\t\tconst response = await ctx.hoststack.environments.create(teamId, args.project_id, input);\n\t\tconst data = { environment: shape(response.environment) };\n\t\treturn respond({\n\t\t\tsummary: `Created environment \"${args.name}\" (${args.type}) in project ${args.project_id}.`,\n\t\t\tdata,\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'update_environment',\n\tcategory: 'environments',\n\tdescription: [\n\t\t\"Rename an environment, change whether it is protected, or make it the project's default.\",\n\t\t'',\n\t\t'When to use: correcting a name, turning protection on for an env that has become production-like, or promoting a different env to default. Everything else about an environment is fixed at creation — `type` in particular cannot be changed, because the hostname suffix it drives is already baked into every service in the env.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - project_id: project publicId.',\n\t\t' - environment_id: environment publicId or numeric id.',\n\t\t' - name (optional): new name, 1–64 chars.',\n\t\t' - is_protected (optional): require admin role for destructive actions in this env.',\n\t\t' - is_default (optional): make this the project default. See below.',\n\t\t'',\n\t\t'is_default is a MOVE, not a flag. Setting it true demotes whichever env is currently default, because a project has exactly one — and for the same reason you cannot set it false: that would leave the project with none, and every create that omits an envId falls back to the default, so the next `create_service` would fail with \"Project has no Production environment\". To change which env is default, set is_default true on the one you want; the old one is demoted for you.',\n\t\t'',\n\t\t'Returns: { environment: Environment }.',\n\t\t'',\n\t\t'Example: update_environment({ project_id: \"prj_abc\", environment_id: \"env_xyz\", name: \"Staging EU\" }) → { environment: { publicId: \"env_xyz\", name: \"Staging EU\" } }',\n\t].join('\\n'),\n\tinput: {\n\t\tproject_id: z.string().describe('Project publicId.'),\n\t\tenvironment_id: z\n\t\t\t.union([z.string(), z.number()])\n\t\t\t.describe('Environment publicId or numeric id.'),\n\t\tname: z.string().min(1).max(64).optional().describe('New name (1–64 chars).'),\n\t\tis_protected: z\n\t\t\t.boolean()\n\t\t\t.optional()\n\t\t\t.describe('Require admin role for destructive actions in this env.'),\n\t\tis_default: z\n\t\t\t.boolean()\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t\"Make this the project's default, demoting the current one. Cannot be set false — promote a different env instead.\",\n\t\t\t),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst input: { name?: string; isProtected?: boolean; isDefault?: boolean } = {};\n\t\tif (args.name !== undefined) input.name = args.name;\n\t\tif (args.is_protected !== undefined) input.isProtected = args.is_protected;\n\t\tif (args.is_default !== undefined) input.isDefault = args.is_default;\n\n\t\t// The API answers an empty patch with the unchanged row, so without\n\t\t// this the tool would report a successful update that changed nothing.\n\t\tif (Object.keys(input).length === 0) {\n\t\t\treturn respondError(\n\t\t\t\t'Nothing to update. Pass at least one of name, is_protected or is_default.',\n\t\t\t);\n\t\t}\n\n\t\tconst response = await ctx.hoststack.environments.update(\n\t\t\tteamId,\n\t\t\targs.project_id,\n\t\t\targs.environment_id,\n\t\t\tinput,\n\t\t);\n\t\tconst data = { environment: shape(response.environment) };\n\t\tconst fields = Object.keys(input).join(', ');\n\t\treturn respond({\n\t\t\tsummary: `Updated ${fields} on environment ${response.environment.name}.`,\n\t\t\tdata,\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'delete_environment',\n\tcategory: 'environments',\n\tdescription: [\n\t\t'Delete an environment from a project.',\n\t\t'',\n\t\t'When to use: an env is no longer used (e.g. tearing down a feature branch staging). Blocked when the env still has live services or databases attached — destroy or move them first. The default env (usually Production) cannot be deleted.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - project_id: project publicId.',\n\t\t' - environment_id: environment publicId or numeric id.',\n\t\t'',\n\t\t'Returns: { success: true } on success.',\n\t\t'',\n\t\t'Example: delete_environment({ project_id: \"prj_abc\", environment_id: \"env_xyz\" }) → { success: true }',\n\t].join('\\n'),\n\tinput: {\n\t\tproject_id: z.string().describe('Project publicId.'),\n\t\tenvironment_id: z\n\t\t\t.union([z.string(), z.number()])\n\t\t\t.describe('Environment publicId or numeric id.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tawait ctx.hoststack.environments.delete(teamId, args.project_id, args.environment_id);\n\t\treturn respond({\n\t\t\tsummary: `Deleted environment ${String(args.environment_id)} from project ${args.project_id}.`,\n\t\t\tdata: { success: true },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'promote_deploy',\n\tcategory: 'environments',\n\tdescription: [\n\t\t'Promote a built deploy from one environment to another (build-once, run-many).',\n\t\t'',\n\t\t'When to use: the user has tested a deploy in staging and wants to ship that exact image to production without rebuilding. Atomic and image-based — same docker image runs on the sibling service in the target env.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: source service publicId (the one that owns the deploy).',\n\t\t' - deploy_id: source deploy publicId. Must have `dockerImageId` set (i.e. a successful build).',\n\t\t\" - target_environment_id: env publicId or numeric id to promote into. Must be in the same project. Cannot be the source service's own env.\",\n\t\t'',\n\t\t\"Behaviour: if a sibling service with the same name already exists in the target env, the new deploy lands on it. Otherwise the API auto-clones the source service's build/runtime config into the target env first. Env-specific config (env vars, secret files, volumes, IP allowlists, custom domains) is NOT copied — those are per-env by design.\",\n\t\t'',\n\t\t'Returns: { deploy: Deploy } — the new deploy on the target service.',\n\t\t'',\n\t\t'Example: promote_deploy({ service_id: \"svc_staging\", deploy_id: \"dpl_built\", target_environment_id: \"env_prod\" }) → { deploy: { id: 99, status: \"pending\", trigger: \"rollback\", dockerImageId: \"sha256:…\" } }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Source service publicId.'),\n\t\tdeploy_id: z.string().describe('Source deploy publicId (must have a built image).'),\n\t\ttarget_environment_id: z\n\t\t\t.union([z.string(), z.number()])\n\t\t\t.describe('Target environment publicId or numeric id.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\t// Resolve target env to numeric id; the API expects a number.\n\t\tconst targetEnvId = await ctx.hoststack.resolveId(args.target_environment_id, {\n\t\t\tkind: 'environment',\n\t\t\tteamId: await ctx.hoststack.resolveId(teamId, { kind: 'team' }),\n\t\t});\n\t\tconst response = await ctx.hoststack.deploys.promote(\n\t\t\tteamId,\n\t\t\targs.service_id,\n\t\t\targs.deploy_id,\n\t\t\ttargetEnvId,\n\t\t);\n\t\tconst data = { deploy: shape(response.deploy) };\n\t\treturn respond({\n\t\t\tsummary: `Promoted deploy ${args.deploy_id} from service ${args.service_id} to env ${String(args.target_environment_id)}.`,\n\t\t\tdata,\n\t\t});\n\t},\n});\n","import { z } from 'zod';\n\nimport { formatCount } from '../lib/format.js';\nimport { respond } from '../lib/respond.js';\nimport { shape } from '../lib/shape.js';\nimport { defineTool } from '../registry.js';\n\ninterface MetricWindow {\n\tpageviews: number;\n\tvisitors: number;\n\tbounceRate: number | null;\n\tavgDurationMs: number | null;\n}\n\ninterface SitesResponse {\n\tsites: (Record<string, unknown> & { id: number; domain: string })[];\n}\n\ninterface SummaryResponse {\n\trange: string;\n\tsites: (Record<string, unknown> & {\n\t\tdomain: string;\n\t\tcurrent: MetricWindow;\n\t\tvisitorsAreSummedDailies: boolean;\n\t})[];\n}\n\ninterface OverviewResponse {\n\trange: string;\n\tsource: 'raw' | 'rollup';\n\tvisitorsAreSummedDailies: boolean;\n\tfiltersSupported: boolean;\n\tsummary: { current: MetricWindow; previous: MetricWindow };\n\t[key: string]: unknown;\n}\n\n/** Turn a domain into the site id the API wants, or fail with a useful message. */\nasync function resolveSiteIds(\n\tdomains: string[] | undefined,\n\tteamId: number,\n\tapi: {\n\t\tget: <T>(path: string, params?: Record<string, string | number | undefined>) => Promise<T>;\n\t},\n): Promise<string | undefined> {\n\tif (!domains || domains.length === 0) return undefined;\n\tconst { sites } = await api.get<SitesResponse>(`/api/analytics/${teamId}/sites`);\n\tconst ids: number[] = [];\n\tconst missing: string[] = [];\n\tfor (const domain of domains) {\n\t\tconst needle = domain\n\t\t\t.toLowerCase()\n\t\t\t.replace(/^https?:\\/\\//, '')\n\t\t\t.replace(/\\/.*$/, '');\n\t\tconst match = sites.find((s) => s.domain === needle);\n\t\tif (match) ids.push(match.id);\n\t\telse missing.push(domain);\n\t}\n\tif (missing.length > 0) {\n\t\tthrow new Error(\n\t\t\t`Not tracking ${missing.join(', ')}. Known sites: ${sites.map((s) => s.domain).join(', ') || 'none yet'}.`,\n\t\t);\n\t}\n\treturn ids.join(',');\n}\n\nconst ALLOWED_ORIGINS_INPUT = z\n\t.array(z.string().trim().min(1).max(253))\n\t.max(20)\n\t.describe(\n\t\t'Extra origins allowed to post events, beyond the site domain and its subdomains (both are always allowed). Bare hostnames, e.g. [\"staging.example.com\", \"localhost:5173\"]. Replaces the whole list; pass [] to clear it.',\n\t);\n\nconst RETENTION_DAYS_INPUT = z\n\t.number()\n\t.int()\n\t.positive()\n\t.describe('How many days of raw events to keep. Rollups outlive this.');\n\ndefineTool({\n\tname: 'list_analytics_sites',\n\tcategory: 'analytics',\n\tdescription: [\n\t\t'List the websites this team tracks — domains, their site keys, retention, and which service (if any) serves each one.',\n\t\t'',\n\t\t'When to use: before any other analytics call, to learn which domains exist; or to fetch the script tag a site needs.',\n\t\t'',\n\t\t'The site key is PUBLIC by design — it ships in a `data-site-key` attribute that anyone can view-source, like a Sentry DSN. It is write-only and scoped to one site. Do not treat it as a secret, and do not confuse it with the team API key.',\n\t\t'',\n\t\t'Returns: { sites: Array }. Each site has id, publicId, name, domain, ingestKey, allowedOrigins, serviceId, serviceName, retentionDays, droppedCount (events refused by the hourly quota and therefore MISSING from every count for that site), and keyGraceEndsAt when a rotation is still in its grace window.',\n\t\t'',\n\t\t'Each site also carries lastEventAt (null means NOTHING has ever reached ingest for that key) plus lastRefusalReason and refusedRecently, a 7-day tally by reason. An empty chart with lastEventAt set is a site with no traffic; an empty chart with lastEventAt null is a broken setup. Do not report \"no visitors\" without checking which one you are looking at — call check_analytics_site for the diagnosis.',\n\t\t'',\n\t\t\"Example: list_analytics_sites({}) → { sites: [{ id: 3, domain: 'micci.dk', ingestKey: 'site_…', retentionDays: 30, serviceName: 'micci-web' }] }.\",\n\t].join('\\n'),\n\tinput: {},\n\thandler: async (_args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.api.get<SitesResponse>(`/api/analytics/${teamId}/sites`);\n\t\tconst items = Array.isArray(response.sites) ? response.sites.map(shape) : [];\n\t\tconst summary =\n\t\t\titems.length === 0\n\t\t\t\t? 'No analytics sites yet. Create one with create_analytics_site.'\n\t\t\t\t: `Tracking ${items.length} site${items.length === 1 ? '' : 's'}.`;\n\t\treturn respond({ summary, data: { sites: items } });\n\t},\n});\n\ndefineTool({\n\tname: 'check_analytics_site',\n\tcategory: 'analytics',\n\tdescription: [\n\t\t'Why a site is reporting nothing — the diagnosis, without having to generate traffic and guess.',\n\t\t'',\n\t\t'When to use: any time a chart is empty, a site was just set up, a key was rotated, or someone asks whether the snippet is installed correctly. Call this BEFORE telling anyone their traffic dropped.',\n\t\t'',\n\t\t'The ingest endpoint answers HTTP 204 to a real site key and to a typo alike — deliberately, so it cannot be used to enumerate keys — which means five completely different situations produce the same empty chart: a stale or mistyped key in the deployed snippet, an origin the site does not allow, a blown hourly quota, events blocked in the browser before they leave (CORS, ad blocker, CSP), and simply no visitors yet. This tool tells them apart.',\n\t\t'',\n\t\t\"Returns: health ('receiving' | 'quiet' | 'refusing' | 'never'), a headline and detail sentence, lastEventAt, lastRefusalAt/Reason/Origin, refusedRecently (a 7-day tally by reason), refusedOrigins (which origins were turned away for bad_origin and how often, over the same window), the site key the snippet must carry, allowed origins, and quota usage this hour.\",\n\t\t'',\n\t\t\"Use refusedOrigins, not lastRefusalOrigin, to decide what to allow: lastRefusalOrigin is the newest refusal of ANY reason, and bots outnumber everything, so one crawler hit overwrites the origin behind a real outage. Feed what you find to update_analytics_site — and if the origin is NOT the user's, the site key is public by design and somebody has pasted it into their own page, so the answer is to rotate the key rather than allow them.\",\n\t\t'',\n\t\t\"health='never' means nothing has EVER reached ingest for this key, so the request is not leaving the browser — check the snippet is in the deployed HTML and tell the user to add `data-debug` to the script tag, which makes the tracker log its resolved endpoint and every accepted 204 to the console.\",\n\t\t'',\n\t\t\"Example: check_analytics_site({ domain: 'example.com' }) → { health: 'refusing', headline: 'Reaching us, and being refused.', detail: 'Something on example.com posted 214 event(s) with a site key we do not recognise…', lastEventAt: null, refusedRecently: { unknown_key: 214, bad_origin: 0, bot: 3, over_quota: 0 } }.\",\n\t].join('\\n'),\n\tinput: {\n\t\tdomain: z.string().max(253).describe('Bare hostname of a site this team tracks.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst siteIds = await resolveSiteIds([args.domain], teamId, ctx.api);\n\t\tconst status = await ctx.api.get<Record<string, unknown> & { headline: string }>(\n\t\t\t`/api/analytics/${teamId}/sites/${siteIds}/status`,\n\t\t);\n\t\treturn respond({ summary: `${args.domain}: ${status.headline}`, data: shape(status) });\n\t},\n});\n\ndefineTool({\n\tname: 'get_analytics_summary',\n\tcategory: 'analytics',\n\tdescription: [\n\t\t'One row per site: visitors, pageviews, bounce rate, average visit and live visitors, each with the previous period to compare against.',\n\t\t'',\n\t\t'When to use: \"how are my sites doing\", a weekly check, or spotting which of several domains moved. This is the cross-site view; use get_analytics_overview for the breakdowns behind one of them.',\n\t\t'',\n\t\t\"READ THE VISITOR NUMBER CAREFULLY. Inside a site's raw-event window (35 days by default, so 24h through 30d are raw) `visitors` is a true distinct count over the range. Beyond it the answer comes from a daily rollup and `visitors` is each day's uniques ADDED TOGETHER — someone who visits daily counts once per day. `visitorsAreSummedDailies` says which you got; quote it when reporting a 90d or 12mo number.\",\n\t\t'',\n\t\t'Visitors are never summed ACROSS sites. The same person on two domains is two session ids, and reconciling them would require the cross-domain identity a cookieless tracker exists not to build. Add pageviews if you need a total; do not add visitors.',\n\t\t'',\n\t\t'Inputs: range (24h|7d|30d|90d|12mo, default 7d), domains (optional list; omit for every site).',\n\t\t'',\n\t\t'Returns: the range asked for, and one row per site — domain, `current` and `previous` blocks of visitors / pageviews / bounceRate / avgVisitSeconds, the live visitor count, and `visitorsAreSummedDailies`, which says whether that visitor number is a distinct count or a sum of daily uniques. Quote that flag with any 90d or 12mo figure.',\n\t\t'',\n\t\t\"Example: get_analytics_summary({ range: '30d' }) → { range: '30d', sites: [{ domain: 'hoststack.dev', current: { visitors: 4210, pageviews: 11890, bounceRate: 47.2 }, previous: {…}, live: 3, visitorsAreSummedDailies: false }] }.\",\n\t].join('\\n'),\n\tinput: {\n\t\trange: z.enum(['24h', '7d', '30d', '90d', '12mo']).optional().describe('Default 7d.'),\n\t\tdomains: z\n\t\t\t.array(z.string().max(253))\n\t\t\t.max(50)\n\t\t\t.optional()\n\t\t\t.describe('Bare hostnames. Omit for every site this team tracks.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst siteIds = await resolveSiteIds(args.domains, teamId, ctx.api);\n\t\tconst response = await ctx.api.get<SummaryResponse>(`/api/analytics/${teamId}/summary`, {\n\t\t\trange: args.range ?? '7d',\n\t\t\t...(siteIds ? { siteIds } : {}),\n\t\t});\n\t\tconst sites = Array.isArray(response.sites) ? response.sites : [];\n\t\tconst summed = sites.some((s) => s.visitorsAreSummedDailies);\n\t\tconst summary =\n\t\t\tsites.length === 0\n\t\t\t\t? 'No sites to report on.'\n\t\t\t\t: `${sites.length} site${sites.length === 1 ? '' : 's'} over ${response.range}.${\n\t\t\t\t\t\tsummed\n\t\t\t\t\t\t\t? ' Visitor counts are daily uniques summed, not distinct over the range — this range is past the raw-event window.'\n\t\t\t\t\t\t\t: ''\n\t\t\t\t\t}`;\n\t\treturn respond({ summary, data: { range: response.range, sites: sites.map(shape) } });\n\t},\n});\n\ndefineTool({\n\tname: 'get_analytics_overview',\n\tcategory: 'analytics',\n\tdescription: [\n\t\t'The full breakdown for one site (or several): timeseries, top paths, referrers, custom events, browsers, operating systems, languages, screen sizes, campaigns, devices and countries.',\n\t\t'',\n\t\t'When to use: after get_analytics_summary has told you WHICH site moved, and you need to see what drove it — a referrer that appeared, a campaign that landed, a path that spiked.',\n\t\t'',\n\t\t'Two things change past the raw-event window (35 days by default, so 24h through 30d are raw): `visitors` becomes daily uniques summed rather than a distinct count (`visitorsAreSummedDailies`), and FILTERS ARE IGNORED because the rollup stores per-day aggregates with no events left to filter (`filtersSupported: false`). Check both fields before drawing a conclusion; do not silently present a filtered request that was not filtered.',\n\t\t'',\n\t\t'Inputs: range (24h|7d|30d|90d|12mo, default 7d), domains (optional list; omit for every site), filters (optional map — country, browser, os, device, language, path, referrer, utmSource, utmMedium, utmCampaign, utmTerm, utmContent).',\n\t\t'',\n\t\t\"Returns: `source` ('raw' | 'rollup') and `filtersSupported`, which together say how much of the request was actually honoured; a `summary` block of current-vs-previous totals; a timeseries; and the breakdowns — topPaths, topReferrers, topEvents, browsers, operatingSystems, languages, screenSizes, campaigns, devices and countries. Read `source` and `filtersSupported` before quoting any of it.\",\n\t\t'',\n\t\t\"Example: get_analytics_overview({ domains: ['hoststack.dev'], range: '7d', filters: { country: 'DK' } }) → { source: 'raw', filtersSupported: true, summary: { current: { visitors: 310, … } }, topPaths: [{ path: '/pricing', pageviews: 214 }], topReferrers: [{ referrer: 'Direct', visits: 190 }], … }.\",\n\t].join('\\n'),\n\tinput: {\n\t\trange: z.enum(['24h', '7d', '30d', '90d', '12mo']).optional().describe('Default 7d.'),\n\t\tdomains: z\n\t\t\t.array(z.string().max(253))\n\t\t\t.max(50)\n\t\t\t.optional()\n\t\t\t.describe('Bare hostnames. Omit for every site this team tracks.'),\n\t\tfilters: z\n\t\t\t.record(z.string(), z.string().max(200))\n\t\t\t.optional()\n\t\t\t.describe('Dimension filters. Ignored for ranges past the raw-event window.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst siteIds = await resolveSiteIds(args.domains, teamId, ctx.api);\n\t\tconst response = await ctx.api.get<OverviewResponse>(`/api/analytics/${teamId}/overview`, {\n\t\t\trange: args.range ?? '7d',\n\t\t\t...(siteIds ? { siteIds } : {}),\n\t\t\t...(args.filters ?? {}),\n\t\t});\n\t\tconst { current } = response.summary;\n\t\tconst caveats: string[] = [];\n\t\tif (response.visitorsAreSummedDailies) {\n\t\t\tcaveats.push(\n\t\t\t\t'visitors are daily uniques summed (this range is past the raw-event window)',\n\t\t\t);\n\t\t}\n\t\tif (args.filters && Object.keys(args.filters).length > 0 && !response.filtersSupported) {\n\t\t\tcaveats.push(\n\t\t\t\t'the filters you passed were NOT applied — they only work on shorter ranges',\n\t\t\t);\n\t\t}\n\t\tconst summary = `${formatCount(current.pageviews)} pageviews from ${formatCount(current.visitors)} visitors over ${response.range}${\n\t\t\tcaveats.length > 0 ? `. Note: ${caveats.join('; ')}.` : '.'\n\t\t}`;\n\t\treturn respond({ summary, data: response });\n\t},\n});\n\ndefineTool({\n\tname: 'create_analytics_site',\n\tcategory: 'analytics',\n\tdescription: [\n\t\t'Start tracking a domain, and return the script tag to paste into its <head>.',\n\t\t'',\n\t\t'When to use: the user wants analytics for a site that is not in list_analytics_sites yet. Works for any domain, whether or not it is hosted on HostStack.',\n\t\t'',\n\t\t'If the domain is already attached to a service on this team, the new site links itself to that service and appears on its Analytics tab. Nothing is counted until the snippet is actually on the page — creating the site alone produces an empty dashboard, which is expected, not a fault.',\n\t\t'',\n\t\t'Inputs: domain (required, bare hostname — \"example.com\", not a URL), name (optional display name, defaults to the domain), allowed_origins (optional extra origins beyond the domain and its subdomains), retention_days (optional raw-event retention).',\n\t\t'',\n\t\t'To change any of these later — or to attach the site to a service — use update_analytics_site. Creating is not the only chance to set them.',\n\t\t'',\n\t\t'Returns: the created site (id, domain, name, ingestKey, retentionDays) and the ready-made `snippet` to paste into the page — the key is in that snippet, so hand it over rather than describing it.',\n\t\t'',\n\t\t\"Example: create_analytics_site({ domain: 'micci.dk' }) → { site: { id: 3, domain: 'micci.dk', ingestKey: 'site_…' }, snippet: '<script defer src=\\\"https://hoststack.dev/t.js\\\" data-site-key=\\\"site_…\\\"></script>' }.\",\n\t].join('\\n'),\n\tinput: {\n\t\tdomain: z.string().min(1).max(253).describe('Bare hostname, e.g. example.com'),\n\t\tname: z\n\t\t\t.string()\n\t\t\t.min(1)\n\t\t\t.max(100)\n\t\t\t.optional()\n\t\t\t.describe('Display name. Defaults to the domain.'),\n\t\tallowed_origins: ALLOWED_ORIGINS_INPUT.optional(),\n\t\tretention_days: RETENTION_DAYS_INPUT.optional(),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst site = await ctx.api.post<\n\t\t\tRecord<string, unknown> & { domain: string; ingestKey: string }\n\t\t>(`/api/analytics/${teamId}/sites`, {\n\t\t\tdomain: args.domain,\n\t\t\t...(args.name ? { name: args.name } : {}),\n\t\t\t...(args.allowed_origins ? { allowedOrigins: args.allowed_origins } : {}),\n\t\t\t...(args.retention_days ? { retentionDays: args.retention_days } : {}),\n\t\t});\n\t\tconst snippet = `<script defer src=\"https://hoststack.dev/t.js\" data-site-key=\"${site.ingestKey}\"></script>`;\n\t\treturn respond({\n\t\t\tsummary: `Now tracking ${site.domain}. Paste the snippet into its <head> — nothing is counted until you do.`,\n\t\t\tdata: { site: shape(site), snippet },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'update_analytics_site',\n\tcategory: 'analytics',\n\tdescription: [\n\t\t'Change an existing analytics site: its allowed origins, display name, retention, or which service it belongs to.',\n\t\t'',\n\t\t\"When to use: events are being refused with `bad_origin` because they come from an origin the site does not list (check_analytics_site names it), a site needs a clearer name, retention should change, or the site should appear on a service's Analytics tab.\",\n\t\t'',\n\t\t'Inputs: domain (required — the site to change, by its bare hostname), then any of allowed_origins, name, retention_days, service_id. Everything is optional except domain; only the fields you pass are touched.',\n\t\t'',\n\t\t'allowed_origins REPLACES the list rather than appending, so read the current one from list_analytics_sites first and send it back with your addition. The site domain and its subdomains are always allowed and never need listing.',\n\t\t'',\n\t\t'Returns: { site } — the site as it now stands.',\n\t\t'',\n\t\t\"Example: update_analytics_site({ domain: 'example.com', allowed_origins: ['staging.example.com'] }) → { site: { domain: 'example.com', allowedOrigins: ['staging.example.com'], … } }.\",\n\t].join('\\n'),\n\tinput: {\n\t\tdomain: z.string().min(1).max(253).describe('Bare hostname of a site this team tracks.'),\n\t\tallowed_origins: ALLOWED_ORIGINS_INPUT.optional(),\n\t\tname: z.string().trim().min(1).max(100).optional().describe('New display name.'),\n\t\tretention_days: RETENTION_DAYS_INPUT.optional(),\n\t\tservice_id: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.positive()\n\t\t\t.nullable()\n\t\t\t.optional()\n\t\t\t.describe('Numeric service id to attach this site to, or null to detach it.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst siteId = await resolveSiteIds([args.domain], teamId, ctx.api);\n\n\t\tconst patch: Record<string, unknown> = {};\n\t\tif (args.allowed_origins !== undefined) patch['allowedOrigins'] = args.allowed_origins;\n\t\tif (args.name !== undefined) patch['name'] = args.name;\n\t\tif (args.retention_days !== undefined) patch['retentionDays'] = args.retention_days;\n\t\tif (args.service_id !== undefined) patch['serviceId'] = args.service_id;\n\t\t// A PATCH with nothing in it is a caller mistake, not a no-op worth a\n\t\t// round trip — say which fields exist rather than returning the site\n\t\t// unchanged and letting them think it worked.\n\t\tif (Object.keys(patch).length === 0) {\n\t\t\tthrow new Error(\n\t\t\t\t'Nothing to update. Pass at least one of allowed_origins, name, retention_days or service_id.',\n\t\t\t);\n\t\t}\n\n\t\tconst site = await ctx.api.patch<Record<string, unknown> & { domain: string }>(\n\t\t\t`/api/analytics/${teamId}/sites/${siteId}`,\n\t\t\tpatch,\n\t\t);\n\t\tconst changed = Object.keys(patch).join(', ');\n\t\treturn respond({\n\t\t\tsummary: `Updated ${site.domain} (${changed}).`,\n\t\t\tdata: { site: shape(site) },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'verify_site_domain',\n\tcategory: 'analytics',\n\tdescription: [\n\t\t\"Prove the team owns a site's domain, by publishing a TXT record.\",\n\t\t'',\n\t\t'When to use: before turning on an uptime check for a site that HostStack does not host. Analytics itself needs no proof — the ingest key only labels events the site posts about itself — so do NOT send a user through this just to get a dashboard. It exists for the capabilities where the PLATFORM acts on the hostname.',\n\t\t'',\n\t\t'Call it once with no `check` to get the record to publish, then again with check=true once the record is live. The token is stable across calls, so it is safe to re-read the instructions while the user is mid-paste.',\n\t\t'',\n\t\t'A site whose domain is already verified on a service in this team is proven ALREADY and does not need this — `list_analytics_sites` reports that as domainProven. This is for the site with no service behind it, which can never have such a link.',\n\t\t'',\n\t\t'The record is read from the domain\\'s own authoritative nameservers rather than through a cache, so \"I just added it\" works immediately instead of being masked by a negative-cache TTL.',\n\t\t'',\n\t\t'Inputs: siteId (required), check (optional, default false — true means \"look at DNS now and tell me the verdict\").',\n\t\t'',\n\t\t'Returns: the record to publish plus `verified`. A `verified: false` is a successful check with a negative answer, not an error — most often the record simply has not propagated yet.',\n\t\t'',\n\t\t\"Example: verify_site_domain({ siteId: 3 }) → { recordName: '_hoststack-verify.poststack.dev', recordType: 'TXT', recordValue: 'hoststack-verify=9f3c…', verified: false }.\",\n\t].join('\\n'),\n\tinput: {\n\t\tsiteId: z.number().int().positive(),\n\t\tcheck: z\n\t\t\t.boolean()\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t'True to read DNS now and return the verdict. False just returns the record.',\n\t\t\t),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst instructions = await ctx.api.get<{\n\t\t\trecordName: string;\n\t\t\trecordType: string;\n\t\t\trecordValue: string;\n\t\t\tverified: boolean;\n\t\t}>(`/api/analytics/${teamId}/sites/${args.siteId}/verification`);\n\n\t\tif (!args.check) {\n\t\t\treturn respond({\n\t\t\t\tsummary: instructions.verified\n\t\t\t\t\t? 'Already verified.'\n\t\t\t\t\t: `Publish a ${instructions.recordType} record at ${instructions.recordName} with the value shown, then call again with check=true.`,\n\t\t\t\tdata: shape(instructions),\n\t\t\t});\n\t\t}\n\n\t\tconst verdict = await ctx.api.post<{ verified: boolean; detail?: string }>(\n\t\t\t`/api/analytics/${teamId}/sites/${args.siteId}/verify`,\n\t\t\t{},\n\t\t);\n\t\treturn respond({\n\t\t\tsummary: verdict.verified\n\t\t\t\t? 'Verified. This site can now be given an uptime check.'\n\t\t\t\t: `Not verified yet — ${verdict.detail ?? 'the record was not found'}.`,\n\t\t\tdata: { record: shape(instructions), result: shape(verdict) },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'get_site_uptime_check',\n\tcategory: 'analytics',\n\tdescription: [\n\t\t\"Read a site's uptime check without touching it.\",\n\t\t'',\n\t\t'When to use: answer \"has it probed yet?\", \"is it up?\", \"how many failures in a row?\" — the site counterpart of get_uptime_check.',\n\t\t'',\n\t\t'READ THIS BEFORE REACHING FOR set_site_uptime_check TO INSPECT ONE. The setter RESETS the accumulated state it returns (status back to \"unknown\", consecutiveFailures to 0, lastCheckedAt to null), because a check whose shape just changed has not observed the new shape failing. So using it to look at a check is what stops the check ever showing a probe: every read restarts the measurement, and a working check reads as one that never runs. This tool exists because that trap cost a real investigation an afternoon.',\n\t\t'',\n\t\t\"Returns: { check } — enabled, path, method, expectedStatus, timeoutMs, intervalSeconds, failureThreshold, plus live state: status ('up' | 'down' | 'unknown'), consecutiveFailures, lastCheckedAt, lastStatusCode, lastLatencyMs, lastError, lastChangedAt. `null` when the site has no check.\",\n\t\t'',\n\t\t\"Example: get_site_uptime_check({ siteId: 12 }) → { check: { status: 'up', lastStatusCode: 200, lastCheckedAt: '2026-09-09T05:27:34Z' } }.\",\n\t].join('\\n'),\n\tinput: {\n\t\tsiteId: z.number().int().positive(),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.api.get<{ check: Record<string, unknown> | null }>(\n\t\t\t`/api/analytics/${teamId}/sites/${args.siteId}/uptime-check`,\n\t\t);\n\t\tif (!response.check) {\n\t\t\treturn respond({\n\t\t\t\tsummary: 'No uptime check on this site — nothing is watching it.',\n\t\t\t\tdata: { check: null },\n\t\t\t});\n\t\t}\n\t\tconst check = shape(response.check) as Record<string, unknown>;\n\t\tconst lastCheckedAt = check.lastCheckedAt;\n\t\treturn respond({\n\t\t\tsummary: `Uptime check is ${String(check.status ?? 'unknown')}${\n\t\t\t\tlastCheckedAt ? `, last probed ${String(lastCheckedAt)}` : ', not probed yet'\n\t\t\t}.`,\n\t\t\tdata: { check },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'set_site_uptime_check',\n\tcategory: 'analytics',\n\tdescription: [\n\t\t'Watch a site HostStack does not host — request its URL on a schedule and alert when it stops answering.',\n\t\t'',\n\t\t'When to use: the user has a site running somewhere else (their own box, another provider) and wants to know when it goes down. This is the one observability capability an off-platform site cannot provide for itself: analytics is a script tag and error reporting is an HTTP POST, but an outside-in probe has to come from outside.',\n\t\t'',\n\t\t\"Works on a site HostStack DOES host too, and that is not a worse option: this check names ONE hostname — the site's own proven domain — where set_uptime_check follows whichever of the service's domains is primary at probe time. A service answering on a canonical host plus a 301 alias has no single correct expectedStatus, so a site check per hostname is how each one gets asserted, and nothing in the prober treats a service-backed site differently. Use set_uptime_check when the check should follow the service's domain; use this one when a named host is the point.\",\n\t\t'',\n\t\t'To READ a check, call get_site_uptime_check. Changing the shape of a check RESETS its accumulated state (status to \"unknown\", consecutiveFailures to 0, lastCheckedAt to null) — the new shape has not been measured yet, so carrying failures forward would alert about a condition nobody observed. A write that resolves to the shape ALREADY STORED changes nothing and leaves that state alone. The inputs are defaults rather than a partial edit, though: omitting intervalSeconds on a check currently set to 30 resolves to 60, which IS a change.',\n\t\t'',\n\t\t'REQUIRES a proven domain. Call verify_site_domain first if `domainProven` is false; an unverified target is refused with 400. This is not paperwork: an uptime check makes the control plane fetch the hostname every interval, forever, from our IP, so it must be a name the team has shown it owns.',\n\t\t'',\n\t\t\"Always https, and the host is the site's domain — `path` is a path, not a URL. Redirects are NOT followed: a 301 is an answer, and following one can walk the probe onto a parked page and report a dead site as healthy. Set expectedStatus to the redirect if one is expected.\",\n\t\t'',\n\t\t'Inputs (all optional except siteId): enabled, path (default \"/\"), method (GET|HEAD), expectedStatus (default 200), timeoutMs (1000–60000, default 10000 — this is what catches a hung server), intervalSeconds (30–3600, default 60), failureThreshold (1–10, default 3).',\n\t\t'',\n\t\t\"Returns: the stored check — enabled, the resolved target URL, method, expectedStatus, timeoutMs, intervalSeconds, failureThreshold — plus its live state: status ('up' | 'down' | 'unknown'), lastCheckedAt, lastStatusCode and consecutiveFailures. A freshly created check is 'unknown' until the first probe runs.\",\n\t\t'',\n\t\t\"Example: set_site_uptime_check({ siteId: 3, path: '/health', intervalSeconds: 60 }) → { check: { status: 'unknown', … } }.\",\n\t].join('\\n'),\n\tinput: {\n\t\tsiteId: z.number().int().positive(),\n\t\tenabled: z.boolean().optional(),\n\t\tpath: z.string().max(500).optional().describe('Must start with /. Default \"/\".'),\n\t\tmethod: z.enum(['GET', 'HEAD']).optional(),\n\t\texpectedStatus: z.number().int().min(100).max(599).optional(),\n\t\ttimeoutMs: z.number().int().min(1000).max(60_000).optional(),\n\t\tintervalSeconds: z.number().int().min(30).max(3600).optional(),\n\t\tfailureThreshold: z.number().int().min(1).max(10).optional(),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst { siteId, ...body } = args;\n\t\tconst response = await ctx.api.put<{ check: Record<string, unknown> }>(\n\t\t\t`/api/analytics/${teamId}/sites/${siteId}/uptime-check`,\n\t\t\tbody,\n\t\t);\n\t\tconst check = shape(response.check) as Record<string, unknown>;\n\t\t// Say which of the two happened, because 'unknown' with a null lastCheckedAt\n\t\t// is exactly what a never-probed check and a just-reset one look like — and\n\t\t// reading that as \"it does not work\" is the mistake this wording exists to\n\t\t// prevent. A non-null lastCheckedAt means the write changed nothing, so the\n\t\t// state survived.\n\t\tconst probed = check['lastCheckedAt'] !== null && check['lastCheckedAt'] !== undefined;\n\t\treturn respond({\n\t\t\tsummary: probed\n\t\t\t\t? `Uptime check saved — the shape was already stored, so nothing was reset: still ${String(check['status'])}, last probed ${String(check['lastCheckedAt'])}.`\n\t\t\t\t: 'Uptime check saved, and not probed yet — \"unknown\" with no lastCheckedAt is the state of a check that has not run once, not a broken one. It reports within a minute or two: read it with get_site_uptime_check, never by saving it again.',\n\t\t\tdata: { check },\n\t\t});\n\t},\n});\n","/**\n * Number rendering for the text summaries this server returns.\n *\n * Pinned to the platform's locale rather than the host machine's:\n * `toLocaleString()` with no argument formats in whatever locale the process\n * happens to run under, so the same summary reached one agent as \"1.762\n * pageviews\" and another as \"1,762\" — and the first reads as one-point-seven\n * to a model that has no `LANG` to interpret it by.\n *\n * Copied from `@hoststack/shared`'s `APP_LOCALE` rather than imported: this\n * package publishes to npm and `@hoststack/shared` is `private: true`, so\n * nothing the bundle reaches may import it. `shared-parity.test.ts` — which\n * the bundle does NOT reach — compares the two.\n */\nexport const MCP_LOCALE = 'en-IE';\n\nconst COUNT_FORMAT = new Intl.NumberFormat(MCP_LOCALE);\n\n/** A plain count with thousands separators, e.g. 1762 → \"1,762\". */\nexport function formatCount(value: number): string {\n\treturn COUNT_FORMAT.format(value);\n}\n","import { z } from 'zod';\n\nimport { respond } from '../lib/respond.js';\nimport { shape } from '../lib/shape.js';\nimport { defineTool } from '../registry.js';\n\ninterface IssuesResponse {\n\tissues: unknown[];\n\ttotal: number;\n}\n\ninterface IssueResponse {\n\tissue: Record<string, unknown>;\n}\n\ninterface OccurrencesResponse {\n\toccurrences: unknown[];\n}\n\ninterface FixResponse {\n\ttask: { publicId?: string; title?: string; body?: string };\n\talreadyExisted: boolean;\n\tbox: { serviceId: number; name: string; asleep: boolean };\n\tautomodeEnabled: boolean;\n}\n\ninterface IngestKeysResponse {\n\tkeys: unknown[];\n}\n\ninterface IngestKeyResponse {\n\tkey: Record<string, unknown> & { key?: string };\n}\n\ndefineTool({\n\tname: 'list_error_issues',\n\tcategory: 'errors',\n\tdescription: [\n\t\t\"List error issues for the team — exceptions the team's own applications reported, grouped by cause rather than listed one per event.\",\n\t\t'',\n\t\t'When to use: \"what is throwing in production\", triaging after a deploy, or finding the bug behind a user report. Pairs with list_alerts (which tells you an alert fired) and get_service_logs (which shows the raw output around it).',\n\t\t'',\n\t\t'Grouping: an issue is one distinct problem. Its fingerprint is the exception class, the message with variable parts removed (so \"user 41 not found\" and \"user 9002 not found\" are ONE issue), and the topmost stack frame in the team\\'s own code — not the framework\\'s. `culprit` is that frame, and it is the line to open first.',\n\t\t'',\n\t\t'Counts are exact; occurrence samples are not. `occurrenceCount` is the true number of times it happened. A bounded number of full occurrences is retained per issue (see get_error_issue), and `droppedCount` is how many were counted but not stored because the service exceeded its hourly ingest quota.',\n\t\t'',\n\t\t'Defaults to status=unresolved, because the list exists to answer \"what is broken\". Pass status=resolved or status=ignored for the others.',\n\t\t'',\n\t\t'Inputs (all optional): serviceId, status (unresolved|resolved|ignored), q (substring match on title/culprit), limit (default 50, max 200), offset, sort (last_seen|first_seen|count).',\n\t\t'',\n\t\t'Returns: { issues: Array, total: number }. Each issue has id, publicId, serviceId, serviceName, type, title, culprit, level, status, occurrenceCount, droppedCount, affectedUsers (capped at 500 — 500 means \"500 or more\"), firstSeenAt, lastSeenAt, firstSeenRelease, lastSeenRelease, environment, sourceTaskPublicId (a dev-box task already open for it).',\n\t\t'',\n\t\t\"Example: list_error_issues({ serviceId: 48, sort: 'count' }) → { issues: [{ id: 12, type: 'TypeError', title: 'TypeError: cart.total is not a function', culprit: 'src/checkout.ts:12 in checkout', occurrenceCount: 4187, affectedUsers: 96, lastSeenRelease: 'a91f3c2', … }], total: 3 }.\",\n\t].join('\\n'),\n\tinput: {\n\t\tserviceId: z.number().int().positive().optional().describe('Only issues for this service.'),\n\t\tstatus: z\n\t\t\t.enum(['unresolved', 'resolved', 'ignored'])\n\t\t\t.optional()\n\t\t\t.describe('Default unresolved.'),\n\t\tq: z.string().max(200).optional().describe('Substring match on title or culprit.'),\n\t\tlimit: z.number().int().positive().max(200).optional().describe('Default 50, cap 200.'),\n\t\toffset: z.number().int().min(0).optional(),\n\t\tsort: z\n\t\t\t.enum(['last_seen', 'first_seen', 'count'])\n\t\t\t.optional()\n\t\t\t.describe('Default last_seen.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst params: Record<string, string | number | undefined> = {};\n\t\tfor (const key of ['serviceId', 'status', 'q', 'limit', 'offset', 'sort'] as const) {\n\t\t\tconst value = args[key];\n\t\t\tif (value !== undefined) params[key] = String(value);\n\t\t}\n\t\tconst response = await ctx.api.get<IssuesResponse>(`/api/errors/${teamId}/issues`, params);\n\t\tconst items = Array.isArray(response.issues) ? response.issues.map(shape) : [];\n\t\tconst summary =\n\t\t\titems.length === 0\n\t\t\t\t? `No ${args.status ?? 'unresolved'} error issues${args.serviceId ? ' for that service' : ''}.`\n\t\t\t\t: `Returned ${items.length} of ${response.total} ${args.status ?? 'unresolved'} issue${response.total === 1 ? '' : 's'}.`;\n\t\treturn respond({ summary, data: { issues: items, total: response.total } });\n\t},\n});\n\ndefineTool({\n\tname: 'get_error_issue',\n\tcategory: 'errors',\n\tdescription: [\n\t\t'Fetch one error issue plus its most recent stored occurrences — the stack traces, request context and releases behind the aggregate.',\n\t\t'',\n\t\t'When to use: after list_error_issues has told you WHICH problem to look at, and you need the actual stack to reason about the cause.',\n\t\t'',\n\t\t'Occurrences are SAMPLES, not the full history: a bounded number is kept per issue while the count stays exact, and they age out after 30 days while the issue itself remains. An issue whose samples have expired returns an empty occurrences array — that is normal, not an error.',\n\t\t'',\n\t\t'Each occurrence has stack (raw, as the runtime printed it), context (tenant-supplied JSON, with sensitive keys already redacted), requestId, release, environment and createdAt.',\n\t\t'',\n\t\t'Inputs: issueId (required), occurrences (how many samples, default 5, max 50).',\n\t\t'',\n\t\t'Returns: { issue: {...}, occurrences: Array }.',\n\t\t'',\n\t\t\"Example: get_error_issue({ issueId: 12 }) → { issue: { title: 'TypeError: cart.total is not a function', occurrenceCount: 4187, … }, occurrences: [{ stack: ' at checkout (/app/src/checkout.ts:12:9)…', requestId: 'req_9f3c', release: 'a91f3c2', createdAt: '…' }] }.\",\n\t].join('\\n'),\n\tinput: {\n\t\tissueId: z.number().int().positive().describe('Numeric issue id from list_error_issues.'),\n\t\toccurrences: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.positive()\n\t\t\t.max(50)\n\t\t\t.optional()\n\t\t\t.describe('How many samples to include. Default 5.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst [issueRes, occRes] = await Promise.all([\n\t\t\tctx.api.get<IssueResponse>(`/api/errors/${teamId}/issues/${args.issueId}`),\n\t\t\tctx.api.get<OccurrencesResponse>(\n\t\t\t\t`/api/errors/${teamId}/issues/${args.issueId}/occurrences`,\n\t\t\t\t{ limit: String(args.occurrences ?? 5) },\n\t\t\t),\n\t\t]);\n\t\tconst occurrences = Array.isArray(occRes.occurrences) ? occRes.occurrences.map(shape) : [];\n\t\tconst issue = shape(issueRes.issue) as Record<string, unknown>;\n\t\treturn respond({\n\t\t\tsummary: `${String(issue['title'] ?? 'Issue')} — ${String(issue['occurrenceCount'] ?? 0)} occurrence(s), ${occurrences.length} sample(s) available.`,\n\t\t\tdata: { issue, occurrences },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'update_error_issue',\n\tcategory: 'errors',\n\tdescription: [\n\t\t'Resolve, ignore or reopen an error issue.',\n\t\t'',\n\t\t'When to use: after fixing a bug (resolve, so a recurrence is reported as a regression), or to silence noise you have decided to live with (ignore).',\n\t\t'',\n\t\t'`resolved` records the release it was resolved in. If the issue occurs again afterwards it reopens itself and fires an `error.issue_regressed` alert — which is the point of resolving rather than ignoring, and the reason not to resolve something you have not actually fixed.',\n\t\t'',\n\t\t'`ignored` keeps counting occurrences and stops telling anyone. An ignored issue never reopens itself, so this is the right choice for noise you have decided to live with, and the wrong choice for something you intend to fix.',\n\t\t'',\n\t\t'`unresolved` reopens it.',\n\t\t'',\n\t\t'Inputs: issueId (required), status (required).',\n\t\t'',\n\t\t'Returns: { issue: {...} } with the updated row.',\n\t\t'',\n\t\t\"Example: update_error_issue({ issueId: 12, status: 'resolved' }) → { issue: { status: 'resolved', resolvedInRelease: 'a91f3c2', … } }.\",\n\t].join('\\n'),\n\tinput: {\n\t\tissueId: z.number().int().positive(),\n\t\tstatus: z.enum(['unresolved', 'resolved', 'ignored']),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.api.patch<IssueResponse>(\n\t\t\t`/api/errors/${teamId}/issues/${args.issueId}`,\n\t\t\t{ status: args.status },\n\t\t);\n\t\treturn respond({\n\t\t\tsummary: `Issue ${args.issueId} is now ${args.status}.`,\n\t\t\tdata: { issue: shape(response.issue) },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'fix_error_in_dev_box',\n\tcategory: 'errors',\n\tdescription: [\n\t\t\"Turn an error issue into a coding-agent task in the project's dev box, with the repository already checked out.\",\n\t\t'',\n\t\t\"When to use: the issue is real, it is in the team's own code, and somebody is going to have to open the repository anyway. This writes the whole briefing so nobody has to reconstruct it from the dashboard.\",\n\t\t'',\n\t\t\"This is the thing a third-party error tracker structurally cannot do. The task carries the exception, the stack with the team's OWN frames marked (`>>`), the release it happened on, how often and to how many users, and a real request context — plus hard boundaries: work on a branch, do not deploy, do not delete tests, and stop and say so if the cause turns out not to be in this repository.\",\n\t\t'',\n\t\t\"It CREATES the task; it does not start the agent. Running spends the team's own agent tokens and is a separate decision made in the dev box UI.\",\n\t\t'',\n\t\t'Refused with 422 when the cause cannot be attributed to the repository — every stack frame inside a dependency, no usable stack at all, a browser extension, or a connection failure with no in-app frame. That gate is deliberate: an agent told to fix something it cannot reach does not refuse, it produces a confident and useless diff.',\n\t\t'',\n\t\t'Pressing this twice for one issue returns the existing task (alreadyExisted=true) rather than queueing a duplicate run.',\n\t\t'',\n\t\t\"Inputs: issueId (required), serviceId (optional — pin to a specific dev box; omitted, the project's box is used).\",\n\t\t'',\n\t\t'Returns: { task, alreadyExisted, box: { serviceId, name, asleep }, automodeEnabled }.',\n\t\t'',\n\t\t\"Example: fix_error_in_dev_box({ issueId: 12 }) → { task: { title: 'Fix error: TypeError in src/checkout.ts:12' }, alreadyExisted: false, box: { name: 'shop-dev', asleep: true }, automodeEnabled: false }.\",\n\t].join('\\n'),\n\tinput: {\n\t\tissueId: z.number().int().positive(),\n\t\tserviceId: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.positive()\n\t\t\t.optional()\n\t\t\t.describe(\"Dev box to pin the task to. Omitted, the project's box is used.\"),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst body: Record<string, number> = { issueId: args.issueId };\n\t\tif (args.serviceId !== undefined) body['serviceId'] = args.serviceId;\n\t\tconst response = await ctx.api.post<FixResponse>(\n\t\t\t`/api/dev-env-tasks/${teamId}/from-issue`,\n\t\t\tbody,\n\t\t);\n\t\tconst verb = response.alreadyExisted ? 'Already queued' : 'Queued';\n\t\tconst note = response.box.asleep\n\t\t\t? ' That box is asleep — resume it before running the task.'\n\t\t\t: '';\n\t\treturn respond({\n\t\t\tsummary: `${verb} in dev box \"${response.box.name}\": ${response.task.title ?? 'task'}.${note}`,\n\t\t\tdata: shape(response),\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'list_ingest_keys',\n\tcategory: 'errors',\n\tdescription: [\n\t\t\"List a service's error-ingest keys. Only prefixes and last-used timestamps — the key itself is stored as a hash and is never readable again.\",\n\t\t'',\n\t\t'When to use: checking whether a service is wired up for error reporting at all, or whether an old key is still in use before revoking it (`lastUsedAt` is stamped lazily, at most once a minute).',\n\t\t'',\n\t\t'Inputs: serviceId (required).',\n\t\t'',\n\t\t'Returns: { keys: Array } with id, publicId, name, prefix, lastUsedAt, createdAt.',\n\t\t'',\n\t\t\"Example: list_ingest_keys({ serviceId: 48 }) → { keys: [{ id: 3, name: 'default', prefix: 'ing_9f3c1ab2', lastUsedAt: '2026-08-21T09:14:00Z' }] }.\",\n\t].join('\\n'),\n\tinput: { serviceId: z.number().int().positive() },\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.api.get<IngestKeysResponse>(\n\t\t\t`/api/services/${teamId}/${args.serviceId}/ingest-keys`,\n\t\t);\n\t\tconst keys = Array.isArray(response.keys) ? response.keys.map(shape) : [];\n\t\treturn respond({\n\t\t\tsummary:\n\t\t\t\tkeys.length === 0\n\t\t\t\t\t? 'No ingest keys on this service — it cannot report errors yet.'\n\t\t\t\t\t: `${keys.length} ingest key${keys.length === 1 ? '' : 's'} on this service.`,\n\t\t\tdata: { keys },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'create_ingest_key',\n\tcategory: 'errors',\n\tdescription: [\n\t\t'Mint a write-only error-ingest key for a service, so its application can report exceptions.',\n\t\t'',\n\t\t'When to use: setting a service up for error tracking for the first time, or rotating a key that has leaked or is being retired.',\n\t\t'',\n\t\t'The response is the ONLY place the plaintext key ever exists — it is stored as a SHA-256 hash and cannot be shown again. Put it wherever the application reads it from (an env var, usually) before discarding the response.',\n\t\t'',\n\t\t'The key is deliberately low-privilege: it can create error events for this ONE service and can do nothing else — it cannot read the issues it created, list services, or touch anything on the team. That is why shipping it inside the application, including a browser bundle where it is world-readable, is expected rather than a mistake.',\n\t\t'',\n\t\t'Reporting is a plain HTTP POST, no SDK required: POST {baseUrl}/api/ingest/errors/{key} with {\"events\":[{\"type\",\"value\",\"stack\",\"level\",\"context\"}]}, up to 100 events per request.',\n\t\t'',\n\t\t'Inputs: serviceId (required), name (optional label, default \"default\"). Max 5 keys per service — the extras exist so a key can be rotated without downtime (create the second, ship it, delete the first).',\n\t\t'',\n\t\t'Returns: { key: { id, publicId, name, prefix, key } } where `key` is the plaintext.',\n\t\t'',\n\t\t\"Example: create_ingest_key({ serviceId: 48 }) → { id: 4, name: 'default', prefix: 'ing_1b7d40ae', key: 'ing_1b7d40ae…' }.\",\n\t].join('\\n'),\n\tinput: {\n\t\tserviceId: z.number().int().positive(),\n\t\tname: z.string().min(1).max(100).optional().describe('Label. Default \"default\".'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.api.post<IngestKeyResponse>(\n\t\t\t`/api/services/${teamId}/${args.serviceId}/ingest-keys`,\n\t\t\t{ name: args.name ?? 'default' },\n\t\t);\n\t\treturn respond({\n\t\t\tsummary:\n\t\t\t\t'Ingest key created. The plaintext key is in the payload and is not retrievable again — store it now.',\n\t\t\tdata: shape(response.key),\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'delete_ingest_key',\n\tcategory: 'errors',\n\tdescription: [\n\t\t'Revoke an error-ingest key. It stops working immediately, including on API replicas that had it cached.',\n\t\t'',\n\t\t'When to use: finishing a key rotation, or containing a key that leaked somewhere it should not have.',\n\t\t'',\n\t\t'Anything still posting with it starts getting 401s, so revoke the OLD key after the new one is deployed, not before.',\n\t\t'',\n\t\t'Inputs: serviceId, keyId (both required).',\n\t\t'',\n\t\t'Returns: { success: true }.',\n\t\t'',\n\t\t'Example: delete_ingest_key({ serviceId: 48, keyId: 3 }) → { success: true }.',\n\t].join('\\n'),\n\tinput: {\n\t\tserviceId: z.number().int().positive(),\n\t\tkeyId: z.number().int().positive(),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tawait ctx.api.delete(`/api/services/${teamId}/${args.serviceId}/ingest-keys/${args.keyId}`);\n\t\treturn respond({ summary: `Ingest key ${args.keyId} revoked.` });\n\t},\n});\n","import { z } from 'zod';\n\nimport { respond, respondError } from '../lib/respond.js';\nimport { defineTool } from '../registry.js';\n\ninterface Installation {\n\tid: number;\n\taccountLogin: string;\n}\n\ninterface SyncedRepo {\n\tid: number;\n\tfullName: string;\n}\n\ninterface ConnectedRepo {\n\tid: number;\n\tfullName: string;\n\tname: string;\n\tdefaultBranch: string;\n\tisPrivate: boolean;\n\thtmlUrl: string;\n\tlanguage: string | null;\n\taccountLogin: string;\n}\n\n/** The agent-facing shape: the id create_service wants, and enough to pick it. */\nfunction shapeRepo(repo: ConnectedRepo) {\n\treturn {\n\t\tid: repo.id,\n\t\tfullName: repo.fullName,\n\t\tdefaultBranch: repo.defaultBranch,\n\t\tprivate: repo.isPrivate,\n\t\turl: repo.htmlUrl,\n\t\t...(repo.language ? { language: repo.language } : {}),\n\t};\n}\n\ndefineTool({\n\tname: 'list_github_repos',\n\tcategory: 'github',\n\tdescription: [\n\t\t'Every GitHub repository this team can deploy, with the `id` create_service takes — and the `owner/name` you can pass instead of it.',\n\t\t'',\n\t\t'When to use: before create_service or create_standalone_dev_environment with a repo source. `github_repo_id` is a HostStack row id, NOT the id GitHub shows — it appears in no clone URL and no GitHub API response, so it cannot be derived and must not be guessed. (Guessing used to be survivable-looking: the create returned 200 and the mistake surfaced one step later as `Invalid git clone URL` from the deploy. It is now refused at create time.)',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - q (optional): case-insensitive substring of the full name (\"owner/name\"). Omit for everything.',\n\t\t'',\n\t\t'Returns: { items: [{ id, fullName, defaultBranch, private, url, language? }], total } — `total` is the count before `q` filtered it.',\n\t\t'',\n\t\t'If the repo you just pushed is missing, call sync_github_repos first: HostStack cannot see a brand-new repository until an installation re-sync runs.',\n\t\t'',\n\t\t'Example: list_github_repos({ q: \"stenshoppen\" }) → { items: [{ id: 143, fullName: \"acme/stenshoppen\", defaultBranch: \"master\", private: true }], total: 45 }',\n\t\t'Then either: create_service({ …, github_repo: \"acme/stenshoppen\" }) — or the id, if you prefer.',\n\t].join('\\n'),\n\tinput: {\n\t\tq: z\n\t\t\t.string()\n\t\t\t.min(1)\n\t\t\t.max(200)\n\t\t\t.optional()\n\t\t\t.describe('Case-insensitive substring of \"owner/name\". Omit to list everything.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst { repos } = await ctx.api.get<{ repos: ConnectedRepo[] }>(\n\t\t\t`/api/github/${teamId}/repos`,\n\t\t);\n\n\t\tconst needle = args.q?.trim().toLowerCase();\n\t\tconst matched = needle\n\t\t\t? repos.filter((r) => r.fullName.toLowerCase().includes(needle))\n\t\t\t: repos;\n\t\tconst items = matched.map(shapeRepo).sort((a, b) => a.fullName.localeCompare(b.fullName));\n\n\t\tif (repos.length === 0) {\n\t\t\treturn respond({\n\t\t\t\tsummary:\n\t\t\t\t\t'No GitHub repositories are connected to this team. Install the HostStack GitHub App from the dashboard (Settings → GitHub), then call sync_github_repos.',\n\t\t\t\tdata: { items, total: 0 },\n\t\t\t});\n\t\t}\n\t\tconst summary = needle\n\t\t\t? items.length === 0\n\t\t\t\t? `No connected repository matches \"${args.q}\" (${repos.length} connected). Push it, then call sync_github_repos.`\n\t\t\t\t: `${items.length} of ${repos.length} connected repositories match \"${args.q}\".`\n\t\t\t: `${items.length} connected repositor${items.length === 1 ? 'y' : 'ies'}.`;\n\t\treturn respond({ summary, data: { items, total: repos.length } });\n\t},\n});\n\ndefineTool({\n\tname: 'sync_github_repos',\n\tcategory: 'github',\n\tdescription: [\n\t\t'Re-sync the repository list from GitHub for every connected GitHub App installation, and return what that now exposes.',\n\t\t'',\n\t\t\"When to use: right after pushing a brand-new repository, HostStack cannot see it until an installation re-sync runs — so create_service with a github_repo_id or github_repo would fail. Call this first. (Same operation as the dashboard's refresh icon on the repo picker.) To read the list WITHOUT re-syncing, call list_github_repos.\",\n\t\t'',\n\t\t'No inputs.',\n\t\t'',\n\t\t'Returns: { installations: number, accounts: [{ account, count }], totalRepos, repos: [{ id, fullName }] } — `repos` carries the ids create_service takes, so a sync is enough to act on; it does not have to be followed by a list.',\n\t\t'',\n\t\t'Example: sync_github_repos({}) → { installations: 1, accounts: [{ account: \"acme\", count: 12 }], totalRepos: 12, repos: [{ id: 143, fullName: \"acme/stenshoppen\" }, …] }',\n\t].join('\\n'),\n\tinput: {},\n\thandler: async (_args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst { installations } = await ctx.api.get<{ installations: Installation[] }>(\n\t\t\t`/api/github/${teamId}/installations`,\n\t\t);\n\t\tif (installations.length === 0) {\n\t\t\treturn respondError(\n\t\t\t\t'No GitHub installations connected. Install the HostStack GitHub App from the dashboard (Settings → GitHub) first.',\n\t\t\t);\n\t\t}\n\n\t\tconst perAccount: { account: string; count: number }[] = [];\n\t\tconst synced: { id: number; fullName: string }[] = [];\n\t\tfor (const inst of installations) {\n\t\t\tconst { repos } = await ctx.api.post<{ repos: SyncedRepo[] }>(\n\t\t\t\t`/api/github/${teamId}/installations/${inst.id}/sync`,\n\t\t\t);\n\t\t\tperAccount.push({ account: inst.accountLogin, count: repos.length });\n\t\t\tfor (const repo of repos) synced.push({ id: repo.id, fullName: repo.fullName });\n\t\t}\n\t\tsynced.sort((a, b) => a.fullName.localeCompare(b.fullName));\n\n\t\treturn respond({\n\t\t\tsummary: `Synced ${synced.length} repositories across ${installations.length} installation(s).`,\n\t\t\tdata: {\n\t\t\t\tinstallations: installations.length,\n\t\t\t\taccounts: perAccount,\n\t\t\t\ttotalRepos: synced.length,\n\t\t\t\trepos: synced,\n\t\t\t},\n\t\t});\n\t},\n});\n","import { z } from 'zod';\n\nimport { defineTool } from '../registry.js';\nimport { respond } from '../lib/respond.js';\nimport { shape } from '../lib/shape.js';\n\ninterface ReportIssueResponse {\n\tticket: { id: number; publicId: string };\n}\n\ndefineTool({\n\tname: 'report_issue',\n\tcategory: 'support',\n\tdescription: [\n\t\t'File a bug or platform fault with the HostStack team. Use this when something on the PLATFORM is broken in a way you cannot fix from where you stand — a deploy that fails identically no matter what you change, a dev box that will not start, an API that returns the wrong thing, an error message that does not match what actually happened.',\n\t\t'',\n\t\t'When to use: you have already looked. This is not a substitute for reading logs (get_deploy_logs, get_service_logs) or for diagnose_deploy — it is what you do once you have a specific fault and no way to act on it. A report that says \"the deploy failed\" is worth less than no report; one that says \"every deploy since 09:03 fails at container create with 404 No such image, but the image pulls fine by hand\" is what gets fixed.',\n\t\t'',\n\t\t\"When NOT to use: an application bug in the user's own code, a failing test, a misconfigured env var, or anything the user asked you to change. Those are your job, not a platform fault. Do not file the same issue twice — if you already reported it this session, say so instead.\",\n\t\t'',\n\t\t'Diagnostic context is attached SERVER-SIDE, so you do not need to paste it: passing `serviceId` attaches the service and its most recent deploy, and the last 40 lines of that deploy log go with it. Pass `deployId` to pin a specific deploy instead of the newest. Describe what you observed and what you expected — the evidence is collected for you.',\n\t\t'',\n\t\t'Inputs: title (required, one line), description (required — what happened, what you expected, what you already ruled out), severity (low|normal|high|urgent, default normal), serviceId (optional), deployId (optional), databaseId (optional).',\n\t\t'',\n\t\t'Rate-limited to 5 reports per minute per team. Filing one notifies the HostStack team and opens a ticket the user can see and reply to.',\n\t\t'',\n\t\t'Returns: { ticket: { id, publicId } } — quote the publicId to the user so they can follow it up.',\n\t\t'',\n\t\t\"Example: report_issue({ title: 'Dev box deploys fail with 404 No such image', description: 'Every deploy of svc 172 since 2026-08-22 09:03 fails at container create with `404 No such image: …/dev-env:latest`. The deploy log reports \\\"Image ready in 1s\\\" for a 5.5 GB image, so the pull is not happening. The image pulls fine by hand from the same registry.', severity: 'high', serviceId: 172 }) → { ticket: { publicId: 'tkt_…' } }\",\n\t].join('\\n'),\n\tinput: {\n\t\ttitle: z.string().min(1).max(300).describe('One-line summary of the fault.'),\n\t\tdescription: z\n\t\t\t.string()\n\t\t\t.min(1)\n\t\t\t.max(10_000)\n\t\t\t.describe('What happened, what you expected instead, and what you already ruled out.'),\n\t\tseverity: z\n\t\t\t.enum(['low', 'normal', 'high', 'urgent'])\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t'Default normal. Use high/urgent only when something is DOWN or losing data — not for \"this is annoying\".',\n\t\t\t),\n\t\tserviceId: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.positive()\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t'The affected service. Attaches the service, its latest deploy, and that deploy log tail automatically.',\n\t\t\t),\n\t\tdeployId: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.positive()\n\t\t\t.optional()\n\t\t\t.describe('Pin a specific deploy instead of the service’s most recent one.'),\n\t\tdatabaseId: z.number().int().positive().optional().describe('The affected database.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst body: Record<string, unknown> = {\n\t\t\ttitle: args.title,\n\t\t\tdescription: args.description,\n\t\t\tseverity: args.severity ?? 'normal',\n\t\t};\n\t\tif (args.serviceId !== undefined) body['serviceId'] = args.serviceId;\n\t\tif (args.deployId !== undefined) body['deployId'] = args.deployId;\n\t\tif (args.databaseId !== undefined) body['databaseId'] = args.databaseId;\n\n\t\tconst response = await ctx.api.post<ReportIssueResponse>(\n\t\t\t`/api/issue-reports/${teamId}`,\n\t\t\tbody,\n\t\t);\n\t\treturn respond({\n\t\t\tsummary: `Issue reported as ${response.ticket.publicId}. Tell the user the reference so they can follow it up — and do not file this one again.`,\n\t\t\tdata: shape(response.ticket),\n\t\t});\n\t},\n});\n","import { defineTool, listToolDefinitions } from '../registry.js';\nimport { respond } from '../lib/respond.js';\nimport { shapeTeam, shapeUser } from '../lib/shape.js';\n// Reuse the build-time-injected package version (the same value the MCP\n// `initialize` handshake advertises — see server-factory's PACKAGE_VERSION,\n// which is itself just MCP_VERSION). Importing from ../version.js keeps this in\n// one place and avoids an @hoststack/shared import (workspace-trap: it would\n// drag the whole monorepo into the published package).\nimport { MCP_VERSION } from '../version.js';\n\ninterface MeResponse {\n\tuser: unknown;\n\tteam?: unknown;\n}\n\n/**\n * The Dev Box tools this MCP build ships. A driving agent can compare this list\n * (returned by `describe_mcp`) against what its client actually surfaced to\n * detect a STALE npx/npm cache silently presenting an old tool set.\n *\n * KEEP IN SYNC with the tool names registered in ./services.ts. We list the\n * names as a literal (rather than filtering the registry by category) so the\n * marker stays meaningful even if the underlying categories are reshuffled —\n * these are the dev-env capabilities a caller most often finds missing.\n */\nconst DEV_ENV_TOOL_NAMES = [\n\t'create_dev_environment',\n\t'create_standalone_dev_environment',\n\t'spin_up_dev_environment',\n\t'list_dev_environments',\n\t'resize_dev_environment',\n\t'delete_dev_environment',\n] as const;\n\ndefineTool({\n\tname: 'get_me',\n\tcategory: 'meta',\n\tdescription: [\n\t\t'Return the user and team identity bound to the current API key. Useful at the start of a conversation to confirm which account the agent is operating on, especially when a user has multiple HostStack environments.',\n\t\t'',\n\t\t'When to use: orient yourself at the start of a session, confirm which team the API key targets, or surface the email/team-name in a reply to the user.',\n\t\t'',\n\t\t'Returns: { user: { id, email, name, ... }, team?: { id, publicId, name, plan, ... } }.',\n\t\t'',\n\t\t'Example: get_me() → { user: { id: 1, email: \"ada@…\", … }, team: { id: 7, name: \"acme\", plan: \"pro\" } }',\n\t].join('\\n'),\n\tinput: {},\n\thandler: async (_args, ctx) => {\n\t\tconst me = await ctx.api.get<MeResponse>('/api/auth/me');\n\t\tconst data: { user: ReturnType<typeof shapeUser>; team?: ReturnType<typeof shapeTeam> } = {\n\t\t\tuser: shapeUser(me.user),\n\t\t};\n\t\tif (me.team) data.team = shapeTeam(me.team);\n\t\tconst teamLabel =\n\t\t\tme.team && typeof me.team === 'object' && 'name' in me.team\n\t\t\t\t? ` on team ${(me.team as { name: string }).name}`\n\t\t\t\t: '';\n\t\tconst userEmail =\n\t\t\tme.user && typeof me.user === 'object' && 'email' in me.user\n\t\t\t\t? (me.user as { email: string }).email\n\t\t\t\t: 'unknown';\n\t\treturn respond({ summary: `Authenticated as ${userEmail}${teamLabel}.`, data });\n\t},\n});\n\ndefineTool({\n\tname: 'describe_mcp',\n\tcategory: 'meta',\n\tdescription: [\n\t\t'Report this MCP build: its package version, total tool count, and the list of Dev Box tools it exposes. Read-only — touches no account data.',\n\t\t'',\n\t\t'When to use: at the start of a session to confirm the live MCP build, OR when a Dev Box tool you expect (create_dev_environment, create_standalone_dev_environment, spin_up_dev_environment, list_dev_environments, resize_dev_environment, delete_dev_environment) seems to be missing. A STALE npx/npm cache can silently present an old tool set with no other signal — call this and check `devEnvToolsPresent`; if it is false, the cached build predates the Dev Box tools and the user should refresh it (e.g. `npx --yes @hoststack.dev/mcp@latest` / clear the npx cache).',\n\t\t'',\n\t\t'Note on terminology: a \"Dev Box\" (a.k.a. Cloud Dev Box) is an agentic cloud box you code inside (create_dev_environment & friends). It is NOT a project \"environment\" (create_environment), which is a deployment target like Production / Staging / Preview. This tool only reports the Dev Box tool set.',\n\t\t'',\n\t\t'Inputs: none.',\n\t\t'',\n\t\t'Returns: { version, toolCount, devEnvTools: string[], devEnvToolsPresent: boolean } — version is the @hoststack.dev/mcp package version, toolCount is every registered tool, devEnvTools is the expected Dev Box tool names, and devEnvToolsPresent is true only if all of them are actually registered in this build.',\n\t\t'',\n\t\t'Example: describe_mcp() → { version: \"0.14.0\", toolCount: 30, devEnvTools: [\"create_dev_environment\", …], devEnvToolsPresent: true }',\n\t].join('\\n'),\n\tinput: {},\n\thandler: async (_args, _ctx) => {\n\t\tconst registered = new Set(listToolDefinitions().map((t) => t.name));\n\t\tconst devEnvToolsPresent = DEV_ENV_TOOL_NAMES.every((name) => registered.has(name));\n\t\tconst data = {\n\t\t\tversion: MCP_VERSION,\n\t\t\ttoolCount: registered.size,\n\t\t\tdevEnvTools: [...DEV_ENV_TOOL_NAMES],\n\t\t\tdevEnvToolsPresent,\n\t\t};\n\t\tconst summary = devEnvToolsPresent\n\t\t\t? `hoststack MCP v${MCP_VERSION} — ${data.toolCount} tools, all ${DEV_ENV_TOOL_NAMES.length} Dev Box tools present.`\n\t\t\t: `hoststack MCP v${MCP_VERSION} — ${data.toolCount} tools, but Dev Box tools are MISSING (stale build — refresh the npx/npm cache).`;\n\t\treturn respond({ summary, data });\n\t},\n});\n","import { z } from 'zod';\n\nimport { defineTool } from '../registry.js';\nimport { respond } from '../lib/respond.js';\nimport { shape, shapeList } from '../lib/shape.js';\n\n/**\n * Source of truth: packages/shared/src/schemas/notification-channel.ts.\n * Copied rather than imported because the MCP package's Zod schemas are\n * what the model sees — pulling these from `@hoststack/shared` at\n * runtime would add a dependency that the published `@hoststack.dev/mcp`\n * shouldn't carry.\n *\n * \"Kept in sync manually\" is what this used to say, and it wasn't: the list\n * sat nine events behind for two weeks while `machine.offline` — the only\n * warning a team gets that their own hardware stopped serving — was rejected\n * at this tool's boundary. It is now checked against shared by\n * test/shared-parity.test.ts, which is a dev-time import and never reaches\n * the bundle. Add an event in shared and this file fails its own test until\n * it follows.\n *\n * This is not documentation. It is the `z.enum` on create/update below, so a\n * value missing here cannot be subscribed to at all — and because\n * `update_notification_channel` REPLACES the list, a channel configured in\n * the dashboard with a newer event could not be read back and written again.\n */\nexport const NOTIFICATION_EVENTS = [\n\t'deploy.started',\n\t'deploy.succeeded',\n\t'deploy.failed',\n\t'deploy.failed_consecutive',\n\t'service.created',\n\t'service.deleted',\n\t'service.suspended',\n\t'service.resumed',\n\t'service.restart_failed',\n\t'service.no_running_container',\n\t'service.health_check_failed',\n\t'service.acme_cert_failed',\n\t'service.resource_alert',\n\t'service.pressure_sustained',\n\t'service.pressure_recovered',\n\t'service.uptime_down',\n\t'service.uptime_recovered',\n\t'watchdog.reported_down',\n\t'watchdog.reported_recovered',\n\t'watchdog.silent',\n\t'error.issue_new',\n\t'error.issue_regressed',\n\t'git.auth_failed',\n\t'cron.execution_failed',\n\t'cron.schedule_missed',\n\t'workflow.failed',\n\t'devenv.agent.needs_input',\n\t'devenv.agent.finished',\n\t'devenv.task.created',\n\t'devenv.task.needs_input',\n\t'devenv.task.finished',\n\t'database.backup_failed',\n\t'database.backup_overdue',\n\t'database.failed',\n\t'database.restore_failed',\n\t'volume.backup_failed',\n\t'volume.backup_overdue',\n\t'domain.registrant_verification_lapsed',\n\t'service.auto_restarted',\n\t'machine.offline',\n\t'machine.online',\n\t'billing.invoice',\n\t'billing.payment_failed',\n\t'billing.spend_limit',\n] as const;\n\ndefineTool({\n\tname: 'list_notification_channels',\n\tcategory: 'alerts',\n\tdescription: [\n\t\t'List configured notification channels for the team (Slack/Discord webhooks + email recipients). Each channel has a list of events it subscribes to — fire only happens when an event in that list occurs.',\n\t\t'',\n\t\t\"When to use: setting up alerts ('which channels are notifying us on deploy failures?'), auditing why someone got/didn't get paged, or before subscribing a new event.\",\n\t\t'',\n\t\t'Returns: { items: Channel[] } — each channel includes id, type, name, webhookUrl (masked), active, events, createdAt.',\n\t\t'',\n\t\t\"Example: list_notification_channels() → { items: [{ id: 3, type: 'slack', name: 'eng-alerts', events: ['deploy.failed', 'git.auth_failed'], … }] }\",\n\t].join('\\n'),\n\tinput: {},\n\thandler: async (_args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.notifications.listChannels(teamId);\n\t\tconst data = shapeList(response, 'channels', shape);\n\t\tconst summary =\n\t\t\tdata.items.length === 0\n\t\t\t\t? 'No notification channels yet — create one with create_notification_channel.'\n\t\t\t\t: `Found ${data.items.length} notification channel${data.items.length === 1 ? '' : 's'}.`;\n\t\treturn respond({ summary, data });\n\t},\n});\n\ndefineTool({\n\tname: 'create_notification_channel',\n\tcategory: 'alerts',\n\tdescription: [\n\t\t'Create a Slack, Discord, or email notification channel and subscribe it to a list of events.',\n\t\t'',\n\t\t'When to use: setting up alert delivery for a team — paging the on-call when a deploy fails, posting to #incidents when git auth breaks, emailing the owner on ACME renewal failure.',\n\t\t'',\n\t\t'For type=email, `webhook_url` is the recipient email address. For type=slack or type=discord, it is the incoming webhook URL. The URL is stored encrypted server-side and masked in list responses.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - type: \"slack\" | \"discord\" | \"email\".',\n\t\t' - name: human-readable label (1–128 chars).',\n\t\t' - webhook_url: Slack/Discord webhook URL OR email address.',\n\t\t' - events: list of event names to subscribe to. Pass an empty list to create a channel that fires for nothing (manual subscribe later with update_notification_channel).',\n\t\t'',\n\t\t// Rendered from the enum instead of retyped: the hand-written copy of\n\t\t// this sentence went stale the same way the enum did, and a description\n\t\t// that disagrees with the schema teaches the model to send values the\n\t\t// tool then rejects.\n\t\t`Valid events: ${NOTIFICATION_EVENTS.join(', ')}.`,\n\t\t'',\n\t\t'Returns: { channel: Channel }.',\n\t\t'',\n\t\t\"Example: create_notification_channel({ type: 'slack', name: 'eng-alerts', webhook_url: 'https://hooks.slack.com/…', events: ['deploy.failed', 'git.auth_failed', 'service.restart_failed'] })\",\n\t].join('\\n'),\n\tinput: {\n\t\ttype: z.enum(['slack', 'discord', 'email']).describe('Channel type.'),\n\t\tname: z.string().min(1).max(128).describe('Human-readable label.'),\n\t\twebhook_url: z\n\t\t\t.string()\n\t\t\t.max(500)\n\t\t\t.describe('Slack/Discord webhook URL or email address (when type=email).'),\n\t\tevents: z\n\t\t\t.array(z.enum(NOTIFICATION_EVENTS))\n\t\t\t.describe(\n\t\t\t\t'List of events the channel subscribes to. Empty list = subscribe to nothing.',\n\t\t\t),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\t// Cast for the same reason update_notification_channel below casts, plus\n\t\t// one of its own: the SDK's `NotificationChannelEvent` union is a third\n\t\t// hand-copy of this list and is still nine values behind shared, so\n\t\t// events the API accepts do not type-check through it. The values are\n\t\t// Zod-validated against the mirrored enum on the way in, and the cast\n\t\t// stops being load-bearing the moment that union is regenerated.\n\t\tconst response = await ctx.hoststack.notifications.createChannel(teamId, {\n\t\t\ttype: args.type,\n\t\t\tname: args.name,\n\t\t\twebhookUrl: args.webhook_url,\n\t\t\tevents: args.events as Parameters<\n\t\t\t\ttypeof ctx.hoststack.notifications.createChannel\n\t\t\t>[1]['events'],\n\t\t});\n\t\tconst data = { channel: shape(response.channel) };\n\t\treturn respond({\n\t\t\tsummary: `Created ${args.type} channel \"${args.name}\" subscribed to ${args.events.length} event${args.events.length === 1 ? '' : 's'}.`,\n\t\t\tdata,\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'update_notification_channel',\n\tcategory: 'alerts',\n\tdescription: [\n\t\t'Update a notification channel: rename, enable/disable, or change its event subscription list. The webhook URL and type are immutable — create a new channel if those need to change.',\n\t\t'',\n\t\t'When to use: silencing a noisy channel (active=false), adding a new event to an existing list, or relabeling.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - channel_id: numeric id of the channel.',\n\t\t' - name (optional): new label.',\n\t\t' - active (optional): false to silence, true to re-enable.',\n\t\t' - events (optional): replace the full subscription list. Passing [] silences all events without disabling the channel.',\n\t\t'',\n\t\t'Returns: { channel: Channel }.',\n\t\t'',\n\t\t\"Example: update_notification_channel({ channel_id: 3, events: ['deploy.failed', 'service.restart_failed', 'git.auth_failed'] })\",\n\t].join('\\n'),\n\tinput: {\n\t\tchannel_id: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.positive()\n\t\t\t.describe('Numeric channel id from list_notification_channels.'),\n\t\tname: z.string().min(1).max(128).optional().describe('New label.'),\n\t\tactive: z.boolean().optional().describe('false silences without deleting.'),\n\t\tevents: z\n\t\t\t.array(z.enum(NOTIFICATION_EVENTS))\n\t\t\t.optional()\n\t\t\t.describe('Replaces the full subscription list.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst update: { name?: string; active?: boolean; events?: readonly string[] } = {};\n\t\tif (args.name !== undefined) update.name = args.name;\n\t\tif (args.active !== undefined) update.active = args.active;\n\t\tif (args.events !== undefined) update.events = args.events;\n\n\t\tif (Object.keys(update).length === 0) {\n\t\t\treturn respond({ summary: 'No fields to update.', data: {} });\n\t\t}\n\t\t// Cast: SDK's events type is the typed enum array; passing through\n\t\t// from the Zod-validated args is safe at the type level even if\n\t\t// TS can't bridge the readonly tuple to the SDK signature.\n\t\tconst response = await ctx.hoststack.notifications.updateChannel(\n\t\t\tteamId,\n\t\t\targs.channel_id,\n\t\t\tupdate as Parameters<typeof ctx.hoststack.notifications.updateChannel>[2],\n\t\t);\n\t\tconst data = { channel: shape(response.channel) };\n\t\treturn respond({\n\t\t\tsummary: `Updated notification channel #${args.channel_id}.`,\n\t\t\tdata,\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'delete_notification_channel',\n\tcategory: 'alerts',\n\tdescription: [\n\t\t'Permanently delete a notification channel. Use update_notification_channel({ active: false }) to silence without deleting if you might want it back later.',\n\t\t'',\n\t\t'When to use: a channel is no longer needed (decommissioned Slack workspace, team member left, webhook leaked) and you want it gone from the team config entirely.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - channel_id: numeric id of the channel.',\n\t\t'',\n\t\t'Returns: { ok: true }.',\n\t\t'',\n\t\t'Example: delete_notification_channel({ channel_id: 3 }) → { ok: true }',\n\t].join('\\n'),\n\tinput: {\n\t\tchannel_id: z.number().int().positive().describe('Numeric channel id.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tawait ctx.hoststack.notifications.deleteChannel(teamId, args.channel_id);\n\t\treturn respond({\n\t\t\tsummary: `Deleted notification channel #${args.channel_id}.`,\n\t\t\tdata: { ok: true },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'test_notification_channel',\n\tcategory: 'alerts',\n\tdescription: [\n\t\t'Fire a synthetic test event to the channel so the user can confirm the webhook is wired up correctly. The webhook receives an unmistakeable \"test message\" payload.',\n\t\t'',\n\t\t\"When to use: after creating a channel, or when alerts stop arriving — quickest way to tell whether the issue is on HostStack's side or the channel's.\",\n\t\t'',\n\t\t'Inputs:',\n\t\t' - channel_id: numeric id of the channel.',\n\t\t'',\n\t\t'Returns: { success: boolean, error?: string } — false + error string when the webhook returned non-2xx.',\n\t\t'',\n\t\t'Example: test_notification_channel({ channel_id: 3 }) → { success: true }',\n\t].join('\\n'),\n\tinput: {\n\t\tchannel_id: z.number().int().positive().describe('Numeric channel id.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.notifications.testChannel(teamId, args.channel_id);\n\t\tconst data = shape(response);\n\t\tconst okFlag =\n\t\t\tdata && typeof data === 'object' && 'success' in data\n\t\t\t\t? (data as { success: boolean }).success\n\t\t\t\t: false;\n\t\treturn respond({\n\t\t\tsummary: okFlag\n\t\t\t\t? `Test event delivered to channel #${args.channel_id}.`\n\t\t\t\t: `Test event FAILED for channel #${args.channel_id}: ${(data as { error?: string }).error ?? 'unknown error'}.`,\n\t\t\tdata,\n\t\t});\n\t},\n});\n","import { z } from 'zod';\n\nimport { defineTool } from '../registry.js';\nimport { respond } from '../lib/respond.js';\nimport { shapeList, shapeProject } from '../lib/shape.js';\n\n// HostStack region IDs (matches @hoststack/shared REGION_IDS and the SDK's\n// internal RegionId). The SDK narrows region to this union but doesn't export\n// the type name, so we mirror the literals here — structurally identical, so\n// it stays assignable to the SDK's create-project param.\n//\n// AVAILABLE_REGION_IDS mirrors @hoststack/shared's list of regions we actually\n// run workers in — only these may be selected when creating a project, since a\n// project pinned to a region with no fleet can never deploy. Keep in lockstep\n// with the shared constant when a new region goes live.\nconst AVAILABLE_REGION_IDS = ['eu-central-1'] as const;\ntype RegionId = (typeof AVAILABLE_REGION_IDS)[number];\n\ndefineTool({\n\tname: 'list_projects',\n\tcategory: 'projects',\n\tdescription: [\n\t\t'List every project in the active HostStack team.',\n\t\t'',\n\t\t'When to use: the agent needs an overview of what projects exist before drilling into services, deploys, or databases. Use this as the first step when the user mentions a project by name and you need to resolve its ID.',\n\t\t'',\n\t\t'Returns: { items: Project[] } — each project includes id, publicId, name, description, createdAt.',\n\t\t'',\n\t\t'Example: list_projects() → { items: [{ id: 12, publicId: \"prj_…\", name: \"billing-api\", … }] }',\n\t].join('\\n'),\n\tinput: {},\n\thandler: async (_args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.projects.list(teamId);\n\t\tconst data = shapeList(response, 'projects', shapeProject);\n\t\tconst summary =\n\t\t\tdata.items.length === 0\n\t\t\t\t? 'No projects yet — create one in the dashboard or via create_project.'\n\t\t\t\t: `Found ${data.items.length} project${data.items.length === 1 ? '' : 's'}.`;\n\t\treturn respond({ summary, data });\n\t},\n});\n\ndefineTool({\n\tname: 'create_project',\n\tcategory: 'projects',\n\tdescription: [\n\t\t'Create a new project (logical grouping of services + databases).',\n\t\t'',\n\t\t\"When to use: the user wants to set up a new app or environment in HostStack. Projects are a free organisational layer — they don't cost anything until you add services or databases inside them.\",\n\t\t'',\n\t\t'Inputs:',\n\t\t' - name: human-readable project name (1–60 chars).',\n\t\t' - description (optional): short blurb shown in the dashboard.',\n\t\t' - region (optional): \"eu-central-1\" (Falkenstein). Currently the only region with available capacity; defaults to eu-central-1.',\n\t\t'',\n\t\t'Returns: { project: Project } — includes the new id and publicId.',\n\t\t'',\n\t\t'Example: create_project({ name: \"billing-api\", description: \"Stripe webhooks\", region: \"eu-central-1\" }) → { project: { id: 12, publicId: \"prj_…\", … } }',\n\t].join('\\n'),\n\tinput: {\n\t\tname: z.string().min(1).max(60).describe('Project name (1–60 chars).'),\n\t\tdescription: z.string().max(500).optional().describe('Short description (≤500 chars).'),\n\t\tregion: z\n\t\t\t.enum(AVAILABLE_REGION_IDS)\n\t\t\t.optional()\n\t\t\t.describe('Region: eu-central-1 (Falkenstein) — currently the only available region.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst input: { name: string; description?: string; region?: RegionId } = {\n\t\t\tname: args.name,\n\t\t};\n\t\tif (args.description !== undefined) input.description = args.description;\n\t\tif (args.region !== undefined) input.region = args.region;\n\t\tconst response = await ctx.hoststack.projects.create(teamId, input);\n\t\tconst data = { project: shapeProject(response.project) };\n\t\tconst publicId =\n\t\t\tdata.project && 'publicId' in data.project\n\t\t\t\t? (data.project as { publicId: string }).publicId\n\t\t\t\t: 'unknown';\n\t\treturn respond({ summary: `Created project \"${args.name}\" (${publicId}).`, data });\n\t},\n});\n\ndefineTool({\n\tname: 'update_project',\n\tcategory: 'projects',\n\tdescription: [\n\t\t'Rename a project or update its description.',\n\t\t'',\n\t\t'When to use: the user wants to fix a typo in a project name, attach a clearer description, or align the dashboard label with their internal naming.',\n\t\t'',\n\t\t'Inputs (all optional, at least one required):',\n\t\t' - project_id: publicId of the project (required).',\n\t\t' - name: new name (1–60 chars).',\n\t\t' - description: new description (≤500 chars).',\n\t\t'',\n\t\t'Returns: { project: Project } — the updated record.',\n\t\t'',\n\t\t'Example: update_project({ project_id: \"prj_abc\", name: \"billing-prod\" }) → { project: { name: \"billing-prod\", … } }',\n\t].join('\\n'),\n\tinput: {\n\t\tproject_id: z.string().describe('Project publicId.'),\n\t\tname: z.string().min(1).max(60).optional().describe('New name (1–60 chars).'),\n\t\tdescription: z.string().max(500).optional().describe('New description (≤500 chars).'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tif (args.name === undefined && args.description === undefined) {\n\t\t\treturn respond({\n\t\t\t\tsummary: 'No fields to update — pass `name` and/or `description`.',\n\t\t\t\tdata: {},\n\t\t\t});\n\t\t}\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst input: { name?: string; description?: string } = {};\n\t\tif (args.name !== undefined) input.name = args.name;\n\t\tif (args.description !== undefined) input.description = args.description;\n\t\tconst response = await ctx.hoststack.projects.update(teamId, args.project_id, input);\n\t\tconst data = { project: shapeProject(response.project) };\n\t\treturn respond({ summary: `Updated project ${args.project_id}.`, data });\n\t},\n});\n\ndefineTool({\n\tname: 'get_project',\n\tcategory: 'projects',\n\tdescription: [\n\t\t'Fetch a single project by ID. Includes name, description, and timestamps.',\n\t\t'',\n\t\t'When to use: confirming a project exists before creating resources inside it, or pulling the canonical name/description for a reply to the user.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - project_id: publicId of the project (e.g. \"prj_abc123\").',\n\t\t'',\n\t\t'Returns: { project: Project }.',\n\t\t'',\n\t\t'Example: get_project({ project_id: \"prj_abc\" }) → { project: { id: 12, name: \"billing\", … } }',\n\t].join('\\n'),\n\tinput: {\n\t\tproject_id: z.string().describe('Project publicId (e.g. prj_abc123).'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.projects.get(teamId, args.project_id);\n\t\tconst data = { project: shapeProject(response.project) };\n\t\tconst name =\n\t\t\tdata.project && 'name' in data.project\n\t\t\t\t? (data.project as { name: string }).name\n\t\t\t\t: args.project_id;\n\t\treturn respond({ summary: `Project \"${name}\".`, data });\n\t},\n});\n","import { z } from 'zod';\n\nimport { respond } from '../lib/respond.js';\nimport { shape, shapeList } from '../lib/shape.js';\nimport { defineTool } from '../registry.js';\n\n/**\n * The agent task backlog behind a dev box.\n *\n * This category exists because the backlog was reachable from the dashboard and\n * from `errors fix` and nowhere else: an agent that wanted to hand work to\n * ANOTHER box - a fix that belongs upstream, a follow-up in a different repo -\n * had no tool for it and had to hand-roll an HTTP call.\n *\n * The one thing to keep straight: filing a task does not start an agent. It\n * writes a prompt into a queue that a person (or automode, where that is on)\n * starts later. That is what makes it safe to file work for a box that is\n * asleep, and it is why every description below says so.\n */\nconst NOT_A_RUN =\n\t\"Filing a task does NOT start an agent. It writes a prompt into the box's backlog for someone to run; a suspended box keeps it until it wakes.\";\n\ndefineTool({\n\tname: 'list_dev_tasks',\n\tcategory: 'dev-tasks',\n\tdescription: [\n\t\t\"List a project's agent task backlog - the same list the dashboard's Development → Tasks surface shows.\",\n\t\t'',\n\t\t'When to use: to see what is already queued for a box before filing something (the backlog is how you avoid two agents being pointed at one job), or to find the task id for update_dev_task.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - project_id: numeric project id (from list_projects) or its \"prj_…\" publicId.',\n\t\t'',\n\t\t'Returns: { summary, data: { items: DevTask[] } } - each task with publicId, title, status, serviceId (the box, or null for a loose idea), createdAt.',\n\t\t'',\n\t\t'`status` spans two worlds: `idea`/`done` are what a person sets, while `queued`/`running`/`needs_input`/`failed`/`cancelled` describe an agent run and belong to the runner.',\n\t\t'',\n\t\t'Example: list_dev_tasks({ project_id: 26 }) → { items: [{ publicId: \"task_…\", status: \"idea\", title: \"Fix the footprint join\" }] }',\n\t].join('\\n'),\n\tinput: {\n\t\tproject_id: z\n\t\t\t.union([z.number().int().positive(), z.string()])\n\t\t\t.describe('Project — publicId (\"prj_…\") or numeric id.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.devTasks.list(teamId, args.project_id);\n\t\tconst data = shapeList(response, 'tasks', shape);\n\t\tconst open = data.items.filter(\n\t\t\t(t) =>\n\t\t\t\tt &&\n\t\t\t\ttypeof t === 'object' &&\n\t\t\t\t'status' in t &&\n\t\t\t\t!['done', 'cancelled'].includes(String((t as { status: unknown }).status)),\n\t\t).length;\n\t\tconst summary =\n\t\t\tdata.items.length === 0\n\t\t\t\t? 'No tasks in this project.'\n\t\t\t\t: `${data.items.length} task${data.items.length === 1 ? '' : 's'}, ${open} still open.${\n\t\t\t\t\t\tresponse.automodeEnabled\n\t\t\t\t\t\t\t? ''\n\t\t\t\t\t\t\t: ' Automode is off here, so a queued task waits for someone to start it.'\n\t\t\t\t\t}`;\n\t\treturn respond({ summary, data });\n\t},\n});\n\ndefineTool({\n\tname: 'get_dev_task',\n\tcategory: 'dev-tasks',\n\tdescription: [\n\t\t'Get one task, including the full prompt body.',\n\t\t'',\n\t\t'When to use: to read what a task actually asks for before acting on it or marking it done - `list_dev_tasks` returns titles, not prompts.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - task_id: \"task_…\" publicId or numeric id.',\n\t\t'',\n\t\t'Returns: { summary, data: DevTask }.',\n\t\t'',\n\t\t'Example: get_dev_task({ task_id: \"task_hy1i2jdp…\" }) → { data: { title: \"Footprint coverage\", body: \"The BBRUUID join returns 17 of 49 …\", status: \"idea\" } }',\n\t].join('\\n'),\n\tinput: {\n\t\ttask_id: z\n\t\t\t.union([z.number().int().positive(), z.string()])\n\t\t\t.describe('Task — publicId (\"task_…\") or numeric id.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.devTasks.get(teamId, args.task_id);\n\t\tconst data = shape(response.task);\n\t\treturn respond({ summary: `Task ${response.task.publicId}: ${response.task.title}`, data });\n\t},\n});\n\ndefineTool({\n\tname: 'create_dev_task',\n\tcategory: 'dev-tasks',\n\tdescription: [\n\t\t\"File a task in a project's backlog, optionally pinned to a specific dev box.\",\n\t\t'',\n\t\t`When to use: to hand work to a dev box other than the one you are in - a fix that belongs in an upstream service, a follow-up in a different repo, anything the box you are in cannot do itself. ${NOT_A_RUN}`,\n\t\t'',\n\t\t'Inputs:',\n\t\t' - project_id: numeric project id or \"prj_…\" publicId.',\n\t\t' - title: one line, ≤200 chars. This is what the backlog shows.',\n\t\t' - body: the prompt, markdown, ≤20 000 chars. Write it for someone who was not in this conversation: what is broken, where, how it was measured, and what to watch out for.',\n\t\t' - service_id: the dev box to pin it to (\"svc_…\" or numeric). Omit for a loose idea in the project backlog.',\n\t\t'',\n\t\t'Returns: { summary, data: DevTask } - `publicId` is the id to quote back to the user.',\n\t\t'',\n\t\t'Example: create_dev_task({ project_id: 26, service_id: 51, title: \"Footprint coverage\", body: \"The BBRUUID join returns 17 of 49 …\" })',\n\t].join('\\n'),\n\tinput: {\n\t\tproject_id: z\n\t\t\t.union([z.number().int().positive(), z.string()])\n\t\t\t.describe('Project — publicId (\"prj_…\") or numeric id.'),\n\t\ttitle: z.string().min(1).max(200).describe('One-line title, ≤200 chars.'),\n\t\tbody: z\n\t\t\t.string()\n\t\t\t.max(20_000)\n\t\t\t.optional()\n\t\t\t.describe('The prompt handed to the agent. Markdown, ≤20 000 chars.'),\n\t\tservice_id: z\n\t\t\t.union([z.number().int().positive(), z.string()])\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t'Dev box to pin it to — publicId (\"svc_…\") or numeric id. Omit for a loose idea.',\n\t\t\t),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst serviceId =\n\t\t\targs.service_id === undefined\n\t\t\t\t? undefined\n\t\t\t\t: await ctx.hoststack.resolveId(args.service_id, { kind: 'service', teamId });\n\t\tconst projectId = await ctx.hoststack.resolveId(args.project_id, {\n\t\t\tkind: 'project',\n\t\t\tteamId,\n\t\t});\n\t\tconst response = await ctx.hoststack.devTasks.create(teamId, {\n\t\t\tprojectId,\n\t\t\ttitle: args.title,\n\t\t\t...(args.body ? { body: args.body } : {}),\n\t\t\t...(serviceId ? { serviceId } : {}),\n\t\t});\n\t\tconst data = shape(response.task);\n\t\treturn respond({\n\t\t\tsummary: `Filed ${response.task.publicId}: ${response.task.title}. ${NOT_A_RUN}`,\n\t\t\tdata,\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'update_dev_task',\n\tcategory: 'dev-tasks',\n\tdescription: [\n\t\t'Edit a task, or move it between the two statuses a PERSON may set.',\n\t\t'',\n\t\t'When to use: to mark a task `done` once the work has landed (by hand, or in another branch - most work does not finish inside the task runner), to reopen it as an `idea`, to correct a title or prompt, or to pin a loose idea to a box.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - task_id: \"task_…\" publicId or numeric id.',\n\t\t' - status: \"done\" or \"idea\". Only these two; `queued`/`running`/`failed`/`cancelled` belong to the runner and are rejected.',\n\t\t' - title / body / service_id: optional edits.',\n\t\t' - provider: which agent runs it — \"claude\", \"codex\" or \"opencode\". null clears the pin and lets the box decide.',\n\t\t' - permission_mode: how that agent is allowed to act — \"default\" (ask before acting), \"acceptEdits\" (edit files without asking) or \"bypassPermissions\" (no prompts at all). Only set bypassPermissions when the person asked for it: it is the box-wide yolo setting scoped to one task.',\n\t\t'',\n\t\t'Only mark a task done when it is actually resolved - the backlog is what someone reads to decide what still needs doing.',\n\t\t'',\n\t\t'Returns: { summary, data: DevTask }.',\n\t\t'',\n\t\t'Example: update_dev_task({ task_id: \"task_hy1i2jdp…\", status: \"done\" }) → { data: { status: \"done\" } }',\n\t].join('\\n'),\n\tinput: {\n\t\ttask_id: z\n\t\t\t.union([z.number().int().positive(), z.string()])\n\t\t\t.describe('Task — publicId (\"task_…\") or numeric id.'),\n\t\tstatus: z\n\t\t\t.enum(['idea', 'done'])\n\t\t\t.optional()\n\t\t\t.describe('The only two a person may set. The runner owns the rest of the lifecycle.'),\n\t\ttitle: z.string().min(1).max(200).optional().describe('New title.'),\n\t\tbody: z.string().max(20_000).optional().describe('New prompt body.'),\n\t\tservice_id: z\n\t\t\t.union([z.number().int().positive(), z.string()])\n\t\t\t.nullable()\n\t\t\t.optional()\n\t\t\t.describe('Pin to a dev box, or null to unpin it back to a loose idea.'),\n\t\tprovider: z\n\t\t\t.enum(['claude', 'codex', 'opencode'])\n\t\t\t.nullable()\n\t\t\t.optional()\n\t\t\t.describe('Which agent runs this task, or null to leave it to the box.'),\n\t\tpermission_mode: z\n\t\t\t.enum(['acceptEdits', 'bypassPermissions', 'default'])\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t'How the agent may act. bypassPermissions asks for nothing — set it only on request.',\n\t\t\t),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst serviceId =\n\t\t\targs.service_id === undefined || args.service_id === null\n\t\t\t\t? args.service_id\n\t\t\t\t: await ctx.hoststack.resolveId(args.service_id, { kind: 'service', teamId });\n\t\tconst response = await ctx.hoststack.devTasks.update(teamId, args.task_id, {\n\t\t\t...(args.status ? { status: args.status } : {}),\n\t\t\t...(args.title ? { title: args.title } : {}),\n\t\t\t...(args.body !== undefined ? { body: args.body } : {}),\n\t\t\t...(serviceId !== undefined ? { serviceId } : {}),\n\t\t\t// null is meaningful on provider (unpin), so compare to undefined.\n\t\t\t...(args.provider !== undefined ? { provider: args.provider } : {}),\n\t\t\t...(args.permission_mode !== undefined ? { permissionMode: args.permission_mode } : {}),\n\t\t});\n\t\tconst data = shape(response.task);\n\t\treturn respond({\n\t\t\tsummary: `${response.task.publicId} is now ${response.task.status}: ${response.task.title}`,\n\t\t\tdata,\n\t\t});\n\t},\n});\n","import { z } from 'zod';\n\nimport { defineTool } from '../registry.js';\nimport { respond } from '../lib/respond.js';\nimport { shape, shapeList } from '../lib/shape.js';\n\nconst RESOURCE_LINK_TYPES = [\n\t'database',\n\t'object_storage',\n\t'queue',\n\t'search',\n\t'email_domain',\n] as const;\n\n/**\n * The env vars a link injects, by resource class. Documented in the tool\n * descriptions so an agent can predict what the container will see WITHOUT\n * fetching credentials — the whole point of the link system.\n */\nconst INJECTION_CONTRACT = [\n\t'What a link injects at deploy time:',\n\t' - Alias-prefixed vars for every field of the resource (alias \"APP_DB\" → APP_DB_HOST, APP_DB_PORT, APP_DB_USER, APP_DB_PASSWORD, APP_DB_URL, …). This is how one service consumes several resources of the same type without collisions.',\n\t' - PLUS the conventional name for the first linked resource of each engine class: `DATABASE_URL` (postgres/mysql/mariadb), `REDIS_URL` (redis), `MONGO_URL` (mongodb). So an app reading `process.env.DATABASE_URL` works with no code changes.',\n\t' - The conventional names are set with `??=` — an env var you set yourself via set_env_var ALWAYS wins over the injected one.',\n].join('\\n');\n\ndefineTool({\n\tname: 'list_managed_resources',\n\tcategory: 'resource-links',\n\tdescription: [\n\t\t'List EVERY managed resource the team owns in one call — databases, object storage buckets, queues, search indexes and email domains — from the unified `managed_resources` read-model.',\n\t\t'',\n\t\t'When to use: to find the NUMERIC `id` that link_resource_to_service needs, especially for resource types with no dedicated list tool of their own (object storage, queues, search, email domains — list_databases only covers databases). Also a fast inventory answer for \"what do we actually have running\".',\n\t\t'',\n\t\t'Team-wide, not project-scoped. Use list_databases when you specifically want databases in one project.',\n\t\t'',\n\t\t'Returns: { items: ManagedResource[] } — id (numeric, for linking), publicId, type, name, status, region, createdAt.',\n\t\t'',\n\t\t'Example: list_managed_resources() → { items: [{ id: 42, type: \"database\", name: \"app-db\", status: \"available\" }, { id: 8, type: \"object_storage\", name: \"uploads\", … }] }',\n\t].join('\\n'),\n\tinput: {},\n\thandler: async (_args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.serviceResourceLinks.listManagedResources(teamId);\n\t\tconst data = shapeList(response, 'resources', shape);\n\t\tconst byType = new Map<string, number>();\n\t\tfor (const item of data.items) {\n\t\t\tconst type =\n\t\t\t\titem && typeof item === 'object' && 'type' in item\n\t\t\t\t\t? String((item as { type: unknown }).type)\n\t\t\t\t\t: 'unknown';\n\t\t\tbyType.set(type, (byType.get(type) ?? 0) + 1);\n\t\t}\n\t\tconst breakdown = [...byType.entries()].map(([t, n]) => `${n} ${t}`).join(', ');\n\t\tconst summary =\n\t\t\tdata.items.length === 0\n\t\t\t\t? 'No managed resources on this team yet.'\n\t\t\t\t: `${data.items.length} managed resource${data.items.length === 1 ? '' : 's'}: ${breakdown}.`;\n\t\treturn respond({ summary, data });\n\t},\n});\n\ndefineTool({\n\tname: 'list_service_resources',\n\tcategory: 'resource-links',\n\tdescription: [\n\t\t'List the managed resources (databases, object storage, queues, search, email domains) linked to a service — i.e. which resources get injected into its container as env vars on deploy.',\n\t\t'',\n\t\t'When to use: before adding a link (to avoid a duplicate or an alias collision), when debugging \"why is DATABASE_URL empty in my app\" (the answer is usually: no link exists, or the service was never redeployed after linking), or to find the linkId needed by unlink_resource_from_service.',\n\t\t'',\n\t\tINJECTION_CONTRACT,\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId (\"svc_…\") or numeric id of the service.',\n\t\t'',\n\t\t'Returns: { items: ServiceResourceLink[] } — id (the linkId), resourceType, resourceId (numeric id of the linked resource), alias, createdAt.',\n\t\t'',\n\t\t'Example: list_service_resources({ service_id: \"svc_abc\" }) → { items: [{ id: 7, resourceType: \"database\", resourceId: 42, alias: \"APP_DB\" }] }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z\n\t\t\t.union([z.number().int().positive(), z.string()])\n\t\t\t.describe('Service — publicId (\"svc_…\") or numeric id.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.serviceResourceLinks.list(teamId, args.service_id);\n\t\tconst data = shapeList(response, 'links', shape);\n\t\tconst summary =\n\t\t\tdata.items.length === 0\n\t\t\t\t? `No resources linked to service ${args.service_id}. Nothing is being injected — link a database with link_resource_to_service.`\n\t\t\t\t: `${data.items.length} resource${data.items.length === 1 ? '' : 's'} linked to service ${args.service_id}.`;\n\t\treturn respond({ summary, data });\n\t},\n});\n\ndefineTool({\n\tname: 'link_resource_to_service',\n\tcategory: 'resource-links',\n\tdescription: [\n\t\t'Link a managed resource to a service so its connection details are injected into the container as environment variables. This is the step that connects a database to an app — creating the database alone does nothing for the app.',\n\t\t'',\n\t\t'When to use: immediately after create_database (or when pointing an existing service at an existing resource). This is step 2 of 3 in the managed-database flow: create_database → link_resource_to_service → trigger_deploy.',\n\t\t'',\n\t\t'*** The link takes effect on the NEXT DEPLOY. *** After calling this, call trigger_deploy({ service_id }) or the running container will not see the new vars. A service that \"cannot connect to the database\" right after linking almost always just needs that redeploy.',\n\t\t'',\n\t\tINJECTION_CONTRACT,\n\t\t'',\n\t\t\"Because the platform injects the credentials directly into the container, you do NOT need to read the password, build a connection string by hand, or store it with set_env_var. Don't.\",\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId (\"svc_…\") or numeric id of the consuming service.',\n\t\t` - resource_type: ${RESOURCE_LINK_TYPES.join(' | ')}.`,\n\t\t' - resource_id: the NUMERIC id of the resource — e.g. the `id` field from create_database / list_databases, NOT the \"db_…\" publicId.',\n\t\t' - alias: uppercase env-var prefix (A–Z, 0–9, underscore; must start with a letter; ≤48 chars), e.g. \"APP_DB\", \"CACHE\", \"CATALOG\". Must be unique within the service.',\n\t\t'',\n\t\t'Returns: { link: ServiceResourceLink } — including `id`, the linkId used to unlink later.',\n\t\t'',\n\t\t'Fails with 409 if this exact resource is already linked to the service, or if the alias is already taken on it.',\n\t\t'',\n\t\t'Example: link_resource_to_service({ service_id: \"svc_abc\", resource_type: \"database\", resource_id: 42, alias: \"APP_DB\" }) → { link: { id: 7, alias: \"APP_DB\" } }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z\n\t\t\t.union([z.number().int().positive(), z.string()])\n\t\t\t.describe('Consuming service — publicId (\"svc_…\") or numeric id.'),\n\t\tresource_type: z.enum(RESOURCE_LINK_TYPES).describe('Kind of resource being linked.'),\n\t\tresource_id: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.positive()\n\t\t\t.describe('NUMERIC id of the resource (e.g. database.id) — not the publicId.'),\n\t\talias: z\n\t\t\t.string()\n\t\t\t.min(1)\n\t\t\t.max(48)\n\t\t\t.regex(\n\t\t\t\t/^[A-Z][A-Z0-9_]*$/,\n\t\t\t\t'Alias must be uppercase letters, digits and underscores, starting with a letter.',\n\t\t\t)\n\t\t\t.describe('Uppercase env-var prefix, e.g. \"APP_DB\". Unique within the service.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.serviceResourceLinks.create(teamId, args.service_id, {\n\t\t\tresourceType: args.resource_type,\n\t\t\tresourceId: args.resource_id,\n\t\t\talias: args.alias,\n\t\t});\n\t\tconst data = { link: shape(response.link) };\n\t\treturn respond({\n\t\t\tsummary: `Linked ${args.resource_type} ${args.resource_id} to service ${args.service_id} as \"${args.alias}\". Call trigger_deploy({ service_id: \"${args.service_id}\" }) to inject it — the running container will not see the vars until then.`,\n\t\t\tdata,\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'unlink_resource_from_service',\n\tcategory: 'resource-links',\n\tdescription: [\n\t\t'Remove a resource link from a service. The resource itself is NOT deleted — only the binding.',\n\t\t'',\n\t\t'When to use: repointing a service at a different database, or detaching a resource a service no longer needs. The injected env vars disappear on the next deploy, so a service that still reads DATABASE_URL will break then, not immediately. Use delete_database if the intent is to destroy the data.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId (\"svc_…\") or numeric id of the service.',\n\t\t' - link_id: numeric linkId from list_service_resources (the `id` field on the link, not the resource id).',\n\t\t'',\n\t\t'Returns: { ok: true }.',\n\t\t'',\n\t\t'Example: unlink_resource_from_service({ service_id: \"svc_abc\", link_id: 7 }) → { ok: true }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z\n\t\t\t.union([z.number().int().positive(), z.string()])\n\t\t\t.describe('Service — publicId (\"svc_…\") or numeric id.'),\n\t\tlink_id: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.positive()\n\t\t\t.describe(\"Numeric linkId from list_service_resources (the link's own `id`).\"),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tawait ctx.hoststack.serviceResourceLinks.delete(teamId, args.service_id, args.link_id);\n\t\treturn respond({\n\t\t\tsummary: `Unlinked resource link ${args.link_id} from service ${args.service_id}. The resource still exists; its env vars vanish on the next deploy.`,\n\t\t});\n\t},\n});\n","import type { CreateDevEnvironmentInput } from '@hoststack.dev/sdk';\nimport { z } from 'zod';\n\nimport { defineTool } from '../registry.js';\nimport { MCP_APP_TEMPLATES } from '../lib/app-templates.js';\nimport { respond, respondError } from '../lib/respond.js';\nimport { shape, shapeList, shapeService } from '../lib/shape.js';\nimport { machineInput, resolveMachineId } from './machines.js';\n\n// Keep in sync with the real sources of truth:\n// packages/shared/src/constants/dev-env.ts DEV_ENV_IMAGE\n// apps/api/src/services/dev-environment.service.ts WORKSPACE_VOLUME\n// packages/shared/src/constants/pricing.ts DEV_ENV_MIN_SIZE,\n// HETZNER_BLOCK_VOLUME_*\n// NOT packages/shared/src/templates.ts for the DEV_ENV_* constants below: the\n// `dev-environment` preset was removed from that file on purpose and it now\n// carries the opposite instruction (the canonical creator is /development/new),\n// with a test asserting the preset never returns. Following the old pointer led\n// you to a file that says the thing does not exist. templates.ts IS the source\n// for the app quickstart catalog — mirrored in lib/app-templates.ts, which\n// `list_templates` returns.\n// The MCP package only depends on the SDK (it publishes to npm and must NOT\n// import the private @hoststack/shared package, which would drag the whole\n// monorepo into the bundle), so these constants are duplicated here\n// intentionally rather than imported. Drift is caught by review, not the type\n// system — update both sides together.\nconst DEV_ENV_IMAGE = 'registry.hoststack.dev/hoststack/dev-env:latest';\nconst DEV_ENV_VOLUME = { name: 'workspace', mountPath: '/workspace', sizeGb: 10 };\n\n// What a box can already do, stated on EVERY tool that makes one.\n//\n// This exists because of a real failure: an agent told to \"add a database\" in a\n// dev box tried to `docker run postgres` (there is no daemon) or to install its\n// own, because nothing it could read said the box already had one. Capability\n// that is undiscoverable is capability that does not exist — so the answer is\n// not to add features, it is to say what is there at the moment the box is\n// created. Keep in sync with apps/dev-env-image/agent-guide.md, which is the\n// same information delivered to the agent once it is inside.\nconst DEV_BOX_INSIDE = [\n\t'INSIDE the box (no Docker daemon — do not try `docker run`, and runtime `apt-get` is blocked too; it is all preinstalled):',\n\t' - Databases/engines via `dev-services up`: Postgres 17 (:5432, user \"dev\", trust auth), Redis (:6379), Meilisearch (:7700). MariaDB/MySQL (:3306, user \"dev\", no password, client `mariadb`) and MongoDB (:27017, client `mongosh`) are OPT-IN — `dev-services up mysql` / `dev-services up mongodb` — so a box does not pay RAM for engines it never uses. `dev-services createdb <name>` makes a Postgres role + database in one step. Data lives on /workspace, so it survives restarts.',\n\t' - Language runtimes: node, bun, python and php 8.3 are built in. go, java, ruby, rust, elixir and dotnet install on demand with `dev-runtime add <name>` (persisted to /workspace). Together that covers every runtime the platform itself deploys — a WordPress/Laravel/Rails stack runs in the box. elixir needs `dev-runtime add erlang` first: it is a BEAM distribution with no OTP of its own, and Ubuntu ships one too old to use, so mise builds a current one (takes a few minutes).',\n\t' - `hoststack-status` prints what is running, the box dev URL and agent login state at any time.',\n].join('\\n');\n\nconst SERVICE_TYPES = [\n\t'web_service',\n\t'private_service',\n\t'worker',\n\t'cron_job',\n\t'static_site',\n] as const;\n// The real service catalog, ascending — mirrors the server's\n// SERVICE_SIZE_ORDER. `pico↓xlarge` are shared-CPU; `pro_*` are dedicated-CPU.\n// Note: `starter` is a DATABASE/addon size, not a service size — do not add it.\nconst SERVICE_PLANS = [\n\t'pico',\n\t'nano',\n\t'micro',\n\t'small',\n\t'standard',\n\t'large',\n\t'xlarge',\n\t'pro_standard',\n\t'pro_large',\n] as const;\n\n// The OOM-safe floor for dev boxes — mirrors DEV_ENV_MIN_SIZE in\n// packages/shared/src/constants/pricing.ts. A coding agent + build OOMs on\n// anything below `standard` (2 GB). The server floors a sub-standard plan\n// too; we clamp here so the MCP tool never even requests an undersized box.\nconst DEV_ENV_MIN_SIZE = 'standard';\n\n/**\n * Clamp a requested service size up to the dev-env floor. Mirrors the\n * server-side `coerceDevEnvSize`: an unknown/omitted/sub-`standard` value\n * becomes `standard`; a known size at/above the floor is left untouched.\n * Duplicated (not imported) per the workspace constraint above.\n */\nfunction coerceDevEnvSize(value: string | undefined): (typeof SERVICE_PLANS)[number] {\n\tconst minIdx = SERVICE_PLANS.indexOf(DEV_ENV_MIN_SIZE);\n\tconst idx = value ? SERVICE_PLANS.indexOf(value as (typeof SERVICE_PLANS)[number]) : -1;\n\treturn idx >= minIdx ? (value as (typeof SERVICE_PLANS)[number]) : DEV_ENV_MIN_SIZE;\n}\n\ndefineTool({\n\tname: 'list_services',\n\tcategory: 'services',\n\tdescription: [\n\t\t'List services (web, worker, cron, private, static) in the active HostStack team.',\n\t\t'',\n\t\t'When to use: the agent needs to find a service by name, check what is deployed, or pick a target for a follow-up tool (logs, deploys, env vars). This is the canonical way to resolve a publicId from a human-friendly name.',\n\t\t'',\n\t\t'Note: agentic Dev Boxes (cloud dev environments) are EXCLUDED by default. Pass dev_environment:true to include them, or use list_dev_environments for the dedicated, annotated dev-box listing.',\n\t\t'',\n\t\t'Inputs (all optional):',\n\t\t' - project_id: narrow to one project (numeric id or publicId \"prj_…\").',\n\t\t' - environment_id: narrow to one environment (numeric id or publicId \"env_…\").',\n\t\t' - status: \"active\" | \"deploying\" | \"suspended\" | \"failed\" | \"not_deployed\".',\n\t\t' - type: \"web_service\" | \"private_service\" | \"worker\" | \"cron_job\" | \"static_site\".',\n\t\t' - dev_environment: include Dev Boxes in the results (excluded by default).',\n\t\t'',\n\t\t'Returns: { items: Service[] } — each service includes id, publicId, name, type, status, projectId, branch, runtime, createdAt, and, for a source-built service, repoProvider/repoFullName/repoUrl (what it actually builds — the stored id alone names no repository).',\n\t\t'',\n\t\t'Example: list_services({ status: \"failed\" }) → only services that need attention.',\n\t].join('\\n'),\n\tinput: {\n\t\tproject_id: z\n\t\t\t.union([z.number().int().positive(), z.string()])\n\t\t\t.optional()\n\t\t\t.describe('Project filter — numeric id or publicId.'),\n\t\tenvironment_id: z\n\t\t\t.union([z.number().int().positive(), z.string()])\n\t\t\t.optional()\n\t\t\t.describe('Environment filter — numeric id or publicId.'),\n\t\tstatus: z\n\t\t\t.enum(['active', 'deploying', 'suspended', 'failed', 'not_deployed'])\n\t\t\t.optional()\n\t\t\t.describe('Filter by current runtime status.'),\n\t\ttype: z\n\t\t\t.enum(['web_service', 'private_service', 'worker', 'cron_job', 'static_site'])\n\t\t\t.optional()\n\t\t\t.describe('Filter by service type.'),\n\t\tdev_environment: z\n\t\t\t.boolean()\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t'Include agentic Dev Boxes in the results (excluded by default; see list_dev_environments).',\n\t\t\t),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst filters: Parameters<typeof ctx.hoststack.services.list>[1] = {};\n\t\tif (args.dev_environment) filters.devEnvironment = true;\n\t\tif (args.project_id !== undefined) {\n\t\t\t// SDK accepts publicId or numeric id; let resolveId handle it.\n\t\t\tconst resolved = await ctx.hoststack.resolveId(args.project_id, {\n\t\t\t\tkind: 'project',\n\t\t\t\tteamId,\n\t\t\t});\n\t\t\tfilters.projectId = resolved;\n\t\t}\n\t\tif (args.environment_id !== undefined) {\n\t\t\tconst resolved = await ctx.hoststack.resolveId(args.environment_id, {\n\t\t\t\tkind: 'environment',\n\t\t\t\tteamId,\n\t\t\t});\n\t\t\tfilters.environmentId = resolved;\n\t\t}\n\t\tif (args.status) filters.status = args.status;\n\t\tif (args.type) filters.type = args.type;\n\n\t\tconst response = await ctx.hoststack.services.list(teamId, filters);\n\t\tconst data = shapeList(response, 'services', shapeService);\n\t\tconst filterDesc = [\n\t\t\targs.project_id !== undefined ? `project=${args.project_id}` : null,\n\t\t\targs.environment_id !== undefined ? `env=${args.environment_id}` : null,\n\t\t\targs.status ? `status=${args.status}` : null,\n\t\t\targs.type ? `type=${args.type}` : null,\n\t\t\targs.dev_environment ? 'devBoxes=included' : null,\n\t\t]\n\t\t\t.filter(Boolean)\n\t\t\t.join(', ');\n\t\tconst summary =\n\t\t\tdata.items.length === 0\n\t\t\t\t? filterDesc\n\t\t\t\t\t? `No services match the given filters (${filterDesc}).`\n\t\t\t\t\t: 'No services yet — create one with create_service (or create_dev_environment for an AI dev box), or via the dashboard.'\n\t\t\t\t: `Found ${data.items.length} service${data.items.length === 1 ? '' : 's'}${filterDesc ? ` matching ${filterDesc}` : ''}.`;\n\t\treturn respond({ summary, data });\n\t},\n});\n\ndefineTool({\n\tname: 'create_service',\n\tcategory: 'services',\n\tdescription: [\n\t\t'Create a new service in a project. Source is either a connected git repo (github_repo \"owner/name\", or github_repo_id) OR a pre-built docker_image — never both.',\n\t\t'',\n\t\t'When to use: the user wants to deploy something new. For a one-command AI dev environment specifically, prefer create_dev_environment (it also attaches the /workspace volume and sets the MCP keys).',\n\t\t'',\n\t\t'*** For a packaged app (WordPress, Ghost, n8n, Uptime Kuma, Vaultwarden), start at list_templates and pass template_id. *** Those templates are prebuilt images that only boot with the volume, scratch dirs, uid and companion database the platform attaches from the template id — the same image passed as a bare docker_image crash-loops on the read-only rootfs, and the fix is a redeploy away rather than an edit away.',\n\t\t'',\n\t\t'*** NOT for databases. *** If the user wants Postgres, Redis, MySQL, MariaDB or MongoDB, call create_database — do NOT create a service with docker_image \"postgres:16\" / \"redis:7\" / \"mongo\" / \"mysql\". A database deployed as a service is unmanaged: no backups, no version upgrades, no HA, no credential rotation, no metrics, no persistent volume, and nothing injects its URL into your app. `docker_image` here is for YOUR application images (or sidecars), not for datastores the platform already manages.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - project_id: numeric id or publicId (\"prj_…\") of the target project.',\n\t\t' - name: service name (1–100 chars).',\n\t\t' - type: \"web_service\" | \"private_service\" | \"worker\" | \"cron_job\" | \"static_site\".',\n\t\t' - docker_image (optional): pre-built image ref to deploy instead of building from source.',\n\t\t' - github_repo (optional): connect a connected GitHub repo by name — \"owner/name\". Preferred over github_repo_id: it is what you actually know, and a wrong name is refused here instead of failing the deploy.',\n\t\t\" - github_repo_id (optional): the same repo by its HostStack numeric id (from list_github_repos). NOT GitHub's id. Mutually exclusive with github_repo.\",\n\t\t' - git_repo_url (optional): a PUBLIC https git URL, for a repo that is not connected through GitHub. Private repos do not work here — they need a provider connection, because this field carries no credentials and embedding them in the URL is rejected. Mutually exclusive with github_repo_id and docker_image.',\n\t\t' - branch (optional): git branch (default \"main\").',\n\t\t' - install_command / build_command / start_command (optional): build/run shell commands. start_command is required for web/private services without a docker_image.',\n\t\t' - cron_schedule (optional): cron expression — required for cron_job.',\n\t\t' - publish_path (optional): static-site output dir (e.g. \"dist\").',\n\t\t' - runtime (optional): \"node\" | \"bun\" | \"python\" | … (auto-detected from a repo when omitted).',\n\t\t' - port (optional): the port the container listens on (1–65535). A source-built service should omit it — the platform injects $PORT and expects the app to bind that. Set it for a docker_image whose listen port is fixed by the image (WordPress 80, Ghost 2368, n8n 5678): the platform publishes and health-checks this port and never re-reads what the process actually bound, so an image listening elsewhere fails its first deploy on a container that is perfectly healthy.',\n\t\t' - template_id (optional): create from a quickstart template — an id from list_templates and nothing else. Everything the template brings (volumes, scratch dirs, uid, generated secrets, companion managed database) is resolved server-side and attached before the first deploy fires; there is no way to send those in the body. For an image template, pass its docker_image and port alongside, exactly as list_templates returns them.',\n\t\t' - plan (optional): service size (default \"micro\").',\n\t\t' - environment_id (optional): bind to a specific environment; defaults to the project Production env.',\n\t\t' - auto_deploy (optional, default true): trigger the first deploy immediately when a source is present.',\n\t\t\" - machine (optional): run it on one of the team's OWN enrolled machines (name or id, see list_machines) instead of HostStack compute. Pinned at creation and never moved afterwards, and the service is up only while that machine is.\",\n\t\t'',\n\t\t'Returns: { service: Service, deployId: number | null }.',\n\t\t'',\n\t\t'Example: create_service({ project_id: \"prj_abc\", name: \"api\", type: \"web_service\", github_repo: \"acme/api\" }) → { service: { publicId: \"svc_…\", repoUrl: \"https://github.com/acme/api\" }, deployId: 1234 }',\n\t\t'Repo not listed by list_github_repos? Call sync_github_repos — a repository pushed since the last sync is invisible until then.',\n\t\t'Example (one-click app): create_service({ project_id: \"prj_abc\", name: \"blog\", type: \"web_service\", template_id: \"wordpress\", docker_image: \"wordpress:php8.3-apache\", port: 80 }) — the wp-content volume, the www-data uid, the Apache scratch dirs and the managed MySQL come from the template id.',\n\t].join('\\n'),\n\tinput: {\n\t\tproject_id: z\n\t\t\t.union([z.number().int().positive(), z.string()])\n\t\t\t.describe('Target project — numeric id or publicId.'),\n\t\tname: z.string().min(1).max(100).describe('Service name (1–100 chars).'),\n\t\ttype: z.enum(SERVICE_TYPES).describe('Service type.'),\n\t\tdocker_image: z\n\t\t\t.string()\n\t\t\t.max(500)\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t'Pre-built APPLICATION image ref. Mutually exclusive with github_repo_id. Not for databases — use create_database for postgres/redis/mysql/mariadb/mongodb.',\n\t\t\t),\n\t\tgithub_repo: z\n\t\t\t.string()\n\t\t\t.min(1)\n\t\t\t.max(255)\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t'Connected GitHub repo as \"owner/name\" (see list_github_repos). Resolved server-side. Mutually exclusive with github_repo_id and docker_image.',\n\t\t\t),\n\t\tgithub_repo_id: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.positive()\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t\"HostStack row id of a connected GitHub repo (list_github_repos) — not GitHub's own id. Prefer github_repo. Mutually exclusive with docker_image.\",\n\t\t\t),\n\t\tgit_repo_url: z\n\t\t\t.string()\n\t\t\t.max(2048)\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t'Public https git URL. No embedded credentials — private repos need a provider connection. Mutually exclusive with github_repo_id and docker_image.',\n\t\t\t),\n\t\tbranch: z.string().max(200).optional().describe('Git branch (default \"main\").'),\n\t\tinstall_command: z.string().max(1000).optional().describe('Install shell command.'),\n\t\tbuild_command: z.string().max(1000).optional().describe('Build shell command.'),\n\t\tstart_command: z\n\t\t\t.string()\n\t\t\t.max(1000)\n\t\t\t.optional()\n\t\t\t.describe('Start shell command (required for web/private services without an image).'),\n\t\tcron_schedule: z\n\t\t\t.string()\n\t\t\t.max(100)\n\t\t\t.optional()\n\t\t\t.describe('Cron expression — required for cron_job.'),\n\t\tpublish_path: z.string().max(500).optional().describe('Static-site output dir.'),\n\t\truntime: z.string().max(50).optional().describe('Runtime hint (node/bun/python/…).'),\n\t\tport: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(1)\n\t\t\t.max(65535)\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t'Listen port, for a prebuilt image whose port is fixed by the image. Omit for a source-built service — it binds the injected $PORT.',\n\t\t\t),\n\t\ttemplate_id: z\n\t\t\t.string()\n\t\t\t.max(64)\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t'Quickstart template id from list_templates. Its volumes, scratch dirs, uid, secrets and companion database are resolved server-side; for an image template also pass its docker_image and port.',\n\t\t\t),\n\t\tplan: z.enum(SERVICE_PLANS).optional().describe('Service size (default \"micro\").'),\n\t\tenvironment_id: z\n\t\t\t.union([z.number().int().positive(), z.string()])\n\t\t\t.optional()\n\t\t\t.describe('Bind to a specific environment; defaults to Production.'),\n\t\tauto_deploy: z\n\t\t\t.boolean()\n\t\t\t.optional()\n\t\t\t.describe('Trigger the first deploy immediately (default true).'),\n\t\tmachine: machineInput,\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst projectId = await ctx.hoststack.resolveId(args.project_id, {\n\t\t\tkind: 'project',\n\t\t\tteamId,\n\t\t});\n\n\t\tconst input: Parameters<typeof ctx.hoststack.services.create>[1] = {\n\t\t\tname: args.name,\n\t\t\ttype: args.type,\n\t\t\tprojectId,\n\t\t};\n\t\tif (args.docker_image !== undefined) input.dockerImage = args.docker_image;\n\t\tif (args.github_repo !== undefined) input.githubRepo = args.github_repo;\n\t\tif (args.github_repo_id !== undefined) input.githubRepoId = args.github_repo_id;\n\t\tif (args.git_repo_url !== undefined) input.gitRepoUrl = args.git_repo_url;\n\t\tif (args.branch !== undefined) input.branch = args.branch;\n\t\tif (args.install_command !== undefined) input.installCommand = args.install_command;\n\t\tif (args.build_command !== undefined) input.buildCommand = args.build_command;\n\t\tif (args.start_command !== undefined) input.startCommand = args.start_command;\n\t\tif (args.cron_schedule !== undefined) input.cronSchedule = args.cron_schedule;\n\t\tif (args.publish_path !== undefined) input.publishPath = args.publish_path;\n\t\tif (args.runtime !== undefined) input.runtime = args.runtime;\n\t\tif (args.port !== undefined) input.port = args.port;\n\t\tif (args.template_id !== undefined) input.templateId = args.template_id;\n\t\tif (args.plan !== undefined) input.plan = args.plan;\n\t\tif (args.auto_deploy !== undefined) input.autoDeploy = args.auto_deploy;\n\t\tif (args.environment_id !== undefined) {\n\t\t\tinput.environmentId = await ctx.hoststack.resolveId(args.environment_id, {\n\t\t\t\tkind: 'environment',\n\t\t\t\tteamId,\n\t\t\t});\n\t\t}\n\t\tif (args.machine !== undefined) {\n\t\t\tinput.machineId = await resolveMachineId(ctx, teamId, args.machine);\n\t\t}\n\n\t\tconst response = await ctx.hoststack.services.create(teamId, input);\n\t\tconst data = {\n\t\t\tservice: shapeService(response.service),\n\t\t\tdeployId: (response as { deployId?: number | null }).deployId ?? null,\n\t\t};\n\t\tconst publicId =\n\t\t\tdata.service && 'publicId' in data.service\n\t\t\t\t? (data.service as { publicId: string }).publicId\n\t\t\t\t: 'unknown';\n\t\treturn respond({ summary: `Created service \"${args.name}\" (${publicId}).`, data });\n\t},\n});\n\ndefineTool({\n\tname: 'create_dev_environment',\n\tcategory: 'services',\n\tdescription: [\n\t\t'Spin up an AI Dev Box in one call: a cloud \"Dev Box\" (an agentic container from the dev-env image — Claude Code + Codex + OpenCode + MCPs preinstalled) inside a project, with a persistent /workspace volume and the MCP API keys set, then fire the first deploy.',\n\t\t'',\n\t\t'This makes a cloud Dev Box, NOT a project deploy environment. A project \"environment\" (create_environment → Production / Staging / Preview) is a deployment target; this is a workspace you code in. If you actually want a -dev deploy target, use create_environment instead.',\n\t\t'',\n\t\t'When to use: the user wants a cloud terminal / Dev Box they can drive coding agents in (from a desk or a phone). This mirrors the dashboard\\'s \"AI Dev Environment\" wizard preset — create (deploy deferred) → set keys → attach /workspace → deploy, so the first container boots with its volume and keys already in place.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - project_id: numeric id or publicId (\"prj_…\") of the target project.',\n\t\t' - name (optional): service name (default \"dev-environment\").',\n\t\t' - plan (optional): box size. Defaults to \"standard\" (2 GB) — the OOM-safe floor (DEV_ENV_MIN_SIZE); a coding agent + build OOMs below it. A smaller plan is clamped up to \"standard\".',\n\t\t' - disk_gb (optional): /workspace volume size in GB (default 10, min 10, max 10240).',\n\t\t' - hoststack_api_key (optional): sets HOSTSTACK_API_KEY so the hoststack MCP works inside the container.',\n\t\t' - poststack_api_key (optional): sets POSTSTACK_API_KEY so the poststack MCP works inside the container.',\n\t\t' - repo_url (optional): clone this git URL into /workspace on first boot (with optional branch).',\n\t\t'',\n\t\tDEV_BOX_INSIDE,\n\t\t'',\n\t\t'Returns: { service: Service, volumeAttached: boolean, deployId: number | null }. Open the Terminal tab on the service (dashboard or phone) once it is running; log in once with `claude /login` inside the container.',\n\t\t'',\n\t\t'Example: create_dev_environment({ project_id: \"prj_abc\", name: \"scratch\", hoststack_api_key: \"hs_live_…\" })',\n\t].join('\\n'),\n\tinput: {\n\t\tproject_id: z\n\t\t\t.union([z.number().int().positive(), z.string()])\n\t\t\t.describe('Target project — numeric id or publicId.'),\n\t\tname: z\n\t\t\t.string()\n\t\t\t.min(1)\n\t\t\t.max(100)\n\t\t\t.optional()\n\t\t\t.describe('Service name (default \"dev-environment\").'),\n\t\tplan: z\n\t\t\t.enum(SERVICE_PLANS)\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t'Box size (default \"standard\" — 2 GB, the OOM-safe floor; a smaller plan is clamped up to \"standard\").',\n\t\t\t),\n\t\tdisk_gb: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t// 10/10240 are the API's real bounds (HETZNER_BLOCK_VOLUME_MIN_GB /\n\t\t\t// _MAX_GB in packages/shared/src/constants/pricing.ts). This used to\n\t\t\t// say 1–100, which failed in both directions: `disk_gb: 5` passed\n\t\t\t// this gate, created a billable service, then 400'd on the volume\n\t\t\t// attach — and the handler treats that as fatal and tears the whole\n\t\t\t// box down again. Rejecting it here costs the caller one error\n\t\t\t// message instead of a create-and-destroy round trip.\n\t\t\t.min(10)\n\t\t\t.max(10240)\n\t\t\t.optional()\n\t\t\t.describe('/workspace volume size in GB (default 10, min 10, max 10240).'),\n\t\thoststack_api_key: z\n\t\t\t.string()\n\t\t\t.optional()\n\t\t\t.describe('Value for HOSTSTACK_API_KEY (enables the hoststack MCP in-container).'),\n\t\tpoststack_api_key: z\n\t\t\t.string()\n\t\t\t.optional()\n\t\t\t.describe('Value for POSTSTACK_API_KEY (enables the poststack MCP in-container).'),\n\t\trepo_url: z\n\t\t\t.string()\n\t\t\t.max(500)\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t'Clone this git URL into /workspace on first boot (HTTPS, or SSH once a key is set).',\n\t\t\t),\n\t\tbranch: z.string().max(200).optional().describe('Branch to clone (with repo_url).'),\n\t\tmachine: machineInput,\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst projectId = await ctx.hoststack.resolveId(args.project_id, {\n\t\t\tkind: 'project',\n\t\t\tteamId,\n\t\t});\n\t\tconst name = args.name ?? 'dev-environment';\n\t\tconst sizeGb = args.disk_gb ?? DEV_ENV_VOLUME.sizeGb;\n\t\t// Defence in depth: clamp the requested plan up to the OOM-safe dev-env\n\t\t// floor BEFORE asking the SDK to create the box. The server floors it\n\t\t// too, but this guarantees the tool never even requests a sub-`standard`\n\t\t// box that would OOM a coding agent on first boot.\n\t\tconst plan = coerceDevEnvSize(args.plan);\n\n\t\t// Defer the first deploy (autoDeploy:false) so we can set the MCP keys\n\t\t// and attach /workspace BEFORE the container boots — same ordering as\n\t\t// the dashboard wizard's use-new-service-form.\n\t\tconst createInput: Parameters<typeof ctx.hoststack.services.create>[1] = {\n\t\t\tname,\n\t\t\ttype: 'private_service',\n\t\t\tprojectId,\n\t\t\tdockerImage: DEV_ENV_IMAGE,\n\t\t\tautoDeploy: false,\n\t\t\tplan,\n\t\t};\n\t\t// Resolved BEFORE the service exists: an unknown machine name should cost\n\t\t// the caller an error message, not a created-then-torn-down box.\n\t\tif (args.machine !== undefined) {\n\t\t\tcreateInput.machineId = await resolveMachineId(ctx, teamId, args.machine);\n\t\t}\n\t\tconst created = await ctx.hoststack.services.create(teamId, createInput);\n\t\tconst service = created.service;\n\n\t\t// From here on the service EXISTS and is billable. Any later failure\n\t\t// must tear it back down so we never orphan a half-provisioned,\n\t\t// non-persistent box — see rollback() below. (The server's own\n\t\t// rollbackPartialDevBox only covers the dedicated dev-env create path,\n\t\t// not this generic create-then-attach sequence.)\n\t\tconst rollback = async (): Promise<void> => {\n\t\t\ttry {\n\t\t\t\t// tearDownDevEnvironment cascades the box + volume + cloned DB;\n\t\t\t\t// it only accepts dev-env services, which this is (dev-env image).\n\t\t\t\tawait ctx.hoststack.services.tearDownDevEnvironment(teamId, service.id);\n\t\t\t} catch {\n\t\t\t\ttry {\n\t\t\t\t\tawait ctx.hoststack.services.delete(teamId, service.id);\n\t\t\t\t} catch {\n\t\t\t\t\t// Best-effort: surface the original failure to the caller\n\t\t\t\t\t// even if cleanup itself fails (orphan will need manual\n\t\t\t\t\t// removal via delete_dev_environment / the dashboard).\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\n\t\ttry {\n\t\t\tconst envVars: { key: string; value: string; isSecret: boolean }[] = [];\n\t\t\tif (args.hoststack_api_key)\n\t\t\t\tenvVars.push({\n\t\t\t\t\tkey: 'HOSTSTACK_API_KEY',\n\t\t\t\t\tvalue: args.hoststack_api_key,\n\t\t\t\t\tisSecret: true,\n\t\t\t\t});\n\t\t\tif (args.poststack_api_key)\n\t\t\t\tenvVars.push({\n\t\t\t\t\tkey: 'POSTSTACK_API_KEY',\n\t\t\t\t\tvalue: args.poststack_api_key,\n\t\t\t\t\tisSecret: true,\n\t\t\t\t});\n\t\t\t// Clone an arbitrary repo at boot (the image entrypoint reads these).\n\t\t\tif (args.repo_url) {\n\t\t\t\tenvVars.push({\n\t\t\t\t\tkey: 'HOSTSTACK_DEVENV_REPO_URL',\n\t\t\t\t\tvalue: args.repo_url,\n\t\t\t\t\tisSecret: false,\n\t\t\t\t});\n\t\t\t\tif (args.branch)\n\t\t\t\t\tenvVars.push({\n\t\t\t\t\t\tkey: 'HOSTSTACK_DEVENV_BRANCH',\n\t\t\t\t\t\tvalue: args.branch,\n\t\t\t\t\t\tisSecret: false,\n\t\t\t\t\t});\n\t\t\t}\n\t\t\tif (envVars.length > 0) {\n\t\t\t\tawait ctx.hoststack.envVars.bulkSet(teamId, service.id, { vars: envVars });\n\t\t\t}\n\n\t\t\t// A Dev Box without its /workspace volume silently loses all\n\t\t\t// out-of-image work on the next recreate. ABORT rather than deploy\n\t\t\t// a non-persistent box: a missing volume is a hard failure here.\n\t\t\ttry {\n\t\t\t\tawait ctx.hoststack.volumes.create(teamId, service.id, {\n\t\t\t\t\tname: DEV_ENV_VOLUME.name,\n\t\t\t\t\tmountPath: DEV_ENV_VOLUME.mountPath,\n\t\t\t\t\tsizeGb,\n\t\t\t\t});\n\t\t\t} catch (error: unknown) {\n\t\t\t\tconst reason = error instanceof Error ? error.message : String(error);\n\t\t\t\tawait rollback();\n\t\t\t\treturn respondError(\n\t\t\t\t\t`Failed to attach the /workspace volume for Dev Box \"${name}\" — aborted and rolled back rather than deploy a box that would lose all work on recreate. Volume error: ${reason}`,\n\t\t\t\t\t{ volumeError: reason, rolledBack: true },\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst deploy = await ctx.hoststack.deploys.trigger(teamId, service.id);\n\t\t\tconst deployId = deploy.deploy?.id ?? null;\n\n\t\t\treturn respond({\n\t\t\t\tsummary: `Created AI Dev Box \"${name}\" (${service.publicId}) with a /workspace volume — deploying. Open it in the dashboard's Dev Boxes section (or the service's Terminal tab) once running.`,\n\t\t\t\tdata: { service: shapeService(service), volumeAttached: true, deployId },\n\t\t\t});\n\t\t} catch (error: unknown) {\n\t\t\t// Any post-create failure (env-var set, deploy trigger, …) tears the\n\t\t\t// billable box back down so nothing is orphaned.\n\t\t\tconst reason = error instanceof Error ? error.message : String(error);\n\t\t\tawait rollback();\n\t\t\treturn respondError(\n\t\t\t\t`Failed to provision Dev Box \"${name}\" — rolled back the partially-created box. Error: ${reason}`,\n\t\t\t\t{ rolledBack: true },\n\t\t\t);\n\t\t}\n\t},\n});\n\ndefineTool({\n\tname: 'spin_up_dev_environment',\n\tcategory: 'services',\n\tdescription: [\n\t\t'Spin up an agentic Dev Box FROM an existing service so you can reproduce a bug, fix it, view it, and ship it. Creates a cloud Dev Box (Claude/Codex/OpenCode + MCPs) that RUNS a clone of the app: the repo is auto-cloned into /workspace, the service env-vars are copied, and its linked database is cloned (so the box never touches the prod DB). The box gets an unguessable public dev URL and seamless `git push`.',\n\t\t'',\n\t\t'This makes a cloud Dev Box, NOT a project deploy environment — for a -dev/-staging deploy target use create_environment instead.',\n\t\t'',\n\t\t'When to use: \"spin up a dev environment for <service>\", \"I found a bug on <service>, give me a box to fix it\". Distinct from create_dev_environment, which makes a BARE box not tied to any app.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: the source service to debug — numeric id or publicId (\"svc_…\").',\n\t\t' - include_database_clone (optional): clone the linked database so the app runs on a copy of real data (default true).',\n\t\t' - name (optional): dev box name (default \"<source>-dev\").',\n\t\t'',\n\t\tDEV_BOX_INSIDE,\n\t\t'',\n\t\t'Returns: { service, devUrl, deployId }. Once it is live: open the Terminal tab, run `claude`, start the dev server (it must bind $PORT), and view the app at https://<devUrl>. Tear it all down later with delete_dev_environment.',\n\t\t'',\n\t\t'Example: spin_up_dev_environment({ service_id: \"svc_api\" }) → a dev box running a clone of the api service (repo + env-vars + cloned DB) with a public dev URL.',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z\n\t\t\t.union([z.number().int().positive(), z.string()])\n\t\t\t.describe('Source service to debug — numeric id or publicId.'),\n\t\tinclude_database_clone: z\n\t\t\t.boolean()\n\t\t\t.optional()\n\t\t\t.describe('Clone the linked database so the app runs on copied data (default true).'),\n\t\tname: z\n\t\t\t.string()\n\t\t\t.min(1)\n\t\t\t.max(100)\n\t\t\t.optional()\n\t\t\t.describe('Dev box name (default \"<source>-dev\").'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst serviceId = await ctx.hoststack.resolveId(args.service_id, {\n\t\t\tkind: 'service',\n\t\t\tteamId,\n\t\t});\n\t\tconst opts: { includeDatabaseClone?: boolean; name?: string } = {};\n\t\tif (args.include_database_clone !== undefined)\n\t\t\topts.includeDatabaseClone = args.include_database_clone;\n\t\tif (args.name !== undefined) opts.name = args.name;\n\t\tconst result = await ctx.hoststack.services.spinUpDevEnvironment(teamId, serviceId, opts);\n\t\treturn respond({\n\t\t\tsummary: `Spun up dev environment \"${result.service.name}\" (${result.service.publicId}) — deploying. Once live: open the Terminal tab, run \\`claude\\`, start the dev server on $PORT, and view it at https://${result.devUrl}. \\`git push\\` works from inside.`,\n\t\t\tdata: {\n\t\t\t\tservice: shapeService(result.service),\n\t\t\t\tdevUrl: result.devUrl,\n\t\t\t\tdeployId: result.deployId,\n\t\t\t},\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'delete_dev_environment',\n\tcategory: 'services',\n\tdescription: [\n\t\t'Tear down an agentic Dev Box in one call: removes the cloud Dev Box AND cascade-deletes its cloned database, its /workspace volume, and the auto-created `development` environment if it is now empty.',\n\t\t'',\n\t\t'This deletes a cloud Dev Box, NOT a project deploy environment — to remove a -dev/-staging deploy target use delete_environment instead.',\n\t\t'',\n\t\t'When to use: \"delete / tear down the dev environment\", once you have shipped the fix and no longer need the box.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: the dev box to tear down — numeric id or publicId.',\n\t\t'',\n\t\t'Returns: { ok: true }. Irreversible — the cloned database and workspace volume are deleted.',\n\t\t'',\n\t\t'Example: delete_dev_environment({ service_id: \"svc_api_dev\" }) → removes the dev box, its cloned database, and the /workspace volume.',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z\n\t\t\t.union([z.number().int().positive(), z.string()])\n\t\t\t.describe('The dev box to tear down — numeric id or publicId.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst serviceId = await ctx.hoststack.resolveId(args.service_id, {\n\t\t\tkind: 'service',\n\t\t\tteamId,\n\t\t});\n\t\tawait ctx.hoststack.services.tearDownDevEnvironment(teamId, serviceId);\n\t\treturn respond({\n\t\t\tsummary:\n\t\t\t\t'Dev environment torn down — box, cloned database, /workspace volume, and empty development environment removed.',\n\t\t\tdata: { ok: true },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'resize_dev_environment',\n\tcategory: 'services',\n\tdescription: [\n\t\t'Resize a cloud Dev Box (or any service) to a different size tier — the supported way to give it more memory/CPU/disk headroom (e.g. when ESLint/tsc OOMs).',\n\t\t'',\n\t\t'This resizes a cloud Dev Box, NOT a project deploy environment.',\n\t\t'',\n\t\t'When to use: a Dev Box OOM-killed (see exitReason/recommendedSize from list_dev_environments), or you just want more headroom. This changes the SIZE TIER.',\n\t\t'',\n\t\t\"How it applies: the new tier's memory + CPU take effect LIVE on the running container (no recreate, no dropped shell sessions); a larger disk takes effect on the next recreate (suspend→resume). Dev boxes are floored to the OOM-safe minimum size server-side.\",\n\t\t'',\n\t\t'The tier is not always the container\\'s limit, so check `effectiveMemoryMb` from list_dev_environments rather than assuming: a dedicated (Pro) tier resolves ~1.5 GB below nominal for agent headroom, and a per-service memory override REPLACES the tier figure rather than merely clamping to it. An UPSIZE now retires an override that would hold the box below its new tier; a re-assert of the SAME tier does not, so on a box already at the top tier a resize cannot lift an override — clear it with a config PATCH of memoryMb to 512 (the legacy floor, which means \"no override\"), or use the Remove-cap button on the box\\'s Settings → General tab. `memoryPinnedBelowTier: true` is how you tell that case apart from a box that is genuinely maxed out.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: the box to resize — numeric id or publicId.',\n\t\t' - size: target tier — one of the service catalog sizes (e.g. \"standard\", \"large\", \"xlarge\").',\n\t\t'',\n\t\t'Returns: { service } with the new plan.',\n\t\t'',\n\t\t'Example: resize_dev_environment({ service_id: \"svc_skyskraber_dev\", size: \"large\" }) → bumps the box to the large tier, applied live.',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z\n\t\t\t.union([z.number().int().positive(), z.string()])\n\t\t\t.describe('The box to resize — numeric id or publicId.'),\n\t\tsize: z\n\t\t\t.enum(SERVICE_PLANS)\n\t\t\t.describe(\n\t\t\t\t'Target size tier (service catalog size, e.g. \"standard\", \"large\", \"xlarge\").',\n\t\t\t),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst serviceId = await ctx.hoststack.resolveId(args.service_id, {\n\t\t\tkind: 'service',\n\t\t\tteamId,\n\t\t});\n\t\tconst { service } = await ctx.hoststack.services.resize(teamId, serviceId, args.size);\n\t\treturn respond({\n\t\t\tsummary: `Resized to ${service.plan} — memory/CPU applied live; disk grows on next recreate.`,\n\t\t\tdata: { service: shapeService(service) },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'list_dev_environments',\n\tcategory: 'services',\n\tdescription: [\n\t\t'List the team\\'s cloud Dev Boxes (the dashboard \"Dev Boxes\" / Development section), each annotated with the companion services attached to it.',\n\t\t'',\n\t\t\"These are cloud Dev Boxes, NOT project deploy environments — to list a project's deploy targets (Production / Staging / Preview) use list_environments instead.\",\n\t\t'',\n\t\t'When to use: \"show my dev environments\", before opening/tearing one down, to find a box\\'s id.',\n\t\t'',\n\t\t'Returns: { items: [{ ...service, devUrl, databases, exitReason, recommendedSize, effectiveMemoryMb, memoryPinnedBelowTier }] } where `databases` lists the companion engines wired into the box (e.g. [\"postgres\",\"redis\"]). `exitReason` is \"oom_killed\" / \"crashed\" / null for the box\\'s last container exit; when it is \"oom_killed\", `recommendedSize` is the next tier up to rescale to (use resize_dev_environment).',\n\t\t'',\n\t\t'`effectiveMemoryMb` is the container\\'s REAL ceiling — what `/sys/fs/cgroup/memory.max` reads inside the box — and is the number to size any in-box work against. Do NOT derive it from `plan`: a dedicated (Pro) tier sits ~1.5 GB below nominal, and a per-service override replaces the tier figure outright. When `memoryPinnedBelowTier` is true an override, not the tier, is the cap, so `recommendedSize: null` means \"nothing bigger to sell you\" rather than \"nothing you can do\" — see resize_dev_environment for how to lift it.',\n\t\t'',\n\t\t'Example: list_dev_environments() → every dev box for the active team.',\n\t].join('\\n'),\n\tinput: {},\n\thandler: async (_args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst { environments } = await ctx.hoststack.services.listDevEnvironments(teamId);\n\t\tconst oomCount = environments.filter((e) => e.exitReason === 'oom_killed').length;\n\t\treturn respond({\n\t\t\tsummary:\n\t\t\t\t`${environments.length} dev environment${environments.length === 1 ? '' : 's'}.` +\n\t\t\t\t(oomCount > 0\n\t\t\t\t\t? ` ${oomCount} recently OOM-killed — consider resize_dev_environment.`\n\t\t\t\t\t: ''),\n\t\t\tdata: {\n\t\t\t\titems: environments.map((env) => ({\n\t\t\t\t\t...shapeService(env),\n\t\t\t\t\tdevUrl: env.devUrl ?? null,\n\t\t\t\t\tdatabases: env.databases ?? [],\n\t\t\t\t\texitReason: env.exitReason ?? null,\n\t\t\t\t\trecommendedSize: env.recommendedSize ?? null,\n\t\t\t\t\t// The number an agent should size its work against. Absent it,\n\t\t\t\t\t// the only honest way to learn a box's ceiling was to shell in\n\t\t\t\t\t// and read the cgroup — so every box rediscovered its own.\n\t\t\t\t\teffectiveMemoryMb: env.effectiveMemoryMb ?? null,\n\t\t\t\t\tmemoryPinnedBelowTier: env.memoryPinnedBelowTier ?? false,\n\t\t\t\t})),\n\t\t\t},\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'list_templates',\n\tcategory: 'services',\n\tdescription: [\n\t\t'List the app quickstart templates the dashboard New-Service wizard offers, plus the separate Dev Box preset. Two kinds live in one list: a stack template (Next.js, Django, SvelteKit, Spring Boot, …) is a pre-filled install/build/start command set you pair with a Git repo, and a one-click app (WordPress, Ghost, n8n, Uptime Kuma, Vaultwarden) is a prebuilt image that needs no repo at all.',\n\t\t'',\n\t\t'When to use: whenever asked to deploy a known stack (\"deploy my Next.js app\", \"put this Django project on HostStack\"), before hand-writing a start command; whenever asked for a packaged app (\"deploy WordPress\", \"I want a Ghost blog\"), before reaching for a bare docker_image; and before creating a Dev Box, to confirm the image, /workspace size, plan floor, and companion engines instead of duplicating those constants.',\n\t\t'',\n\t\t'Returns: { templates: [{ id, name, description, type, runtime?, installCommand?, buildCommand?, startCommand?, publishPath?, cronSchedule?, dockerImage?, port?, requiresTemplateId? }], devBox: { id, name, image, volume: { name, mountPath, sizeGb }, minPlan, createWith, companions, inBox } }. A template WITHOUT `dockerImage` is source-built — pass its commands to create_service with a `github_repo_id`. A template WITH `dockerImage` is flagged `requiresTemplateId` and must be created as create_service({ template_id, docker_image, port, … }): the volume, scratch dirs, uid, generated secrets and companion managed database it needs are attached server-side from the id, and the same image sent without it crash-loops on the read-only rootfs. The Dev Box is NOT in `templates`: it is created with create_standalone_dev_environment / create_dev_environment, never create_service. `companions` are SEPARATE managed databases you can attach; `inBox` is what the box already runs on its own (engines via `dev-services`, language runtimes via `dev-runtime`) — check it before concluding a box cannot run something.',\n\t\t'',\n\t\t'Example: list_templates() → { templates: [{ id: \"nextjs-ssr\", name: \"Next.js\", type: \"web_service\", runtime: \"node\", installCommand: \"npm install\", buildCommand: \"npm run build\", startCommand: \"npm run start\" }, { id: \"wordpress\", name: \"WordPress\", type: \"web_service\", dockerImage: \"wordpress:php8.3-apache\", port: 80, requiresTemplateId: true }, …], devBox: { id: \"dev-environment\", image: \"registry.hoststack.dev/hoststack/dev-env:latest\", volume: { mountPath: \"/workspace\", sizeGb: 10 }, minPlan: \"standard\", companions: [\"postgres\",\"redis\",\"meilisearch\"] } }',\n\t].join('\\n'),\n\tinput: {},\n\thandler: async () => {\n\t\t// The Dev Box preset is assembled from THIS package's own constants\n\t\t// (DEV_ENV_IMAGE / DEV_ENV_VOLUME / DEV_ENV_MIN_SIZE), and the app\n\t\t// catalog from lib/app-templates.ts. Both are copies of\n\t\t// packages/shared, NOT imports, per the workspace constraint (the MCP\n\t\t// package must not depend on the private @hoststack/shared package).\n\t\t//\n\t\t// It is returned under its own key rather than in `templates`: it is not\n\t\t// creatable with create_service (hard plan floor, /workspace volume,\n\t\t// companion DBs), and folding it into the same list is exactly what made\n\t\t// an agent try to build one through the wrong tool.\n\t\tconst devBox = {\n\t\t\tid: 'dev-environment',\n\t\t\tname: 'AI Dev Environment',\n\t\t\timage: DEV_ENV_IMAGE,\n\t\t\tvolume: {\n\t\t\t\tname: DEV_ENV_VOLUME.name,\n\t\t\t\tmountPath: DEV_ENV_VOLUME.mountPath,\n\t\t\t\tsizeGb: DEV_ENV_VOLUME.sizeGb,\n\t\t\t},\n\t\t\t/** OOM-safe plan floor — a coding agent + build needs at least this. */\n\t\t\tminPlan: DEV_ENV_MIN_SIZE,\n\t\t\t/** Create it with these, never create_service. */\n\t\t\tcreateWith: ['create_standalone_dev_environment', 'create_dev_environment'],\n\t\t\t/** Companion engines you can attach (fresh + empty), like local `make db-up`. */\n\t\t\tcompanions: ['postgres', 'redis', 'meilisearch'],\n\t\t\t/**\n\t\t\t * What the box can already run without attaching anything. Reported\n\t\t\t * so an agent picking a template can see that \"it needs MySQL\" or\n\t\t\t * \"it's a PHP app\" is already satisfied, instead of concluding the\n\t\t\t * box is unsuitable and reaching for Docker (there is no daemon).\n\t\t\t */\n\t\t\tinBox: {\n\t\t\t\tengines: {\n\t\t\t\t\tdefault: ['postgres', 'redis', 'meilisearch'],\n\t\t\t\t\toptIn: ['mysql', 'mongodb'],\n\t\t\t\t\tstart: 'dev-services up [mysql|mongodb]',\n\t\t\t\t},\n\t\t\t\truntimes: {\n\t\t\t\t\tbuiltin: ['node', 'bun', 'python', 'php'],\n\t\t\t\t\t// erlang is listed because elixir cannot run without it —\n\t\t\t\t\t// see DEV_BOX_INSIDE. Ordering matters for a caller acting\n\t\t\t\t\t// on this: add erlang, then elixir.\n\t\t\t\t\tonDemand: ['go', 'java', 'ruby', 'rust', 'erlang', 'elixir', 'dotnet'],\n\t\t\t\t\tinstall: 'dev-runtime add <name>',\n\t\t\t\t},\n\t\t\t},\n\t\t};\n\t\t// The image templates carry the flag per row rather than leaving it to the\n\t\t// prose above. Everything else in this list is \"copy these fields into\n\t\t// create_service\", and an entry that looks the same but silently needs a\n\t\t// `template_id` alongside is the one difference a caller skimming the\n\t\t// JSON would miss — and would only find out about from a crash-looping\n\t\t// container two deploys later.\n\t\tconst templates = MCP_APP_TEMPLATES.map((t) =>\n\t\t\tt.dockerImage ? { ...t, requiresTemplateId: true } : t,\n\t\t);\n\t\t// Counted, not enumerated: respond() hard-throws over SUMMARY_MAX_LEN\n\t\t// (500), and a summary that lists every id would start failing at\n\t\t// runtime the moment the catalog grows. The ids are in `data` anyway.\n\t\tconst byType = new Map<string, number>();\n\t\tfor (const t of templates) byType.set(t.type, (byType.get(t.type) ?? 0) + 1);\n\t\tconst breakdown = [...byType].map(([type, n]) => `${n} ${type}`).join(', ');\n\t\treturn respond({\n\t\t\tsummary:\n\t\t\t\t`${templates.length} app templates (${breakdown}) — pick one by id, then either pass its ` +\n\t\t\t\t'commands to create_service with a github_repo_id, or, if it has a dockerImage, ' +\n\t\t\t\t'pass template_id + docker_image + port. Plus the Dev Box preset ' +\n\t\t\t\t`(image ${DEV_ENV_IMAGE}, /workspace ${DEV_ENV_VOLUME.sizeGb} GB, floor \"${DEV_ENV_MIN_SIZE}\"), ` +\n\t\t\t\t'which is created with create_standalone_dev_environment, not create_service.',\n\t\t\tdata: { templates, devBox },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'create_standalone_dev_environment',\n\tcategory: 'services',\n\tdescription: [\n\t\t\"Create a STANDALONE Dev Box in one call: a cloud box (Claude Code + Codex + OpenCode + MCPs) on a persistent /workspace, from a connected GitHub repo, an arbitrary clone URL, or blank — with optional companion Postgres / Redis / Meilisearch wired into its env (mirrors a local `make db-up`). The box lives in the team's hidden Development home, NOT under a project.\",\n\t\t'',\n\t\t'This makes a cloud Dev Box, NOT a project deploy environment (create_environment → Production / Staging / Preview). It is a workspace you code in, not a deployment target.',\n\t\t'',\n\t\tDEV_BOX_INSIDE,\n\t\t'',\n\t\t\"Those in-box engines are the no-Docker replacement for a local `make db-up`, and are the right answer for development inside the box. The `companions` option is a DIFFERENT thing: it attaches SEPARATE managed databases to the box's environment, for when the box needs a real, persistent, backed-up datastore rather than a scratch one.\",\n\t\t'',\n\t\t'When to use: \"create a dev environment for <repo>\", \"spin me up a cloud dev box with a Postgres\". Distinct from spin_up_dev_environment (which clones an EXISTING service) and create_dev_environment (a bare box in a chosen project).',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - name (optional): the Dev Box name. Omit it and the box is named after its source (the repo name, or \"dev-box\" when blank), with a numeric suffix if that name is taken — so \"give me a dev box\" needs no invented name.',\n\t\t' - source_kind: \"github_repo\" (clone a connected repo — needs github_repo_id), \"url\" (clone any http(s) git URL — needs clone_url), or \"blank\" (empty box).',\n\t\t' - github_repo_id (for github_repo): HostStack row id of a connected GitHub repo — call list_github_repos for it. It is NOT the id GitHub shows, and it cannot be guessed.',\n\t\t' - clone_url (for url): an http(s) git clone URL.',\n\t\t' - branch (optional): branch to clone.',\n\t\t' - databases (optional): companion services to attach — any of \"postgres\", \"redis\", \"meilisearch\".',\n\t\t\" - machine (optional): run the box on one of the team's OWN enrolled machines (name or id, see list_machines) instead of HostStack compute. /workspace then lives on that machine's disk, so the box is reachable only while it is switched on, and any companion databases are created there too.\",\n\t\t' - plan (optional): box size. Defaults to \"standard\" (2 GB) — the OOM-safe floor (DEV_ENV_MIN_SIZE); a smaller plan is floored up to \"standard\" server-side.',\n\t\t\" - agent_accounts (optional): bind specific saved agent logins by account id per provider. OMIT to auto-inherit the box owner's default logins (claude/codex/opencode), so `claude` is already authenticated on first boot — no manual login.\",\n\t\t'',\n\t\t'Returns: { service, devUrl, deployId } — deploying. Once live: open the Terminal tab, run `claude`, start the dev server on $PORT, view at https://<devUrl>. Tear down with delete_dev_environment.',\n\t\t'',\n\t\t'Example: list_github_repos({ q: \"app\" }) → id 42, then create_standalone_dev_environment({ name: \"app-dev\", source_kind: \"github_repo\", github_repo_id: 42, databases: [\"postgres\",\"redis\"] })',\n\t].join('\\n'),\n\tinput: {\n\t\tname: z\n\t\t\t.string()\n\t\t\t.min(1)\n\t\t\t.max(100)\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t'Dev Box name. Omit to have it named after the source (the repo name, or \"dev-box\" for a blank one), de-duplicated against existing boxes.',\n\t\t\t),\n\t\tsource_kind: z.enum(['github_repo', 'url', 'blank']).describe('Where the code comes from.'),\n\t\tgithub_repo_id: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.positive()\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t'Connected GitHub repo id from list_github_repos (required when source_kind=\"github_repo\").',\n\t\t\t),\n\t\tclone_url: z\n\t\t\t.string()\n\t\t\t.url()\n\t\t\t.optional()\n\t\t\t.describe('http(s) git clone URL (required when source_kind=\"url\").'),\n\t\tbranch: z.string().min(1).max(255).optional().describe('Branch to clone.'),\n\t\tdatabases: z\n\t\t\t.array(z.enum(['postgres', 'redis', 'meilisearch']))\n\t\t\t.optional()\n\t\t\t.describe('Companion services to attach (fresh + empty).'),\n\t\tplan: z\n\t\t\t.enum(SERVICE_PLANS)\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t'Box size (default \"standard\" — 2 GB; a smaller plan is floored to \"standard\").',\n\t\t\t),\n\t\tagent_accounts: z\n\t\t\t.array(\n\t\t\t\tz.object({\n\t\t\t\t\tprovider: z.enum(['claude', 'codex', 'opencode']),\n\t\t\t\t\taccount_id: z.number().int().positive(),\n\t\t\t\t}),\n\t\t\t)\n\t\t\t.max(3)\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t\"Bind saved agent logins by account id per provider. Omit to inherit the box owner's default logins automatically.\",\n\t\t\t),\n\t\tmachine: machineInput,\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tlet source: CreateDevEnvironmentInput['source'];\n\t\tif (args.source_kind === 'github_repo') {\n\t\t\tif (!args.github_repo_id) {\n\t\t\t\treturn respond({\n\t\t\t\t\tsummary: 'github_repo_id is required when source_kind is \"github_repo\".',\n\t\t\t\t\tdata: { ok: false },\n\t\t\t\t});\n\t\t\t}\n\t\t\tsource = {\n\t\t\t\tkind: 'github_repo',\n\t\t\t\tgithubRepoId: args.github_repo_id,\n\t\t\t\t...(args.branch ? { branch: args.branch } : {}),\n\t\t\t};\n\t\t} else if (args.source_kind === 'url') {\n\t\t\tif (!args.clone_url) {\n\t\t\t\treturn respond({\n\t\t\t\t\tsummary: 'clone_url is required when source_kind is \"url\".',\n\t\t\t\t\tdata: { ok: false },\n\t\t\t\t});\n\t\t\t}\n\t\t\tsource = {\n\t\t\t\tkind: 'url',\n\t\t\t\tcloneUrl: args.clone_url,\n\t\t\t\t...(args.branch ? { branch: args.branch } : {}),\n\t\t\t};\n\t\t} else {\n\t\t\tsource = { kind: 'blank' };\n\t\t}\n\t\tconst machineId =\n\t\t\targs.machine !== undefined\n\t\t\t\t? await resolveMachineId(ctx, teamId, args.machine)\n\t\t\t\t: undefined;\n\t\tconst input: CreateDevEnvironmentInput = {\n\t\t\t...(args.name ? { name: args.name } : {}),\n\t\t\tsource,\n\t\t\t...(machineId !== undefined ? { machineId } : {}),\n\t\t\t...(args.databases ? { databases: args.databases } : {}),\n\t\t\t...(args.plan ? { plan: args.plan } : {}),\n\t\t\t...(args.agent_accounts && args.agent_accounts.length > 0\n\t\t\t\t? {\n\t\t\t\t\t\tagentAccounts: args.agent_accounts.map((a) => ({\n\t\t\t\t\t\t\tprovider: a.provider,\n\t\t\t\t\t\t\taccountId: a.account_id,\n\t\t\t\t\t\t})),\n\t\t\t\t\t}\n\t\t\t\t: {}),\n\t\t};\n\t\tconst result = await ctx.hoststack.services.createDevEnvironment(teamId, input);\n\t\treturn respond({\n\t\t\tsummary: `Created dev environment \"${result.service.name}\" (${result.service.publicId}) — deploying. Once live: open the Terminal tab, run \\`claude\\`, start the dev server on $PORT, and view it at https://${result.devUrl}.`,\n\t\t\tdata: {\n\t\t\t\tservice: shapeService(result.service),\n\t\t\t\tdevUrl: result.devUrl,\n\t\t\t\tdeployId: result.deployId,\n\t\t\t},\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'get_service',\n\tcategory: 'services',\n\tdescription: [\n\t\t'Fetch a single service by ID with its current status AND its service_config row (resources, health-check tuning, scaling, restart policy).',\n\t\t'',\n\t\t'When to use: drilling into a service after list_services, checking deploy/runtime status, grabbing the repo+branch before triggering a deploy, or inspecting the health-check / autoscale knobs before tweaking them via update_service_config.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service (e.g. \"svc_abc123\").',\n\t\t'',\n\t\t'Returns: { service: Service, config: ServiceConfig } — service has type/status/runtime/branch/autoDeploy/region/plan/timestamps plus repoProvider/repoFullName/repoUrl for a source-built service (absent when it deploys a prebuilt image); config has memoryMb, cpuShares, diskSizeGb, port, protocol, healthCheckEnabled, healthCheckInterval, healthCheckTimeout, healthCheckGracePeriodSec, allowSearchIndexing, restartPolicy, deployStrategy (\"rolling\" | \"recreate\"), preDeployCommand, min/maxInstances, scale thresholds.',\n\t\t'',\n\t\t'Example: get_service({ service_id: \"svc_abc\" }) → { service: { type: \"web\", status: \"running\", … }, config: { healthCheckGracePeriodSec: 120, … } }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId (e.g. svc_abc123).'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\t// Bundle service + config so a single call answers both \"how is it\n\t\t// running?\" and \"how is it tuned?\". Previously callers had to follow\n\t\t// up with a separate read for any of the health-check / scaling /\n\t\t// resource knobs, which lived only on the service_config row.\n\t\tconst [serviceResponse, configResponse] = await Promise.all([\n\t\t\tctx.hoststack.services.get(teamId, args.service_id),\n\t\t\tctx.hoststack.services.getConfig(teamId, args.service_id),\n\t\t]);\n\t\tconst data = {\n\t\t\tservice: shapeService(serviceResponse.service),\n\t\t\tconfig: shape(configResponse.config),\n\t\t};\n\t\tconst status =\n\t\t\tdata.service && 'status' in data.service\n\t\t\t\t? (data.service as { status: string }).status\n\t\t\t\t: 'unknown';\n\t\treturn respond({ summary: `Service ${args.service_id} is ${status}.`, data });\n\t},\n});\n\ndefineTool({\n\tname: 'get_service_metrics',\n\tcategory: 'services',\n\tdescription: [\n\t\t'Fetch the latest CPU, memory, network, and request-rate metrics for a service.',\n\t\t'',\n\t\t'When to use: a service is reportedly slow, you suspect resource pressure, or you want a quick health snapshot before scaling. Returns a single point-in-time sample. For a time series — \"is memory growing?\", \"did CPU spike during that deploy?\" — use get_service_metrics_history instead.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t'',\n\t\t'Returns: { metrics: { cpu, memory, network, requests } } — values are normalised utilisation/throughput numbers.',\n\t\t'',\n\t\t'Example: get_service_metrics({ service_id: \"svc_abc\" }) → { metrics: { cpu: 0.42, memory: 0.71, … } }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.services.getMetrics(teamId, args.service_id);\n\t\tconst data = { metrics: shape(response.metrics) };\n\t\treturn respond({ summary: `Metrics snapshot for service ${args.service_id}.`, data });\n\t},\n});\n\ndefineTool({\n\tname: 'get_service_metrics_history',\n\tcategory: 'services',\n\tdescription: [\n\t\t'Fetch a metrics time series (CPU, memory, network, disk) for a service.',\n\t\t'',\n\t\t'When to use: answering \"is memory growing over the last hour?\", \"did CPU spike during deploy X?\", or \"what does normal look like for this service?\". Pairs well with get_deploy to align spikes against deploy times.',\n\t\t'',\n\t\t'Inputs (all optional — omit both for the trailing hour):',\n\t\t' - service_id: publicId of the service.',\n\t\t' - from: ISO-8601 lower bound OR relative offset like \"-15m\" / \"-2h\" / \"-7d\".',\n\t\t' - to: ISO-8601 upper bound (or relative offset). Defaults to now.',\n\t\t'',\n\t\t'Resolution: ≤7d → raw samples (one per ~30s), >7d → hourly pre-aggregates, >30d → daily. At most 300 points are returned, so a window wider than ~2.5h is thinned to fit.',\n\t\t'',\n\t\t'`cpuPercent` is the PEAK of whatever the point covers, and 100 means one host CORE — not 100% of the service. A `micro` (500 millicores) is capped by its cgroup at ~50, so ~50 IS that plan pegged. To compare against a `service.pressure_sustained` alert, which reports a share of the plan allowance, multiply by 1000/cpuMillicores (the alert ships that denominator in its metadata).',\n\t\t'',\n\t\t'Memory is the reading at that point, NOT a peak — so a memory number here can sit below an alert that averaged the hour.',\n\t\t'',\n\t\t'Returns: { history: Array<{ timestamp, cpuPercent, memoryUsedMb, memoryLimitMb, networkRxBytes, networkTxBytes, diskUsedMb }> }.',\n\t\t'',\n\t\t'Example: get_service_metrics_history({ service_id: \"svc_abc\", from: \"-1h\" }) → ~120 points for the last hour.',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t\tfrom: z\n\t\t\t.string()\n\t\t\t.optional()\n\t\t\t.describe('ISO-8601 lower bound or relative offset (e.g. \"-1h\", \"-2d\").'),\n\t\tto: z.string().optional().describe('ISO-8601 upper bound; defaults to now.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\t// Translate the same `-Nm/h/d` relative shorthand we accept in logs\n\t\t// so callers don't have to learn a second convention. The metrics\n\t\t// history endpoint expects ISO timestamps server-side; we resolve\n\t\t// relative offsets here for symmetry with get_service_logs.\n\t\tconst opts: { from?: string; to?: string } = {};\n\t\tconst resolveRel = (raw: string): string => {\n\t\t\tconst rel = /^-(\\d+)(s|m|h|d)$/.exec(raw.trim());\n\t\t\tif (!rel) return raw;\n\t\t\tconst amount = Number.parseInt(rel[1] as string, 10);\n\t\t\tconst unit = rel[2] as 's' | 'm' | 'h' | 'd';\n\t\t\tconst ms =\n\t\t\t\tunit === 's'\n\t\t\t\t\t? amount * 1000\n\t\t\t\t\t: unit === 'm'\n\t\t\t\t\t\t? amount * 60_000\n\t\t\t\t\t\t: unit === 'h'\n\t\t\t\t\t\t\t? amount * 3_600_000\n\t\t\t\t\t\t\t: amount * 86_400_000;\n\t\t\treturn new Date(Date.now() - ms).toISOString();\n\t\t};\n\t\tif (args.from) opts.from = resolveRel(args.from);\n\t\tif (args.to) opts.to = resolveRel(args.to);\n\n\t\tconst response = await ctx.hoststack.services.getMetricsHistory(\n\t\t\tteamId,\n\t\t\targs.service_id,\n\t\t\topts,\n\t\t);\n\t\tconst points = Array.isArray(response.history) ? response.history.length : 0;\n\t\treturn respond({\n\t\t\tsummary: `Returned ${points} metric point${points === 1 ? '' : 's'} for service ${args.service_id}.`,\n\t\t\tdata: { history: response.history },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'update_service',\n\tcategory: 'services',\n\tdescription: [\n\t\t'Rename a service. Only the human-readable name changes — the publicId is permanent.',\n\t\t'',\n\t\t'When to use: the user wants to relabel a service in the dashboard. For deeper config edits (build command, branch, scale, plan), use update_service_config.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t' - name: new name (1–60 chars).',\n\t\t'',\n\t\t'Returns: { service: Service }.',\n\t\t'',\n\t\t'Example: update_service({ service_id: \"svc_abc\", name: \"api-prod\" }) → { service: { name: \"api-prod\", … } }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t\tname: z.string().min(1).max(60).describe('New service name (1–60 chars).'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.services.update(teamId, args.service_id, {\n\t\t\tname: args.name,\n\t\t});\n\t\tconst data = { service: shapeService(response.service) };\n\t\treturn respond({ summary: `Renamed service ${args.service_id} to \"${args.name}\".`, data });\n\t},\n});\n\ndefineTool({\n\tname: 'update_service_config',\n\tcategory: 'services',\n\tdescription: [\n\t\t'Update build/runtime configuration for a service. All fields optional — pass only what you want to change.',\n\t\t'',\n\t\t'When to use: the user wants to tweak how a service builds, runs, scales, or health-checks. Build/runtime fields (branch, install/build/start command, root, dockerfile) take effect on the next deploy — call trigger_deploy after if you need them applied immediately. Instance_count, resource and health-check changes rescale or rewire without a redeploy.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t' - install_command, build_command, start_command (optional): shell commands. Pass null to clear.',\n\t\t' - branch (optional): git branch to track.',\n\t\t' - root_directory (optional): build context root inside the repo.',\n\t\t' - dockerfile_path (optional): path to Dockerfile relative to root_directory. Pass null to clear.',\n\t\t' - auto_deploy (optional): boolean — auto-deploy on git push.',\n\t\t' - health_check_path (optional): HTTP path the platform GETs to verify liveness (e.g. \"/health\"). Pass null for TCP-only check.',\n\t\t' - health_check_enabled (optional): boolean — toggle health checking on/off.',\n\t\t' - allow_search_indexing (optional): boolean — let search engines index the free *.hoststack.dev platform URL. Off by default; the platform URL is served with X-Robots-Tag: noindex so a site does not rank on a hostname it does not own. Custom domains are always indexable and unaffected. Applies on the next deploy.',\n\t\t' - health_check_interval (optional): integer 5–300 seconds — how often the check runs.',\n\t\t' - health_check_timeout (optional): integer 1–60 seconds — single-attempt timeout.',\n\t\t' - health_check_grace_period_sec (optional): integer 1–1800 seconds — startup tolerance before failures count. RAISE THIS (e.g. 180) when the agent reports \"Health check timed out\" on a cold-boot app (Bun + Vite SSR typically need 90–180s).',\n\t\t' - memory_mb (optional): integer 128–16384 — container memory cap.',\n\t\t' - cpu_shares (optional): integer 128–4096 — relative CPU weight.',\n\t\t' - disk_size_gb (optional): integer 1–100 — ephemeral disk cap.',\n\t\t' - port (optional): integer 1–65535 — container port the platform forwards traffic to.',\n\t\t' - protocol (optional): \"http\" | \"tcp\".',\n\t\t' - restart_policy (optional): \"always\" | \"on-failure\" | \"no\".',\n\t\t' - deploy_strategy (optional): \"rolling\" (default) | \"recreate\". SET THIS TO \"recreate\" for any single-container service that holds an exclusive lock on a mounted volume — a database running inside the container, an embedded queue, anything with a lockfile or a fixed host port on shared storage. Under the default rolling strategy those services CANNOT deploy at all: the new container can\\'t open the datadir the outgoing container still holds, so it exits; because it never goes healthy the old one is never stopped; because the old one is never stopped the lock is never released. The deploy deadlocks for the whole grace period and then reports \"Health check timed out\". \"recreate\" stops the old container first, at the cost of a brief outage.',\n\t\t' - pre_deploy_command (optional): shell command run before the new release accepts traffic (typical use: migrations).',\n\t\t' - instance_count (optional): integer 1–50 — pin both min and max instances to this value.',\n\t\t' - min_instances, max_instances (optional): integers — autoscale bounds. Use instead of instance_count when you want a range.',\n\t\t' - scale_cpu_threshold, scale_memory_threshold (optional): integer 10–100 — autoscale trigger percentage.',\n\t\t' - scale_requests_per_sec_threshold (optional): integer 1–10000 — autoscale on request rate instead of (or alongside) CPU and memory. Pass null to stop scaling on request rate.',\n\t\t' - build_timeout_seconds (optional): integer 60–1800 — how long a build may run before it is killed. Raise it for a service whose build legitimately takes longer than the default; pass null to go back to the default.',\n\t\t\" - plan (optional): move the service to a different size tier. This is the supported way to grow past the current tier's ceiling — memory_mb and cpu_shares are CLAMPED to the tier, so raising them alone does nothing once you are at the top of it. Memory and CPU apply to the running container where possible; disk grows on the next recreate. Changing tier changes what the service costs.\",\n\t\t' - docker_image (optional): repoint an image-backed service at a different image. Takes effect on the next deploy — this writes the reference, it does not pull. Pass null to clear it.',\n\t\t' - cron_schedule (optional): the schedule for a cron_job service. Pass null to clear it.',\n\t\t' - publish_path (optional): the directory a static_site publishes (e.g. \"dist\"). Pass null to clear it.',\n\t\t' - runtime (optional): runtime hint (\"node\" | \"bun\" | \"python\" | …), normally auto-detected from the repo. Set it when the detection is wrong.',\n\t\t' - log_filter_rules (optional): list of { pattern, action } rules applied at read time — to get_service_logs (tail AND count) and to the dashboard\\'s live stream alike. Pattern matches the message by case-insensitive substring, literally (a % or _ is not a wildcard); action is \"drop\" (filter out) or \"downgrade\" (flip stderr → stdout so it stops looking like an error). Stored lines are never altered, so removing a rule re-exposes everything it was hiding. Pass [] to clear all rules. Capped at 50 rules.',\n\t\t'',\n\t\t'Returns: { service?: Service, config?: ServiceConfig } — whichever rows were touched.',\n\t\t'',\n\t\t'Example: update_service_config({ service_id: \"svc_abc\", health_check_grace_period_sec: 180 }) → { config: { healthCheckGracePeriodSec: 180, … } }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t\tinstall_command: z\n\t\t\t.string()\n\t\t\t.nullable()\n\t\t\t.optional()\n\t\t\t.describe('Install shell command. Null clears.'),\n\t\tbuild_command: z\n\t\t\t.string()\n\t\t\t.nullable()\n\t\t\t.optional()\n\t\t\t.describe('Build shell command. Null clears.'),\n\t\tstart_command: z\n\t\t\t.string()\n\t\t\t.nullable()\n\t\t\t.optional()\n\t\t\t.describe('Start shell command. Null clears.'),\n\t\tbranch: z.string().optional().describe('Git branch to track.'),\n\t\troot_directory: z.string().optional().describe('Build context root.'),\n\t\tdockerfile_path: z\n\t\t\t.string()\n\t\t\t.nullable()\n\t\t\t.optional()\n\t\t\t.describe('Path to Dockerfile relative to root. Null clears.'),\n\t\tauto_deploy: z.boolean().optional().describe('Auto-deploy on push.'),\n\t\thealth_check_path: z\n\t\t\t.string()\n\t\t\t.nullable()\n\t\t\t.optional()\n\t\t\t.describe('HTTP health-check path (e.g. \"/health\"). Null = TCP-only check.'),\n\t\thealth_check_enabled: z.boolean().optional().describe('Toggle health checking on/off.'),\n\t\tallow_search_indexing: z\n\t\t\t.boolean()\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t'Let search engines index the free *.hoststack.dev platform URL (off by default). Custom domains are always indexable. Applies on the next deploy.',\n\t\t\t),\n\t\thealth_check_interval: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(5)\n\t\t\t.max(300)\n\t\t\t.optional()\n\t\t\t.describe('How often the check runs, in seconds (5–300).'),\n\t\thealth_check_timeout: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(1)\n\t\t\t.max(60)\n\t\t\t.optional()\n\t\t\t.describe('Single-attempt timeout in seconds (1–60).'),\n\t\thealth_check_grace_period_sec: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(1)\n\t\t\t.max(1800)\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t'Startup grace period in seconds (1–1800). Raise this if the app needs more time to boot before health checks start counting failures.',\n\t\t\t),\n\t\tmemory_mb: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(128)\n\t\t\t.max(16384)\n\t\t\t.optional()\n\t\t\t.describe('Container memory cap in MB (128–16384).'),\n\t\tcpu_shares: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(128)\n\t\t\t.max(4096)\n\t\t\t.optional()\n\t\t\t.describe('Relative CPU weight (128–4096).'),\n\t\tdisk_size_gb: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(1)\n\t\t\t.max(100)\n\t\t\t.optional()\n\t\t\t.describe('Ephemeral disk size in GB (1–100).'),\n\t\tport: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(1)\n\t\t\t.max(65535)\n\t\t\t.optional()\n\t\t\t.describe('Container port the platform forwards traffic to.'),\n\t\tprotocol: z.enum(['http', 'tcp']).optional().describe('Traffic protocol.'),\n\t\trestart_policy: z\n\t\t\t.enum(['always', 'on-failure', 'no'])\n\t\t\t.optional()\n\t\t\t.describe('Docker restart policy.'),\n\t\tdeploy_strategy: z\n\t\t\t.enum(['rolling', 'recreate'])\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t'How a deploy replaces the container. \"rolling\" (default) = start new, wait for healthy, switch traffic, stop old (zero downtime). \"recreate\" = stop old first, then start new (brief outage) — required for a container holding an exclusive lock on a mounted volume, which cannot deploy at all under rolling.',\n\t\t\t),\n\t\tpre_deploy_command: z\n\t\t\t.string()\n\t\t\t.optional()\n\t\t\t.describe('Shell command run before the new release accepts traffic.'),\n\t\tinstance_count: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.positive()\n\t\t\t.max(50)\n\t\t\t.optional()\n\t\t\t.describe('Pin min and max instances to this value (1–50).'),\n\t\tmin_instances: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(0)\n\t\t\t.max(50)\n\t\t\t.optional()\n\t\t\t.describe('Autoscale lower bound. Use with max_instances for a range.'),\n\t\tmax_instances: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(1)\n\t\t\t.max(50)\n\t\t\t.optional()\n\t\t\t.describe('Autoscale upper bound. Use with min_instances for a range.'),\n\t\tscale_cpu_threshold: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(10)\n\t\t\t.max(100)\n\t\t\t.optional()\n\t\t\t.describe('Autoscale CPU trigger percentage (10–100).'),\n\t\tscale_memory_threshold: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(10)\n\t\t\t.max(100)\n\t\t\t.optional()\n\t\t\t.describe('Autoscale memory trigger percentage (10–100).'),\n\t\tscale_requests_per_sec_threshold: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(1)\n\t\t\t.max(10000)\n\t\t\t.nullable()\n\t\t\t.optional()\n\t\t\t.describe('Autoscale request-rate trigger (1–10000 req/s). null stops using it.'),\n\t\tbuild_timeout_seconds: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(60)\n\t\t\t.max(1800)\n\t\t\t.nullable()\n\t\t\t.optional()\n\t\t\t.describe('Kill a build that runs longer than this (60–1800s). null = default.'),\n\t\tplan: z\n\t\t\t.enum(SERVICE_PLANS)\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t'Move to a different size tier. memory_mb and cpu_shares are clamped to the tier, so this is what raises the ceiling. Changes cost.',\n\t\t\t),\n\t\tdocker_image: z\n\t\t\t.string()\n\t\t\t.max(500)\n\t\t\t.nullable()\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t'Repoint an image-backed service. Applies on the next deploy. null clears it.',\n\t\t\t),\n\t\tcron_schedule: z\n\t\t\t.string()\n\t\t\t.max(100)\n\t\t\t.nullable()\n\t\t\t.optional()\n\t\t\t.describe('Cron expression for a cron_job service. null clears it.'),\n\t\tpublish_path: z\n\t\t\t.string()\n\t\t\t.max(500)\n\t\t\t.nullable()\n\t\t\t.optional()\n\t\t\t.describe('Static-site output directory, e.g. \"dist\". null clears it.'),\n\t\truntime: z\n\t\t\t.string()\n\t\t\t.max(50)\n\t\t\t.optional()\n\t\t\t.describe('Runtime hint (node/bun/python/…). Normally auto-detected.'),\n\t\tlog_filter_rules: z\n\t\t\t.array(\n\t\t\t\tz.object({\n\t\t\t\t\tpattern: z.string().min(1).max(200),\n\t\t\t\t\taction: z.enum(['drop', 'downgrade']),\n\t\t\t\t}),\n\t\t\t)\n\t\t\t.max(50)\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t'Runtime-log filter rules. Empty array [] clears all rules. Each pattern is case-insensitive substring match against the message.',\n\t\t\t),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\n\t\t// services-row fields (PATCH /services/:tid/:sid). Note: nullable\n\t\t// fields take `null` to clear, distinct from `undefined` which means\n\t\t// \"leave unchanged\" — passing `null` through to the underlying API\n\t\t// is what flips the column to NULL on the row.\n\t\tconst serviceUpdate: Record<string, unknown> = {};\n\t\tif (args.install_command !== undefined)\n\t\t\tserviceUpdate['installCommand'] = args.install_command;\n\t\tif (args.build_command !== undefined) serviceUpdate['buildCommand'] = args.build_command;\n\t\tif (args.start_command !== undefined) serviceUpdate['startCommand'] = args.start_command;\n\t\tif (args.dockerfile_path !== undefined)\n\t\t\tserviceUpdate['dockerfilePath'] = args.dockerfile_path;\n\t\tif (args.branch !== undefined) serviceUpdate['branch'] = args.branch;\n\t\tif (args.root_directory !== undefined) serviceUpdate['rootDirectory'] = args.root_directory;\n\t\tif (args.auto_deploy !== undefined) serviceUpdate['autoDeploy'] = args.auto_deploy;\n\t\tif (args.health_check_path !== undefined)\n\t\t\tserviceUpdate['healthCheckPath'] = args.health_check_path;\n\t\tif (args.plan !== undefined) serviceUpdate['plan'] = args.plan;\n\t\tif (args.cron_schedule !== undefined) serviceUpdate['cronSchedule'] = args.cron_schedule;\n\t\tif (args.publish_path !== undefined) serviceUpdate['publishPath'] = args.publish_path;\n\t\tif (args.runtime !== undefined) serviceUpdate['runtime'] = args.runtime;\n\n\t\t// service_config-row fields (PATCH /services/:tid/:sid/config). The\n\t\t// schema expects min_instances/max_instances; \"pin to N\" means setting\n\t\t// both to the same value.\n\t\tconst configUpdate: Record<string, unknown> = {};\n\t\tif (args.health_check_enabled !== undefined)\n\t\t\tconfigUpdate['healthCheckEnabled'] = args.health_check_enabled;\n\t\tif (args.allow_search_indexing !== undefined)\n\t\t\tconfigUpdate['allowSearchIndexing'] = args.allow_search_indexing;\n\t\tif (args.health_check_interval !== undefined)\n\t\t\tconfigUpdate['healthCheckInterval'] = args.health_check_interval;\n\t\tif (args.health_check_timeout !== undefined)\n\t\t\tconfigUpdate['healthCheckTimeout'] = args.health_check_timeout;\n\t\tif (args.health_check_grace_period_sec !== undefined)\n\t\t\tconfigUpdate['healthCheckGracePeriodSec'] = args.health_check_grace_period_sec;\n\t\tif (args.memory_mb !== undefined) configUpdate['memoryMb'] = args.memory_mb;\n\t\tif (args.cpu_shares !== undefined) configUpdate['cpuShares'] = args.cpu_shares;\n\t\tif (args.disk_size_gb !== undefined) configUpdate['diskSizeGb'] = args.disk_size_gb;\n\t\tif (args.port !== undefined) configUpdate['port'] = args.port;\n\t\tif (args.protocol !== undefined) configUpdate['protocol'] = args.protocol;\n\t\tif (args.restart_policy !== undefined) configUpdate['restartPolicy'] = args.restart_policy;\n\t\tif (args.deploy_strategy !== undefined)\n\t\t\tconfigUpdate['deployStrategy'] = args.deploy_strategy;\n\t\tif (args.pre_deploy_command !== undefined)\n\t\t\tconfigUpdate['preDeployCommand'] = args.pre_deploy_command;\n\t\tif (args.instance_count !== undefined) {\n\t\t\tconfigUpdate['minInstances'] = args.instance_count;\n\t\t\tconfigUpdate['maxInstances'] = args.instance_count;\n\t\t}\n\t\tif (args.min_instances !== undefined) configUpdate['minInstances'] = args.min_instances;\n\t\tif (args.max_instances !== undefined) configUpdate['maxInstances'] = args.max_instances;\n\t\tif (args.scale_cpu_threshold !== undefined)\n\t\t\tconfigUpdate['scaleCpuThreshold'] = args.scale_cpu_threshold;\n\t\tif (args.scale_memory_threshold !== undefined)\n\t\t\tconfigUpdate['scaleMemoryThreshold'] = args.scale_memory_threshold;\n\t\t// null is a meaningful value on both of these — it clears the setting\n\t\t// rather than leaving it alone — so test against undefined, not falsiness.\n\t\tif (args.scale_requests_per_sec_threshold !== undefined)\n\t\t\tconfigUpdate['scaleRequestsPerSecThreshold'] = args.scale_requests_per_sec_threshold;\n\t\tif (args.build_timeout_seconds !== undefined)\n\t\t\tconfigUpdate['buildTimeoutSeconds'] = args.build_timeout_seconds;\n\t\t// docker_image lives on the CONFIG row, not the service row, unlike the\n\t\t// other five added alongside it.\n\t\tif (args.docker_image !== undefined) configUpdate['dockerImage'] = args.docker_image;\n\t\tif (args.log_filter_rules !== undefined) {\n\t\t\t// Empty array is a meaningful value (clears all rules);\n\t\t\t// undefined leaves rules unchanged. Don't short-circuit.\n\t\t\tconfigUpdate['logFilterRules'] = args.log_filter_rules;\n\t\t}\n\n\t\tif (Object.keys(serviceUpdate).length === 0 && Object.keys(configUpdate).length === 0) {\n\t\t\treturn respond({ summary: 'No fields to update.', data: {} });\n\t\t}\n\n\t\tconst data: Record<string, unknown> = {};\n\t\tconst touchedFields: string[] = [];\n\t\tif (Object.keys(serviceUpdate).length > 0) {\n\t\t\tconst serviceResponse = await ctx.hoststack.services.update(\n\t\t\t\tteamId,\n\t\t\t\targs.service_id,\n\t\t\t\tserviceUpdate,\n\t\t\t);\n\t\t\tdata['service'] = shape(serviceResponse.service);\n\t\t\ttouchedFields.push(...Object.keys(serviceUpdate));\n\t\t}\n\t\tif (Object.keys(configUpdate).length > 0) {\n\t\t\tconst configResponse = await ctx.hoststack.services.updateConfig(\n\t\t\t\tteamId,\n\t\t\t\targs.service_id,\n\t\t\t\tconfigUpdate,\n\t\t\t);\n\t\t\tdata['config'] = shape(configResponse.config);\n\t\t\ttouchedFields.push(...Object.keys(configUpdate));\n\t\t}\n\n\t\treturn respond({\n\t\t\tsummary: `Updated ${touchedFields.join(', ')} on service ${args.service_id}.`,\n\t\t\tdata,\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'suspend_service',\n\tcategory: 'services',\n\tdescription: [\n\t\t'Suspend a service: stop all running instances and pause auto-deploys. The service stays configured and can be resumed later with resume_service.',\n\t\t'',\n\t\t'When to use: the user wants to temporarily stop traffic and billing without deleting the service (for example: dev environment overnight, debugging).',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t'',\n\t\t'Returns: { ok: true }.',\n\t\t'',\n\t\t'Example: suspend_service({ service_id: \"svc_dev\" }) → { ok: true }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tawait ctx.hoststack.services.suspend(teamId, args.service_id);\n\t\treturn respond({ summary: `Suspended service ${args.service_id}.`, data: { ok: true } });\n\t},\n});\n\ndefineTool({\n\tname: 'resume_service',\n\tcategory: 'services',\n\tdescription: [\n\t\t'Resume a previously suspended service: start instances back up and re-enable auto-deploys.',\n\t\t'',\n\t\t'When to use: the user wants to bring a suspended service back online. Pair with suspend_service.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t'',\n\t\t'Returns: { ok: true }.',\n\t\t'',\n\t\t'Example: resume_service({ service_id: \"svc_dev\" }) → { ok: true }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tawait ctx.hoststack.services.resume(teamId, args.service_id);\n\t\treturn respond({ summary: `Resumed service ${args.service_id}.`, data: { ok: true } });\n\t},\n});\n\ndefineTool({\n\tname: 'delete_service',\n\tcategory: 'services',\n\tdescription: [\n\t\t'Permanently delete a service: stops and removes its containers, releases its hostname and routes, and cascade-deletes its attached volumes, env vars, domains and deploy history. Irreversible.',\n\t\t'',\n\t\t'When to use: the user explicitly asks to delete or remove a service, or you are cleaning up a service that was created by mistake or is no longer needed. Without this, a broken or abandoned service could only be removed from the dashboard — so a service that failed to come up had to be left in place and worked around by creating another one.',\n\t\t'',\n\t\t'ALWAYS confirm with the user before calling this on anything that has served traffic. To stop a service temporarily without losing it, use suspend_service instead. To tear down an agentic Dev Box (which also cascades its cloned database), use delete_dev_environment.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service to delete.',\n\t\t'',\n\t\t'Returns: { ok: true }.',\n\t\t'',\n\t\t'Example: delete_service({ service_id: \"svc_abandoned\" }) → { ok: true }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tawait ctx.hoststack.services.delete(teamId, args.service_id);\n\t\treturn respond({\n\t\t\tsummary: `Deleted service ${args.service_id} and its attached resources. This cannot be undone.`,\n\t\t\tdata: { ok: true },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'get_service_logs',\n\tcategory: 'logs',\n\tdescription: [\n\t\t\"Fetch a snapshot of the running container's recent runtime logs (stdout/stderr). For *deploy* logs (build output), use get_deploy_logs instead.\",\n\t\t'',\n\t\t'When to use: a service is misbehaving in production and you need to read what it is currently logging. The tool returns a snapshot — there is no streaming over MCP. Re-call to get newer entries.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t' - lines (optional): tail size (default 200, max 1000).',\n\t\t' - since/until (optional): ISO-8601 timestamp OR a relative offset like \"-5m\", \"-1h\", \"-2d\".',\n\t\t' - stream (optional): \"stdout\" | \"stderr\". Omit to combine.',\n\t\t' - level (optional): real log level — trace/debug/info/warn/error/fatal. For structured JSON logs (pino, bunyan, OpenTelemetry severity) this filters on the parsed inner level field. For plain text logs it falls back to a stream-alias hint (info/debug → stdout, warn/error/fatal → stderr).',\n\t\t' - search (optional): case-insensitive substring grep, ≤100 chars.',\n\t\t' - count_only (optional): when true, returns { count } only — much cheaper for \"how many error lines in last 5m\" polling. It counts exactly what the same call would return, so `count` and the entry count agree whenever count <= lines.',\n\t\t'',\n\t\t\"If the service has log_filter_rules with action='drop' (see update_service_config), the lines they match are excluded from BOTH the tail and the count — so a count lower than the raw log volume is the rules working, not lines going missing. `stream` selects on the stream shown in the response, which a 'downgrade' rule may have flipped from stderr to stdout.\",\n\t\t'',\n\t\t'Returns: { logs: LogEntry[] | string } when count_only is false. Each entry has { timestamp, level?, stream, message }. `level` is the parsed inner level when the message is a structured JSON envelope (pino numeric or string), and undefined for plain-text logs. `stream` is always one of stdout/stderr. Or { count: number } when count_only is true.',\n\t\t'',\n\t\t'Example: get_service_logs({ service_id: \"svc_abc\", search: \"OOM\", since: \"-15m\" }) → { logs: [...] }.',\n\t\t'',\n\t\t'More examples:',\n\t\t' - Last 50 stderr lines from the past hour: get_service_logs({ service_id: \"svc_abc\", lines: 50, stream: \"stderr\", since: \"-1h\" })',\n\t\t' - Just count error lines without fetching them: get_service_logs({ service_id: \"svc_abc\", level: \"error\", since: \"-5m\", count_only: true }) → { count: 47 }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t\tlines: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.positive()\n\t\t\t.max(1000)\n\t\t\t.optional()\n\t\t\t.describe('Tail size; default 200, hard cap 1000.'),\n\t\tsince: z\n\t\t\t.string()\n\t\t\t.optional()\n\t\t\t.describe('ISO-8601 timestamp or relative offset (e.g. \"-5m\", \"-1h\").'),\n\t\tuntil: z.string().optional().describe('ISO-8601 timestamp or relative offset upper bound.'),\n\t\tstream: z.enum(['stdout', 'stderr']).optional().describe('Restrict to one stream.'),\n\t\tlevel: z\n\t\t\t.enum(['stdout', 'stderr', 'trace', 'debug', 'info', 'warn', 'error', 'fatal'])\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t'Filter by structured JSON log level (pino/bunyan/severity). Falls back to a stream-alias hint for plain-text logs (info/debug→stdout, warn/error/fatal→stderr).',\n\t\t\t),\n\t\tsearch: z.string().max(100).optional().describe('Case-insensitive substring filter.'),\n\t\tcount_only: z\n\t\t\t.boolean()\n\t\t\t.optional()\n\t\t\t.describe('When true, return only { count } — skips the log payload.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst opts: {\n\t\t\tlines?: number;\n\t\t\tsince?: string;\n\t\t\tuntil?: string;\n\t\t\tstream?: 'stdout' | 'stderr';\n\t\t\tlevel?: 'stdout' | 'stderr' | 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal';\n\t\t\tsearch?: string;\n\t\t\tcountOnly?: boolean;\n\t\t} = {\n\t\t\tlines: args.lines ?? 200,\n\t\t};\n\t\tif (args.since) opts.since = args.since;\n\t\tif (args.until) opts.until = args.until;\n\t\tif (args.stream) opts.stream = args.stream;\n\t\tif (args.level) opts.level = args.level;\n\t\tif (args.search) opts.search = args.search;\n\t\tif (args.count_only) opts.countOnly = args.count_only;\n\n\t\tconst response = await ctx.hoststack.services.getRuntimeLogs(teamId, args.service_id, opts);\n\t\tif ('count' in response) {\n\t\t\treturn respond({\n\t\t\t\tsummary: `${response.count} matching log line${response.count === 1 ? '' : 's'} for service ${args.service_id}.`,\n\t\t\t\tdata: { count: response.count },\n\t\t\t});\n\t\t}\n\t\tconst count = Array.isArray(response.logs)\n\t\t\t? response.logs.length\n\t\t\t: typeof response.logs === 'string'\n\t\t\t\t? response.logs.split('\\n').length\n\t\t\t\t: 0;\n\t\treturn respond({\n\t\t\tsummary: `Fetched ${count} log line${count === 1 ? '' : 's'} for service ${args.service_id}.`,\n\t\t\tdata: { logs: response.logs },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'get_service_logs_bulk',\n\tcategory: 'logs',\n\tdescription: [\n\t\t'Fetch runtime logs for multiple services in parallel and return one response keyed by service_id. Single round-trip from the MCP client perspective.',\n\t\t'',\n\t\t'When to use: investigating something that spans a project — \"what does every service in the auth-fleet say about the OOM at 14:00?\", \"did anything log an error in the last 5 minutes across these 4 services?\". Pair with list_services (filter by project/status) to get the service_ids list first.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_ids: list of service publicIds (svc_…). Hard cap 10 to bound work — for larger fleets, partition the call.',\n\t\t' - lines_per_service (optional): tail size per service (default 100, max 500). Smaller default than get_service_logs since you are fanning out.',\n\t\t' - since/until (optional): same shape as get_service_logs (ISO-8601 or \"-5m\", \"-1h\").',\n\t\t' - stream (optional): \"stdout\" | \"stderr\".',\n\t\t' - level (optional): structured-log level filter (trace/debug/info/warn/error/fatal).',\n\t\t' - search (optional): case-insensitive substring filter applied to every service.',\n\t\t' - count_only (optional): when true, return { results: { [service_id]: { count } } } — much cheaper for \"how many error lines per service in the last 5m\" surveys.',\n\t\t'',\n\t\t'Returns: { results: { [service_id]: { logs?, count?, error? } } } — per-service result, with `error` populated when one service failed (the rest still succeed).',\n\t\t'',\n\t\t'Example: get_service_logs_bulk({ service_ids: [\"svc_api\", \"svc_worker\"], level: \"error\", since: \"-15m\", count_only: true }) → { results: { svc_api: { count: 0 }, svc_worker: { count: 12 } } }.',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_ids: z\n\t\t\t.array(z.string())\n\t\t\t.min(1)\n\t\t\t.max(10)\n\t\t\t.describe('Service publicIds (1–10). Hard cap 10 to bound parallel work.'),\n\t\tlines_per_service: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.positive()\n\t\t\t.max(500)\n\t\t\t.optional()\n\t\t\t.describe('Tail size per service; default 100, hard cap 500.'),\n\t\tsince: z\n\t\t\t.string()\n\t\t\t.optional()\n\t\t\t.describe('ISO-8601 timestamp or relative offset (e.g. \"-5m\", \"-1h\").'),\n\t\tuntil: z.string().optional().describe('ISO-8601 timestamp or relative offset upper bound.'),\n\t\tstream: z.enum(['stdout', 'stderr']).optional().describe('Restrict to one stream.'),\n\t\tlevel: z\n\t\t\t.enum(['stdout', 'stderr', 'trace', 'debug', 'info', 'warn', 'error', 'fatal'])\n\t\t\t.optional()\n\t\t\t.describe('Structured log level filter (same as get_service_logs).'),\n\t\tsearch: z.string().max(100).optional().describe('Case-insensitive substring filter.'),\n\t\tcount_only: z\n\t\t\t.boolean()\n\t\t\t.optional()\n\t\t\t.describe('When true, return only counts per service — skips the log payload.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst linesPerService = args.lines_per_service ?? 100;\n\n\t\t// Build a single options object reused across all fan-outs so we\n\t\t// don't reconstruct identical params in a loop.\n\t\tconst baseOpts: {\n\t\t\tlines: number;\n\t\t\tsince?: string;\n\t\t\tuntil?: string;\n\t\t\tstream?: 'stdout' | 'stderr';\n\t\t\tlevel?: 'stdout' | 'stderr' | 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal';\n\t\t\tsearch?: string;\n\t\t\tcountOnly?: boolean;\n\t\t} = { lines: linesPerService };\n\t\tif (args.since) baseOpts.since = args.since;\n\t\tif (args.until) baseOpts.until = args.until;\n\t\tif (args.stream) baseOpts.stream = args.stream;\n\t\tif (args.level) baseOpts.level = args.level;\n\t\tif (args.search) baseOpts.search = args.search;\n\t\tif (args.count_only) baseOpts.countOnly = args.count_only;\n\n\t\t// Promise.allSettled so one failing service (e.g. deleted between\n\t\t// list_services and this call) doesn't void the rest.\n\t\tconst settled = await Promise.allSettled(\n\t\t\targs.service_ids.map(async (sid) => {\n\t\t\t\tconst response = await ctx.hoststack.services.getRuntimeLogs(teamId, sid, baseOpts);\n\t\t\t\treturn { sid, response };\n\t\t\t}),\n\t\t);\n\n\t\tconst results: Record<string, unknown> = {};\n\t\tlet okCount = 0;\n\t\tlet errorCount = 0;\n\t\tfor (let i = 0; i < settled.length; i += 1) {\n\t\t\tconst outcome = settled[i] as PromiseSettledResult<{\n\t\t\t\tsid: string;\n\t\t\t\tresponse: { logs?: unknown; count?: number };\n\t\t\t}>;\n\t\t\tconst sid = args.service_ids[i] as string;\n\t\t\tif (outcome.status === 'fulfilled') {\n\t\t\t\tconst { response } = outcome.value;\n\t\t\t\tif ('count' in response) {\n\t\t\t\t\tresults[sid] = { count: response.count };\n\t\t\t\t} else {\n\t\t\t\t\tresults[sid] = { logs: response.logs };\n\t\t\t\t}\n\t\t\t\tokCount += 1;\n\t\t\t} else {\n\t\t\t\tconst reason = outcome.reason as { message?: string } | string;\n\t\t\t\tresults[sid] = {\n\t\t\t\t\terror:\n\t\t\t\t\t\ttypeof reason === 'string' ? reason : (reason?.message ?? 'Unknown error'),\n\t\t\t\t};\n\t\t\t\terrorCount += 1;\n\t\t\t}\n\t\t}\n\n\t\tconst summary =\n\t\t\terrorCount === 0\n\t\t\t\t? `Fetched logs from ${okCount} service${okCount === 1 ? '' : 's'}.`\n\t\t\t\t: `Fetched logs from ${okCount} of ${args.service_ids.length} service${args.service_ids.length === 1 ? '' : 's'} (${errorCount} failed — see per-service .error field).`;\n\t\treturn respond({ summary, data: { results } });\n\t},\n});\n","/**\n * Quickstart templates surfaced by the `list_templates` tool.\n *\n * MIRROR of packages/shared/src/templates.ts (`APP_TEMPLATES`). It is copied,\n * not imported, for the same reason DEV_ENV_IMAGE is copied in\n * tools/services.ts: this package publishes to npm and depends only on\n * @hoststack.dev/sdk, so importing the private @hoststack/shared workspace\n * package would drag the monorepo into the published bundle. Update both\n * sides together — drift is now caught by test/shared-parity.test.ts, which\n * imports shared at DEV time only (a test file is never an entry point, so\n * nothing from shared reaches the bundle) and fails when the ids, their order\n * or any mirrored field disagree. \"Caught by review\" is what this said while\n * five templates went missing for a week, so it no longer says it.\n *\n * What is below is exactly what the dashboard's New Service wizard fills in,\n * so an agent that passes it to `create_service` gets a service configured\n * identically to a hand-created one.\n *\n * The rule for which fields come across is \"what a caller has to SEND\", not\n * \"what the template is\". `category` is not mirrored: it only drives the\n * wizard's tab grouping. `type` is, because it is a required `create_service`\n * argument. `dockerImage` and `port` are, because the API takes those two off\n * the wire even for a template — the wizard sends them alongside `templateId`\n * and so must an agent. Everything else an image template declares — its\n * volumes, scratch dirs, `runAsUser`, generated secrets, companion managed\n * database, health-check grace period — is resolved server-side from\n * `templateId` and cannot be sent at all, so copying it here would only\n * describe a payload no caller is allowed to write.\n *\n * The catalog used to carry three OTHER image templates — `docker-nginx`,\n * `docker-meilisearch`, `docker-minio` — and all three were removed after\n * being run under the real service sandbox flags: they exit on the read-only\n * rootfs before ever binding a port. See rule 5 in\n * packages/shared/src/templates.ts for the captured output. Do not re-add\n * them here.\n *\n * The five image templates that DO boot spent a week out of this mirror on\n * purpose, and the condition they were waiting on is worth keeping in mind\n * before anything here is loosened: `create_service` had no `template_id`\n * argument, so an agent shown `wordpress` could only reach it as\n * `docker_image: 'wordpress:php8.3-apache'` — an image with no volume,\n * running as root under a read-only rootfs, i.e. exactly the crash-loop rule\n * 7 was written to prevent, now looking officially supported. They are listed\n * here only because that argument exists; an entry with a `dockerImage` and\n * no `template_id` on the other end is a dead end, not a shortcut.\n */\n\nexport interface McpAppTemplate {\n\tid: string;\n\tname: string;\n\tdescription: string;\n\t/** Maps to the `type` argument of create_service. */\n\ttype: 'web_service' | 'worker' | 'cron_job' | 'static_site' | 'private_service';\n\t/**\n\t * Maps to `runtime` — the builder image the agent picks. Source-built\n\t * templates only; an image template has nothing to build.\n\t */\n\truntime?: string;\n\tinstallCommand?: string;\n\tbuildCommand?: string;\n\tstartCommand?: string;\n\tpublishPath?: string;\n\tcronSchedule?: string;\n\t/**\n\t * Prebuilt image, on the templates that ship one. Its presence is what makes\n\t * an entry a one-click app rather than a command set: pass it to\n\t * `create_service` TOGETHER with `template_id`, never on its own.\n\t */\n\tdockerImage?: string;\n\t/**\n\t * Maps to `port` — what the platform publishes and health-checks. Only ever\n\t * set on a `dockerImage` template, whose listen port is a property of the\n\t * image and cannot be argued with; a source-built app binds the injected\n\t * `$PORT` instead. Dropping it on the way to `create_service` gets a healthy\n\t * container probed on 3000 while the process listens somewhere else.\n\t */\n\tport?: number;\n}\n\nexport const MCP_APP_TEMPLATES: McpAppTemplate[] = [\n\t{\n\t\tid: 'node-express',\n\t\tname: 'Node.js Express',\n\t\tdescription: 'HTTP server with Express.js',\n\t\ttype: 'web_service',\n\t\truntime: 'node',\n\t\tinstallCommand: 'npm install',\n\t\tstartCommand: 'node index.js',\n\t},\n\t{\n\t\tid: 'python-fastapi',\n\t\tname: 'Python FastAPI',\n\t\tdescription: 'Modern Python API framework',\n\t\ttype: 'web_service',\n\t\truntime: 'python',\n\t\tinstallCommand: 'pip install -r requirements.txt',\n\t\tstartCommand: 'uvicorn main:app --host 0.0.0.0 --port $PORT',\n\t},\n\t{\n\t\tid: 'go-api',\n\t\tname: 'Go HTTP Server',\n\t\tdescription: 'Lightweight Go web service',\n\t\ttype: 'web_service',\n\t\truntime: 'go',\n\t\tinstallCommand: 'go mod download',\n\t\tbuildCommand: 'go build -o server .',\n\t\tstartCommand: './server',\n\t},\n\t{\n\t\tid: 'bun-hono',\n\t\tname: 'Bun + Hono',\n\t\tdescription: 'Fast TypeScript API with Bun runtime',\n\t\ttype: 'web_service',\n\t\truntime: 'bun',\n\t\tinstallCommand: 'bun install',\n\t\tstartCommand: 'bun run src/index.ts',\n\t},\n\t{\n\t\tid: 'nextjs-ssr',\n\t\tname: 'Next.js',\n\t\tdescription: 'Server-rendered Next.js started with next start',\n\t\ttype: 'web_service',\n\t\truntime: 'node',\n\t\tinstallCommand: 'npm install',\n\t\tbuildCommand: 'npm run build',\n\t\tstartCommand: 'npm run start',\n\t},\n\t{\n\t\tid: 'react-router-ssr',\n\t\tname: 'React Router / Remix',\n\t\tdescription: 'Server build served by react-router-serve or remix-serve',\n\t\ttype: 'web_service',\n\t\truntime: 'node',\n\t\tinstallCommand: 'npm install',\n\t\tbuildCommand: 'npm run build',\n\t\tstartCommand: 'npm run start',\n\t},\n\t{\n\t\tid: 'sveltekit-node',\n\t\tname: 'SvelteKit',\n\t\tdescription: 'SvelteKit server build produced by adapter-node',\n\t\ttype: 'web_service',\n\t\truntime: 'node',\n\t\tinstallCommand: 'npm install',\n\t\tbuildCommand: 'npm run build',\n\t\tstartCommand: 'node build',\n\t},\n\t{\n\t\tid: 'nuxt',\n\t\tname: 'Nuxt',\n\t\tdescription: 'Nuxt server build running on the Nitro output',\n\t\ttype: 'web_service',\n\t\truntime: 'node',\n\t\tinstallCommand: 'npm install',\n\t\tbuildCommand: 'npm run build',\n\t\tstartCommand: 'node .output/server/index.mjs',\n\t},\n\t{\n\t\tid: 'django',\n\t\tname: 'Django',\n\t\tdescription: 'Django project served by Gunicorn',\n\t\ttype: 'web_service',\n\t\truntime: 'python',\n\t\tinstallCommand: 'pip install -r requirements.txt',\n\t\tbuildCommand: 'python manage.py collectstatic --noinput || true',\n\t\tstartCommand:\n\t\t\t'gunicorn --bind 0.0.0.0:$PORT --workers 2 $(ls */wsgi.py | head -n1 | cut -d/ -f1).wsgi:application',\n\t},\n\t{\n\t\tid: 'flask',\n\t\tname: 'Flask',\n\t\tdescription: 'Flask app served by Gunicorn',\n\t\ttype: 'web_service',\n\t\truntime: 'python',\n\t\tinstallCommand: 'pip install -r requirements.txt',\n\t\tstartCommand: 'gunicorn --bind 0.0.0.0:$PORT --workers 2 app:app',\n\t},\n\t{\n\t\tid: 'spring-boot',\n\t\tname: 'Spring Boot',\n\t\tdescription: 'Spring Boot fat JAR built with Maven or Gradle',\n\t\ttype: 'web_service',\n\t\truntime: 'java',\n\t\t// No install/build on purpose: the agent inspects the repo and picks\n\t\t// Maven vs Gradle with the matching toolchain image. Sending either one\n\t\t// here would break the other half of the split.\n\t\tstartCommand: 'java -jar /app/app.jar --server.port=$PORT',\n\t},\n\t{\n\t\tid: 'static-react',\n\t\tname: 'React SPA',\n\t\tdescription: 'Single-page React application',\n\t\ttype: 'static_site',\n\t\truntime: 'node',\n\t\tinstallCommand: 'npm install',\n\t\tbuildCommand: 'npm run build',\n\t\tpublishPath: './dist',\n\t},\n\t{\n\t\tid: 'static-nextjs',\n\t\tname: 'Next.js Static',\n\t\tdescription: 'Next.js with static export',\n\t\ttype: 'static_site',\n\t\truntime: 'node',\n\t\tinstallCommand: 'npm install',\n\t\tbuildCommand: 'npm run build',\n\t\tpublishPath: './out',\n\t},\n\t{\n\t\tid: 'astro',\n\t\tname: 'Astro',\n\t\tdescription: \"Astro site built to Astro's default static output\",\n\t\ttype: 'static_site',\n\t\truntime: 'node',\n\t\tinstallCommand: 'npm install',\n\t\tbuildCommand: 'npm run build',\n\t\tpublishPath: './dist',\n\t},\n\t{\n\t\tid: 'worker-bullmq',\n\t\tname: 'BullMQ Worker',\n\t\tdescription: 'Background job processor',\n\t\ttype: 'worker',\n\t\truntime: 'node',\n\t\tinstallCommand: 'npm install',\n\t\tstartCommand: 'node worker.js',\n\t},\n\t{\n\t\tid: 'uptime-kuma',\n\t\tname: 'Uptime Kuma',\n\t\tdescription: 'Self-hosted uptime monitoring and status pages, with its own SQLite store',\n\t\ttype: 'web_service',\n\t\tdockerImage: 'louislam/uptime-kuma:1',\n\t\tport: 3000,\n\t},\n\t{\n\t\tid: 'vaultwarden',\n\t\tname: 'Vaultwarden',\n\t\tdescription: 'Self-hosted Bitwarden-compatible password manager (unofficial server)',\n\t\ttype: 'web_service',\n\t\tdockerImage: 'vaultwarden/server:1.32.7-alpine',\n\t\tport: 3000,\n\t},\n\t{\n\t\tid: 'n8n',\n\t\tname: 'n8n',\n\t\tdescription: 'Self-hosted workflow automation — visual editor, 400+ integrations',\n\t\ttype: 'web_service',\n\t\tdockerImage: 'n8nio/n8n:1',\n\t\tport: 5678,\n\t},\n\t{\n\t\tid: 'wordpress',\n\t\tname: 'WordPress',\n\t\tdescription: 'PHP 8.3 + Apache, with a managed MySQL database (billed separately)',\n\t\ttype: 'web_service',\n\t\tdockerImage: 'wordpress:php8.3-apache',\n\t\tport: 80,\n\t},\n\t{\n\t\tid: 'ghost',\n\t\tname: 'Ghost',\n\t\tdescription: 'Blogs and newsletters, with a managed MySQL database (billed separately)',\n\t\ttype: 'web_service',\n\t\tdockerImage: 'ghost:5-alpine',\n\t\tport: 2368,\n\t},\n\t{\n\t\tid: 'cron-cleanup',\n\t\tname: 'Cleanup Cron',\n\t\tdescription: 'Daily maintenance task',\n\t\ttype: 'cron_job',\n\t\truntime: 'node',\n\t\tinstallCommand: 'npm install',\n\t\tstartCommand: 'node cleanup.js',\n\t\tcronSchedule: '0 3 * * *',\n\t},\n];\n","import { z } from 'zod';\n\nimport { respond } from '../lib/respond.js';\nimport { shape } from '../lib/shape.js';\nimport { defineTool } from '../registry.js';\n\ninterface CheckResponse {\n\tcheck: Record<string, unknown> | null;\n}\n\nconst STATUS_MEANING = [\n\t'Status vocabulary: `up` answering as expected · `down` failed the threshold and an alert is open · `unknown` not probed yet · `unresolvable` the service has no active domain to request · `paused` the service is not meant to be answering (suspended, mid-deploy, never deployed) OR it sleeps when idle and probing it would keep it awake.',\n].join('\\n');\n\ndefineTool({\n\tname: 'get_uptime_check',\n\tcategory: 'uptime',\n\tdescription: [\n\t\t\"Read a service's uptime check — HostStack requesting the service's public URL on a schedule and alerting when it stops answering.\",\n\t\t'',\n\t\t'When to use: \"is this service actually reachable\", or before changing a check to see what it currently does. This is NOT the deploy-time health check: that one watches the container from inside the host and stops mattering once a deploy is live. This one runs from the control plane against the public URL, so it also catches DNS, TLS, edge and routing failures — and a service that accepts the connection and then answers nothing.',\n\t\t'',\n\t\tSTATUS_MEANING,\n\t\t'',\n\t\t'Inputs: serviceId (required).',\n\t\t'',\n\t\t'Returns: { check } or { check: null } when none is configured. The check carries path, method, expectedStatus, intervalSeconds, failureThreshold, status, consecutiveFailures, lastCheckedAt, lastStatusCode, lastLatencyMs, lastError, lastChangedAt (the \"down since\" timestamp), and lastTargetUrl.',\n\t\t'',\n\t\t\"WHICH HOSTNAME IS PROBED: `lastTargetUrl` is the URL the last probe actually dialled — read it here rather than inferring it. A check names a PATH and the platform picks the host: the service's primary domain, or the OLDEST domain when no primary is nominated, which on a renamed host is usually the retired alias. So a service answering on two names that behave differently (canonical serves, alias 301s) can have a check that asserts the wrong status code without anything being wrong with the service. If lastTargetUrl is not the name you meant to watch, nominate the right one with update_domain({ domain_id, isPrimary: true }) — set_uptime_check takes a path and never a host.\",\n\t\t'',\n\t\t\"Example: get_uptime_check({ serviceId: 48 }) → { check: { path: '/healthz', status: 'down', consecutiveFailures: 5, lastError: 'No response within 10000ms', lastChangedAt: '2026-08-21T04:12:00Z', lastTargetUrl: 'https://grundfast.dk/healthz' } }.\",\n\t].join('\\n'),\n\tinput: { serviceId: z.number().int().positive() },\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.api.get<CheckResponse>(\n\t\t\t`/api/services/${teamId}/${args.serviceId}/uptime-check`,\n\t\t);\n\t\tif (response.check === null) {\n\t\t\treturn respond({\n\t\t\t\tsummary: 'No uptime check on this service — nothing is watching its public URL.',\n\t\t\t\tdata: { check: null },\n\t\t\t});\n\t\t}\n\t\tconst check = shape(response.check) as Record<string, unknown>;\n\t\treturn respond({\n\t\t\tsummary: `Uptime check is ${String(check['status'])}${check['lastError'] ? ` — ${String(check['lastError'])}` : ''}.`,\n\t\t\tdata: { check },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'set_uptime_check',\n\tcategory: 'uptime',\n\tdescription: [\n\t\t\"Create or update a service's uptime check.\",\n\t\t'',\n\t\t'When to use: turning monitoring on for a service that has just gone live, or adjusting a check that is too noisy (raise failureThreshold) or too slow to notice (lower intervalSeconds).',\n\t\t'',\n\t\t'Only service types with a public URL can be checked (web services and static sites). A worker, cron job or private service is refused with 400 — a check on one could only ever report \"no domain to check\", which reads as a broken feature rather than an inapplicable one.',\n\t\t'',\n\t\t'For a site HostStack does NOT host, there is no serviceId to pass: use set_site_uptime_check with its analytics siteId instead. That path needs the domain proven first (verify_site_domain), because the host it probes comes from the site row rather than from a domain the platform already vouches for.',\n\t\t'',\n\t\t'IMPORTANT: changing the shape of a check RESETS its accumulated state (status, consecutive failures, open alert). A check whose path or expected status just changed has not observed the new check failing, so carrying failures forward would alert about a condition that was never measured. A write that resolves to the shape already stored changes nothing and leaves that state alone — but the inputs are defaults, not a partial edit, so omitting intervalSeconds on a check set to 30 resolves to 60 and IS a change. Read a check with get_uptime_check; never re-save one in order to look at it.',\n\t\t'',\n\t\t\"`path` is a path, not a URL: the host comes from the service's primary domain at probe time, so moving the service to a new domain moves the check with it. On a service with SEVERAL domains that is a choice, and this tool does not make it — nominate the hostname to watch with update_domain({ domain_id, isPrimary: true }), or the platform falls back to the OLDEST domain, which after a rename is usually the alias that redirects. Check which one is live with get_uptime_check's lastTargetUrl before trusting an expectedStatus. When ONE named hostname has to be asserted whatever the platform picks — a canonical host and a 301 alias cannot share an expectedStatus — give that hostname its own analytics site and check it with set_site_uptime_check.\",\n\t\t'',\n\t\t\"Method is GET or HEAD only. A probe fires unattended every interval forever, so it has to be safe to repeat — a check that could POST would be a scheduled writer against the team's own API.\",\n\t\t'',\n\t\t'Redirects are NOT followed: a 301 is an answer, and following one can walk the probe onto a marketing site and report a dead service as healthy. If a redirect is expected, set expectedStatus to it.',\n\t\t'',\n\t\t'Inputs (all optional except serviceId): enabled, path (default \"/\"), method (GET|HEAD), expectedStatus (default 200), timeoutMs (1000–60000, default 10000 — this is what catches a hung server), intervalSeconds (30–3600, default 60), failureThreshold (1–10, default 3 — one failed request is usually a restart, not an outage).',\n\t\t'',\n\t\t'Returns: { check } with the saved check.',\n\t\t'',\n\t\t\"Example: set_uptime_check({ serviceId: 48, path: '/healthz', intervalSeconds: 60, failureThreshold: 3 }) → { check: { status: 'unknown', … } }.\",\n\t].join('\\n'),\n\tinput: {\n\t\tserviceId: z.number().int().positive(),\n\t\tenabled: z.boolean().optional(),\n\t\tpath: z.string().max(500).optional().describe('Must start with /. Default \"/\".'),\n\t\tmethod: z.enum(['GET', 'HEAD']).optional(),\n\t\texpectedStatus: z.number().int().min(100).max(599).optional(),\n\t\ttimeoutMs: z.number().int().min(1000).max(60_000).optional(),\n\t\tintervalSeconds: z.number().int().min(30).max(3600).optional(),\n\t\tfailureThreshold: z.number().int().min(1).max(10).optional(),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst { serviceId, ...body } = args;\n\t\tconst response = await ctx.api.put<{ check: Record<string, unknown> }>(\n\t\t\t`/api/services/${teamId}/${serviceId}/uptime-check`,\n\t\t\tbody,\n\t\t);\n\t\tconst check = shape(response.check) as Record<string, unknown>;\n\t\t// A null lastCheckedAt is what BOTH a never-probed check and a just-reset\n\t\t// one look like, so say which: non-null means the write changed nothing and\n\t\t// the accumulated state survived.\n\t\tconst probed = check['lastCheckedAt'] !== null && check['lastCheckedAt'] !== undefined;\n\t\treturn respond({\n\t\t\tsummary: probed\n\t\t\t\t? `Uptime check saved — the shape was already stored, so nothing was reset: still ${String(check['status'])}, last probed ${String(check['lastCheckedAt'])}.`\n\t\t\t\t: 'Uptime check saved, and not probed yet — \"unknown\" with no lastCheckedAt is the state of a check that has not run once, not a broken one. It reports within a minute or two: read it with get_uptime_check, never by saving it again.',\n\t\t\tdata: { check },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'delete_uptime_check',\n\tcategory: 'uptime',\n\tdescription: [\n\t\t'Stop checking a service. Any open \"not answering\" alert is left as it stands rather than being silently resolved — removing the monitor is not evidence the service came back.',\n\t\t'',\n\t\t'When to use: the service is being retired, or monitoring has moved somewhere else. To pause a check without losing its configuration, call set_uptime_check with enabled=false instead.',\n\t\t'',\n\t\t'Inputs: serviceId (required).',\n\t\t'',\n\t\t'Returns: { success: true }.',\n\t\t'',\n\t\t'Example: delete_uptime_check({ serviceId: 48 }) → { success: true }.',\n\t].join('\\n'),\n\tinput: { serviceId: z.number().int().positive() },\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tawait ctx.api.delete(`/api/services/${teamId}/${args.serviceId}/uptime-check`);\n\t\treturn respond({ summary: 'Uptime check removed. Nothing is watching this service now.' });\n\t},\n});\n","import { z } from 'zod';\n\nimport { defineTool } from '../registry.js';\nimport { respond } from '../lib/respond.js';\nimport { shape, shapeList, shapeVolume } from '../lib/shape.js';\n\n/**\n * Platform minimum for a persistent disk, in GB.\n *\n * Inlined rather than imported: this package publishes to npm standalone and\n * must not depend on the `@hoststack/shared` workspace package. Kept in sync\n * with `HETZNER_BLOCK_VOLUME_MIN_GB` in\n * packages/shared/src/constants/pricing.ts — the API rejects anything below\n * it, so a lower bound here only converts a clean rejection into a failed\n * deploy.\n */\nconst MIN_VOLUME_SIZE_GB = 10;\n\ndefineTool({\n\tname: 'list_volumes',\n\tcategory: 'volumes',\n\tdescription: [\n\t\t'List persistent disks attached to a service. Volumes mount writable storage into the container at the path you choose, surviving redeploys and restarts.',\n\t\t'',\n\t\t'When to use: auditing what disks are attached, checking sizes before adding more, or confirming a volume took effect after creation. Note: storage is metered for billing — use this to spot oversized disks.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t'',\n\t\t'Returns: { items: Volume[] } — each entry has id, publicId, name, mountPath, sizeGb, status (pending|active|deleting), createdAt, updatedAt.',\n\t\t'',\n\t\t'Example: list_volumes({ service_id: \"svc_abc\" }) → { items: [{ name: \"data\", mountPath: \"/var/data\", sizeGb: 10, status: \"active\" }] }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId (e.g. svc_abc123).'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.volumes.list(teamId, args.service_id);\n\t\tconst data = shapeList(response, 'volumes', shapeVolume);\n\t\tconst summary =\n\t\t\tdata.items.length === 0\n\t\t\t\t? `No volumes attached to service ${args.service_id}.`\n\t\t\t\t: `Found ${data.items.length} volume${data.items.length === 1 ? '' : 's'} on service ${args.service_id}.`;\n\t\treturn respond({ summary, data });\n\t},\n});\n\ndefineTool({\n\tname: 'create_volume',\n\tcategory: 'volumes',\n\tdescription: [\n\t\t'Attach a new persistent disk to a service. The disk gets provisioned on the host and bind-mounted into the container at `mount_path` on the next deploy. Use this when an app needs writable persistent storage — uploads, caches, SQLite databases, anything that must survive container restarts.',\n\t\t'',\n\t\t'When to use: the user mentions storing files, uploads, or any data that needs to outlive a container restart. For purely ephemeral scratch space, point the app at /tmp instead (already tmpfs-mounted, free, no provisioning needed) — see HOSTSTACK_TMP_DIR.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service to attach to.',\n\t\t' - name: lowercase alphanumeric + hyphens, ≤64 chars (used as the docker volume identifier — change with care once data is written).',\n\t\t' - mount_path: in-container absolute path (e.g. \"/var/data\").',\n\t\t' - size_gb: optional, 10–100, default 10. 10 GB is the platform minimum (the underlying block storage cannot provision anything smaller) — a smaller value is rejected here rather than failing the next deploy. Metered for billing.',\n\t\t'',\n\t\t'Returns: { volume: Volume } — the created record. A durable volume comes back `status: \"pending\"`; the disk is created and attached on the next deploy, which flips it to \"active\". A deploy is required before the mount exists.',\n\t\t'',\n\t\t'Example: create_volume({ service_id: \"svc_abc\", name: \"data\", mount_path: \"/var/data\", size_gb: 10 }) → { volume: { name: \"data\", mountPath: \"/var/data\", sizeGb: 10, status: \"pending\" } }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t\tname: z\n\t\t\t.string()\n\t\t\t.min(1)\n\t\t\t.max(64)\n\t\t\t.regex(/^[a-z0-9-]+$/)\n\t\t\t.describe('Volume name (lowercase alphanumeric + hyphens).'),\n\t\tmount_path: z\n\t\t\t.string()\n\t\t\t.startsWith('/')\n\t\t\t.max(500)\n\t\t\t.describe('In-container mount path (absolute).'),\n\t\t// 10 GB is the real floor: the block-storage backend rejects anything\n\t\t// smaller. Advertising 1 GB here (and defaulting to it) meant taking the\n\t\t// defaults produced a volume that provisioned with `Hetzner API error:\n\t\t// 422` on the NEXT deploy, with nothing tying the failure back to the\n\t\t// size. Reject it at the call instead.\n\t\tsize_gb: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(MIN_VOLUME_SIZE_GB)\n\t\t\t.max(100)\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t`Disk size in GB (minimum ${MIN_VOLUME_SIZE_GB}, default ${MIN_VOLUME_SIZE_GB}, max 100 via MCP).`,\n\t\t\t),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst input: { name: string; mountPath: string; sizeGb?: number } = {\n\t\t\tname: args.name,\n\t\t\tmountPath: args.mount_path,\n\t\t};\n\t\tif (args.size_gb !== undefined) input.sizeGb = args.size_gb;\n\t\tconst response = await ctx.hoststack.volumes.create(teamId, args.service_id, input);\n\t\tconst data = { volume: shape(response.volume) };\n\t\treturn respond({\n\t\t\tsummary: `Attached volume \"${args.name}\" (${response.volume.sizeGb}GB) at ${args.mount_path} on service ${args.service_id}. Status is \"${response.volume.status}\" — deploy the service to provision and mount it.`,\n\t\t\tdata,\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'update_volume',\n\tcategory: 'volumes',\n\tdescription: [\n\t\t\"Update a volume's mountPath or sizeGb. Resizes take effect on the next deploy; shrinking is rejected at the storage layer (filesystems can't safely shrink under live data).\",\n\t\t'',\n\t\t'When to use: the user wants to grow the disk for an existing volume, or move where it mounts inside the container.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t' - volume_id: publicId of the volume to update.',\n\t\t' - mount_path (optional): new in-container mount path.',\n\t\t' - size_gb (optional): new size in GB (must be ≥ current).',\n\t\t' - backup_enabled (optional): whether this volume is backed up. Turning it off stops future backups; it does not delete the ones already taken.',\n\t\t'',\n\t\t'Returns: { volume: Volume } — the updated record.',\n\t\t'',\n\t\t'Example: update_volume({ service_id: \"svc_abc\", volume_id: \"vol_xyz\", size_gb: 20 }) → { volume: { sizeGb: 20, … } }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t\tvolume_id: z.string().describe('Volume publicId (e.g. vol_…).'),\n\t\tmount_path: z.string().startsWith('/').max(500).optional().describe('New mount path.'),\n\t\tsize_gb: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(MIN_VOLUME_SIZE_GB)\n\t\t\t.max(100)\n\t\t\t.optional()\n\t\t\t.describe(`New size in GB (minimum ${MIN_VOLUME_SIZE_GB}, grow-only).`),\n\t\tbackup_enabled: z\n\t\t\t.boolean()\n\t\t\t.optional()\n\t\t\t.describe('Whether to keep backing this volume up. Past backups are kept either way.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst input: { mountPath?: string; sizeGb?: number; backupEnabled?: boolean } = {};\n\t\tif (args.mount_path !== undefined) input.mountPath = args.mount_path;\n\t\tif (args.size_gb !== undefined) input.sizeGb = args.size_gb;\n\t\tif (args.backup_enabled !== undefined) input.backupEnabled = args.backup_enabled;\n\t\tif (Object.keys(input).length === 0) {\n\t\t\treturn respond({ summary: 'No fields to update.', data: {} });\n\t\t}\n\t\tconst response = await ctx.hoststack.volumes.update(\n\t\t\tteamId,\n\t\t\targs.service_id,\n\t\t\targs.volume_id,\n\t\t\tinput,\n\t\t);\n\t\tconst data = { volume: shape(response.volume) };\n\t\tconst fields = Object.keys(input).join(', ');\n\t\treturn respond({ summary: `Updated ${fields} on volume ${args.volume_id}.`, data });\n\t},\n});\n\ndefineTool({\n\tname: 'delete_volume',\n\tcategory: 'volumes',\n\tdescription: [\n\t\t'Detach and deprovision a volume. The underlying disk is destroyed — back up any data first. Async: marks the row deleting, the host agent finalises removal once it acks. A periodic reaper retries if the agent was offline at delete time.',\n\t\t'',\n\t\t\"When to use: the user explicitly says to remove a volume, or you're cleaning up after retiring a service. Confirm before calling on production data.\",\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t' - volume_id: publicId of the volume.',\n\t\t'',\n\t\t'Returns: { ok: true }.',\n\t\t'',\n\t\t'Example: delete_volume({ service_id: \"svc_abc\", volume_id: \"vol_xyz\" }) → { ok: true }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t\tvolume_id: z.string().describe('Volume publicId.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tawait ctx.hoststack.volumes.delete(teamId, args.service_id, args.volume_id);\n\t\treturn respond({\n\t\t\tsummary: `Detached volume ${args.volume_id} from service ${args.service_id} (deprovisioning).`,\n\t\t\tdata: { ok: true },\n\t\t});\n\t},\n});\n"],"mappings":"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","names":["z","z","z","z","z","z","z","z","z","z","z","z","z","zones","z","z","z","z","z","response","data","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z"]}
|
|
1
|
+
{"version":3,"sources":["../src/server-factory.ts","../src/version.ts","../src/api-client.ts","../src/prompts/registry.ts","../src/resources/registry.ts","../src/registry.ts","../src/prompts/deploy-prompts.ts","../src/lib/shape.ts","../src/resources/hoststack-resources.ts","../src/tools/activity-log.ts","../src/lib/respond.ts","../src/tools/alerts.ts","../src/tools/cron.ts","../src/tools/databases.ts","../src/tools/machines.ts","../src/tools/deploys.ts","../src/tools/dns-records.ts","../src/tools/domains.ts","../src/tools/env-vars.ts","../src/tools/environments.ts","../src/tools/analytics.ts","../src/lib/format.ts","../src/tools/errors.ts","../src/tools/github.ts","../src/tools/issue-reports.ts","../src/tools/meta.ts","../src/tools/notifications.ts","../src/tools/projects.ts","../src/tools/dev-tasks.ts","../src/tools/resource-links.ts","../src/tools/services.ts","../src/lib/app-templates.ts","../src/tools/uptime.ts","../src/tools/volumes.ts"],"sourcesContent":["import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { HostStack } from '@hoststack.dev/sdk';\n\nimport { ApiClient } from './api-client.js';\nimport { attachPrompts } from './prompts/registry.js';\nimport { attachResources } from './resources/registry.js';\nimport { attachTools, type ToolCallSink, type ToolContext } from './registry.js';\nimport { MCP_VERSION } from './version.js';\n\nexport type { ToolCallEvent, ToolCallSink } from './registry.js';\n\n// Importing each tool/prompt/resource module runs its top-level define*()\n// calls and populates the shared registries. ESM caches modules, so each\n// registry is built exactly once per process.\nimport './prompts/deploy-prompts.js';\nimport './resources/hoststack-resources.js';\nimport './tools/activity-log.js';\nimport './tools/alerts.js';\nimport './tools/cron.js';\nimport './tools/databases.js';\nimport './tools/deploys.js';\nimport './tools/dns-records.js';\nimport './tools/domains.js';\nimport './tools/env-vars.js';\nimport './tools/environments.js';\nimport './tools/analytics.js';\nimport './tools/errors.js';\nimport './tools/github.js';\nimport './tools/issue-reports.js';\nimport './tools/machines.js';\nimport './tools/meta.js';\nimport './tools/notifications.js';\nimport './tools/projects.js';\nimport './tools/dev-tasks.js';\nimport './tools/resource-links.js';\nimport './tools/services.js';\nimport './tools/uptime.js';\nimport './tools/volumes.js';\n\nexport interface CreateServerOptions {\n\t/** HostStack API key (hs_live_… or hs_test_…). Required. */\n\tapiKey: string;\n\t/** Base URL for the HostStack API. Defaults to https://hoststack.dev */\n\tbaseUrl?: string;\n\t/**\n\t * Server identity advertised in the MCP `initialize` response. Defaults\n\t * to `{ name: \"hoststack\", version: PACKAGE_VERSION }`.\n\t */\n\tserverInfo?: { name: string; version: string };\n\t/**\n\t * Optional telemetry sink invoked once per tool call (success or failure).\n\t * Runs on the microtask queue; sink errors are swallowed so tool execution\n\t * never blocks on telemetry.\n\t */\n\tonToolCall?: ToolCallSink;\n}\n\nexport const PACKAGE_NAME = 'hoststack';\n// Injected from package.json at build time (see tsup.config.ts `define`) so the\n// MCP `initialize` handshake advertises the real published version.\nexport const PACKAGE_VERSION = MCP_VERSION;\n\ninterface MeResponse {\n\tuser: { id: number };\n\tteam?: { id: number };\n}\n\n/**\n * Build a fully-wired MCP server. Used by both the stdio CLI entry-point and\n * the HTTP transport mounted by the API server. A new server (and its\n * underlying SDK + ApiClient) is created per call, so the HTTP transport can\n * scope each connection to a single API key without cross-contamination.\n */\nexport function createMcpServer(options: CreateServerOptions): McpServer {\n\tconst baseUrl = (options.baseUrl ?? 'https://hoststack.dev').replace(/\\/$/, '');\n\tconst hoststack = new HostStack({ apiKey: options.apiKey, baseUrl });\n\tconst api = new ApiClient(options.apiKey, baseUrl);\n\n\tconst server = new McpServer({\n\t\tname: options.serverInfo?.name ?? PACKAGE_NAME,\n\t\tversion: options.serverInfo?.version ?? PACKAGE_VERSION,\n\t});\n\n\t// Cache the team lookup. The first tool call pays one /auth/me round-trip;\n\t// every subsequent call reuses the cached promise. We store the in-flight\n\t// promise (not the resolved value) so concurrent first-calls don't fan out\n\t// into duplicate /auth/me requests.\n\tlet teamIdPromise: Promise<number> | null = null;\n\tconst resolveTeamId = (): Promise<number> => {\n\t\tif (!teamIdPromise) {\n\t\t\tteamIdPromise = api.get<MeResponse>('/api/auth/me').then((me) => {\n\t\t\t\tif (!me.team?.id) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t'No team bound to API key. Generate a team-scoped key in the dashboard.',\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn me.team.id;\n\t\t\t});\n\t\t\t// If the lookup fails, clear the cache so a retry can succeed once\n\t\t\t// the user fixes their key. Without this, every subsequent tool\n\t\t\t// call would re-throw the same cached rejection.\n\t\t\tteamIdPromise.catch(() => {\n\t\t\t\tteamIdPromise = null;\n\t\t\t});\n\t\t}\n\t\treturn teamIdPromise;\n\t};\n\n\tconst ctx: ToolContext = { hoststack, api, resolveTeamId };\n\tattachTools(server, ctx, options.onToolCall);\n\tattachPrompts(server, ctx);\n\tattachResources(server, ctx);\n\n\treturn server;\n}\n","/**\n * Package version, injected at build time by tsup's `define`\n * (`__MCP_VERSION__` ← package.json `version`). The `declare` keeps the source\n * typecheckable without a build; the `?? '0.0.0-dev'` fallback keeps the\n * un-bundled source (tests, ts-node) working when the define is absent.\n */\ndeclare const __MCP_VERSION__: string | undefined;\n\nexport const MCP_VERSION: string =\n\ttypeof __MCP_VERSION__ === 'string' ? __MCP_VERSION__ : '0.0.0-dev';\n\n/** User-Agent sent on every MCP-originated request, e.g. `hoststack-mcp/0.13.1`. */\nexport const USER_AGENT = `hoststack-mcp/${MCP_VERSION}`;\n","import { USER_AGENT } from './version.js';\n\n/**\n * Lightweight API client for HostStack endpoints not yet covered by the SDK\n * (logs snapshots, activity log, runtime metrics, /me lookup). Uses the same\n * auth and base URL as the SDK instance.\n */\nexport class ApiClient {\n\tconstructor(\n\t\tprivate readonly apiKey: string,\n\t\tprivate readonly baseUrl: string,\n\t) {}\n\n\tprivate get headers(): Record<string, string> {\n\t\treturn {\n\t\t\tAuthorization: `Bearer ${this.apiKey}`,\n\t\t\t'Content-Type': 'application/json',\n\t\t\t'User-Agent': USER_AGENT,\n\t\t};\n\t}\n\n\tasync get<T>(path: string, params?: Record<string, string | number | undefined>): Promise<T> {\n\t\tconst url = new URL(`${this.baseUrl}${path}`);\n\t\tif (params) {\n\t\t\tfor (const [key, value] of Object.entries(params)) {\n\t\t\t\tif (value !== undefined) url.searchParams.set(key, String(value));\n\t\t\t}\n\t\t}\n\t\tconst res = await fetch(url.toString(), { headers: this.headers });\n\t\treturn this.handle<T>(res);\n\t}\n\n\tasync post<T>(path: string, body?: unknown): Promise<T> {\n\t\tconst init: RequestInit = {\n\t\t\tmethod: 'POST',\n\t\t\theaders: this.headers,\n\t\t};\n\t\tif (body !== undefined) init.body = JSON.stringify(body);\n\t\tconst res = await fetch(`${this.baseUrl}${path}`, init);\n\t\treturn this.handle<T>(res);\n\t}\n\n\tasync patch<T>(path: string, body: unknown): Promise<T> {\n\t\tconst res = await fetch(`${this.baseUrl}${path}`, {\n\t\t\tmethod: 'PATCH',\n\t\t\theaders: this.headers,\n\t\t\tbody: JSON.stringify(body),\n\t\t});\n\t\treturn this.handle<T>(res);\n\t}\n\n\tasync put<T>(path: string, body: unknown): Promise<T> {\n\t\tconst res = await fetch(`${this.baseUrl}${path}`, {\n\t\t\tmethod: 'PUT',\n\t\t\theaders: this.headers,\n\t\t\tbody: JSON.stringify(body),\n\t\t});\n\t\treturn this.handle<T>(res);\n\t}\n\n\tasync delete<T>(path: string): Promise<T> {\n\t\tconst res = await fetch(`${this.baseUrl}${path}`, {\n\t\t\tmethod: 'DELETE',\n\t\t\theaders: this.headers,\n\t\t});\n\t\treturn this.handle<T>(res);\n\t}\n\n\tprivate async handle<T>(res: Response): Promise<T> {\n\t\tif (!res.ok) {\n\t\t\tconst body = (await res.json().catch(() => ({ error: res.statusText }))) as unknown;\n\t\t\tthrow new Error(formatApiError(body, res.status));\n\t\t}\n\t\tif (res.status === 204) {\n\t\t\treturn undefined as T;\n\t\t}\n\t\treturn res.json() as Promise<T>;\n\t}\n}\n\n/**\n * Turn an API error body into something an operator can act on.\n *\n * The service layer returns `{ error: \"<string>\" }`, but a request rejected by\n * `zValidator` returns `{ success: false, error: <ZodError> }` — an OBJECT.\n * The old code typed `error` as `string | undefined` and passed it straight to\n * `new Error(...)`, which stringified the ZodError to the literal text\n * `[object Object]`. Every schema rejection therefore surfaced with no\n * indication of which field was wrong or why: `create_volume({ size_gb: 2 })`\n * reported `[object Object]` when the real answer was \"size_gb must be at\n * least 10\". Flatten the issues into `field: message` instead.\n */\nexport function formatApiError(body: unknown, status: number): string {\n\tconst fallback = `API error: ${status}`;\n\tif (!body || typeof body !== 'object') return fallback;\n\tconst err = (body as { error?: unknown }).error;\n\n\t// A string error is the service layer's `{ error: \"...\" }` shape. Blank or\n\t// whitespace-only carries no information, so fall through to the status\n\t// rather than quoting an empty string back at the caller.\n\tif (typeof err === 'string') return err.trim() ? err : fallback;\n\n\t// Zod rejection: `issues` is either on the error object or (older shapes)\n\t// the error IS the issue array.\n\tconst issues = Array.isArray(err)\n\t\t? err\n\t\t: err && typeof err === 'object' && Array.isArray((err as { issues?: unknown }).issues)\n\t\t\t? (err as { issues: unknown[] }).issues\n\t\t\t: null;\n\n\tif (issues && issues.length > 0) {\n\t\tconst rendered = issues\n\t\t\t.map((issue) => {\n\t\t\t\tif (!issue || typeof issue !== 'object') return String(issue);\n\t\t\t\tconst { path, message } = issue as { path?: unknown; message?: unknown };\n\t\t\t\tconst field = Array.isArray(path) && path.length > 0 ? path.join('.') : null;\n\t\t\t\tconst text = typeof message === 'string' ? message : 'invalid value';\n\t\t\t\treturn field ? `${field}: ${text}` : text;\n\t\t\t})\n\t\t\t.join('; ');\n\t\treturn `Invalid request — ${rendered}`;\n\t}\n\n\t// Anything else structured: show it rather than collapsing it to\n\t// `[object Object]`.\n\tif (err !== undefined) {\n\t\ttry {\n\t\t\treturn `${fallback}: ${JSON.stringify(err)}`;\n\t\t} catch {\n\t\t\treturn fallback;\n\t\t}\n\t}\n\treturn fallback;\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { GetPromptResult } from '@modelcontextprotocol/sdk/types.js';\nimport type { HostStack } from '@hoststack.dev/sdk';\nimport type { ZodRawShape, z } from 'zod';\n\nimport type { ApiClient } from '../api-client.js';\n\nexport interface PromptContext {\n\thoststack: HostStack;\n\tapi: ApiClient;\n\tresolveTeamId(): Promise<number>;\n}\n\ninterface InternalPromptDefinition {\n\tname: string;\n\tdescription: string;\n\targs: ZodRawShape;\n\thandler: (\n\t\targs: Record<string, string | undefined>,\n\t\tctx: PromptContext,\n\t) => Promise<GetPromptResult>;\n}\n\nexport type PromptDefinition = Readonly<InternalPromptDefinition>;\n\nconst prompts: InternalPromptDefinition[] = [];\n\n/**\n * Register a single prompt. The generic over `TArgs` lets the handler receive\n * precisely-typed args while the registry stores the widened shape — same\n * trick as `defineTool` to keep the compiler's instantiation budget in check.\n */\nexport function definePrompt<TArgs extends ZodRawShape>(def: {\n\tname: string;\n\tdescription: string;\n\targs: TArgs;\n\thandler: (args: z.infer<z.ZodObject<TArgs>>, ctx: PromptContext) => Promise<GetPromptResult>;\n}): void {\n\tprompts.push({\n\t\tname: def.name,\n\t\tdescription: def.description,\n\t\targs: def.args,\n\t\thandler: def.handler as InternalPromptDefinition['handler'],\n\t});\n}\n\nexport function listPromptDefinitions(): ReadonlyArray<PromptDefinition> {\n\treturn prompts;\n}\n\n/** Iterate the registry and wire each prompt into the MCP server. */\nexport function attachPrompts(server: McpServer, ctx: PromptContext): void {\n\ttype PromptMethod = (\n\t\tname: string,\n\t\tdescription: string,\n\t\targsSchema: ZodRawShape,\n\t\tcb: (args: Record<string, string | undefined>) => Promise<GetPromptResult>,\n\t) => unknown;\n\n\tconst register = server.prompt.bind(server) as unknown as PromptMethod;\n\n\tfor (const def of prompts) {\n\t\tregister(def.name, def.description, def.args, (args) => def.handler(args, ctx));\n\t}\n}\n\n/**\n * Helper to build a single user-role text message — the most common shape for\n * these prompts (we're guiding the agent, not modeling assistant responses).\n */\nexport function userMessage(text: string): GetPromptResult['messages'][number] {\n\treturn { role: 'user', content: { type: 'text', text } };\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { ReadResourceResult } from '@modelcontextprotocol/sdk/types.js';\nimport type { HostStack } from '@hoststack.dev/sdk';\n\nimport type { ApiClient } from '../api-client.js';\n\ninterface ResourceContext {\n\thoststack: HostStack;\n\tapi: ApiClient;\n\tresolveTeamId(): Promise<number>;\n}\n\ninterface StaticResourceDef {\n\tkind: 'static';\n\tname: string;\n\turi: string;\n\tdescription: string;\n\tmimeType?: string;\n\tread: (uri: URL, ctx: ResourceContext) => Promise<ReadResourceResult>;\n}\n\nexport type ResourceDefinition = Readonly<StaticResourceDef>;\n\nconst resources: StaticResourceDef[] = [];\n\nexport function defineResource(def: StaticResourceDef): void {\n\tresources.push(def);\n}\n\nexport function listResourceDefinitions(): ReadonlyArray<ResourceDefinition> {\n\treturn resources;\n}\n\nexport function attachResources(server: McpServer, ctx: ResourceContext): void {\n\tfor (const def of resources) {\n\t\tserver.resource(\n\t\t\tdef.name,\n\t\t\tdef.uri,\n\t\t\t{\n\t\t\t\tdescription: def.description,\n\t\t\t\t...(def.mimeType ? { mimeType: def.mimeType } : {}),\n\t\t\t},\n\t\t\t(uri) => def.read(uri, ctx),\n\t\t);\n\t}\n}\n\n/**\n * Pack a JSON value into the resource read-result envelope. The MCP spec\n * requires `text` for application/json resources — we serialise here so each\n * resource handler stays a one-liner.\n */\nexport function jsonResource(uri: URL, data: unknown): ReadResourceResult {\n\treturn {\n\t\tcontents: [\n\t\t\t{\n\t\t\t\turi: uri.toString(),\n\t\t\t\tmimeType: 'application/json',\n\t\t\t\ttext: JSON.stringify(data, null, 2),\n\t\t\t},\n\t\t],\n\t};\n}\n","import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';\nimport type { HostStack } from '@hoststack.dev/sdk';\nimport type { ZodRawShape, z } from 'zod';\n\nimport type { ApiClient } from './api-client.js';\n\n/**\n * Every category a tool may declare, as a runtime value.\n *\n * The type below is DERIVED from this rather than written beside it, because\n * the two had already drifted: `output-contract.test.ts` kept its own hand-copy\n * of this list, 'support' was added to the type and not to the copy, and the\n * test only failed on the runs where a sibling test file happened to have\n * imported `tools/issue-reports.ts` into the same process first. One list, one\n * source of truth, and the test reads it.\n */\nexport const TOOL_CATEGORIES = [\n\t'projects',\n\t'services',\n\t'deploys',\n\t'databases',\n\t'domains',\n\t'dns',\n\t'volumes',\n\t'machines',\n\t'env-vars',\n\t'environments',\n\t'resource-links',\n\t'cron',\n\t'dev-tasks',\n\t'logs',\n\t'activity-log',\n\t'alerts',\n\t'errors',\n\t'uptime',\n\t'analytics',\n\t'github',\n\t'support',\n\t'meta',\n] as const;\n\nexport type ToolCategory = (typeof TOOL_CATEGORIES)[number];\n\nexport interface ToolContext {\n\thoststack: HostStack;\n\tapi: ApiClient;\n\t/**\n\t * Lazy resolver for the team ID bound to the API key. The agent never has\n\t * to pass `teamId` to a tool — every SDK route is team-scoped, so the MCP\n\t * looks it up once via `/api/auth/me` and caches it for the rest of the\n\t * server's lifetime. Throws if the API key is invalid or unbound.\n\t */\n\tresolveTeamId(): Promise<number>;\n}\n\ninterface InternalToolDefinition {\n\tname: string;\n\tcategory: ToolCategory;\n\tdescription: string;\n\tinput: ZodRawShape;\n\thandler: (args: Record<string, unknown>, ctx: ToolContext) => Promise<CallToolResult>;\n}\n\nexport type ToolDefinition = Readonly<InternalToolDefinition>;\n\nconst tools: InternalToolDefinition[] = [];\n\n/**\n * Register a single tool definition. The generic over `TInput` lets the\n * handler receive precisely-typed arguments while the registry stores the\n * widened shape.\n */\nexport function defineTool<TInput extends ZodRawShape>(def: {\n\tname: string;\n\tcategory: ToolCategory;\n\tdescription: string;\n\tinput: TInput;\n\thandler: (args: z.infer<z.ZodObject<TInput>>, ctx: ToolContext) => Promise<CallToolResult>;\n}): void {\n\ttools.push({\n\t\tname: def.name,\n\t\tcategory: def.category,\n\t\tdescription: def.description,\n\t\tinput: def.input,\n\t\thandler: def.handler as InternalToolDefinition['handler'],\n\t});\n}\n\n/** Snapshot the currently-registered tool definitions (for tests / docs). */\nexport function listToolDefinitions(): ReadonlyArray<ToolDefinition> {\n\treturn tools;\n}\n\n/**\n * Telemetry event emitted once per tool invocation. The MCP package never\n * persists these — it just hands them to the optional `onToolCall` sink so\n * the host (HTTP transport, CLI, tests) can decide where they go.\n *\n * Privacy: `inputHash` is a hash over the JSON-stringified args; we never\n * expose the args themselves so PII from tool inputs (env-var values, domain\n * names) never reaches a sink.\n */\nexport interface ToolCallEvent {\n\ttool: string;\n\tdurationMs: number;\n\tok: boolean;\n\terrorCode: string | null;\n\tinputHash: string | null;\n\tstartedAt: Date;\n}\n\nexport type ToolCallSink = (event: ToolCallEvent) => void | Promise<void>;\n\n/** Iterate the registry and wire each tool into the MCP server. */\nexport function attachTools(server: McpServer, ctx: ToolContext, sink?: ToolCallSink): void {\n\ttype ToolMethod = (\n\t\tname: string,\n\t\tdescription: string,\n\t\tschema: ZodRawShape,\n\t\tcb: (args: Record<string, unknown>) => Promise<CallToolResult>,\n\t) => unknown;\n\n\tconst register = server.tool.bind(server) as unknown as ToolMethod;\n\n\tfor (const def of tools) {\n\t\tregister(def.name, def.description, def.input, async (args) => {\n\t\t\tconst startedAt = new Date();\n\t\t\tconst start = performance.now();\n\t\t\tlet ok = true;\n\t\t\tlet errorCode: string | null = null;\n\t\t\ttry {\n\t\t\t\tconst result = await def.handler(args, ctx);\n\t\t\t\tif (result.isError) {\n\t\t\t\t\tok = false;\n\t\t\t\t\terrorCode = 'tool_error';\n\t\t\t\t}\n\t\t\t\treturn result;\n\t\t\t} catch (err) {\n\t\t\t\tok = false;\n\t\t\t\terrorCode =\n\t\t\t\t\terr instanceof Error ? (err.name || 'Error').slice(0, 64) : 'unknown_error';\n\t\t\t\tthrow err;\n\t\t\t} finally {\n\t\t\t\tif (sink) {\n\t\t\t\t\tconst durationMs = Math.round(performance.now() - start);\n\t\t\t\t\tconst event: ToolCallEvent = {\n\t\t\t\t\t\ttool: def.name,\n\t\t\t\t\t\tdurationMs,\n\t\t\t\t\t\tok,\n\t\t\t\t\t\terrorCode,\n\t\t\t\t\t\tinputHash: hashArgs(args),\n\t\t\t\t\t\tstartedAt,\n\t\t\t\t\t};\n\t\t\t\t\tPromise.resolve()\n\t\t\t\t\t\t.then(() => sink(event))\n\t\t\t\t\t\t.catch(() => undefined);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n}\n\nfunction hashArgs(args: Record<string, unknown>): string | null {\n\ttry {\n\t\tconst json = JSON.stringify(args);\n\t\tconst bunGlobal = (globalThis as { Bun?: { hash?: (data: string) => bigint } }).Bun;\n\t\tif (bunGlobal?.hash) {\n\t\t\treturn bunGlobal.hash(json).toString(16);\n\t\t}\n\t\treturn `len:${json.length}`;\n\t} catch {\n\t\treturn null;\n\t}\n}\n","import { z } from 'zod';\n\nimport { definePrompt, userMessage } from './registry.js';\n\ndefinePrompt({\n\tname: 'diagnose_failed_deploy',\n\tdescription:\n\t\t'Walk the agent through diagnosing a failed deploy: pull the latest deploys for the named service, find the most recent failure, fetch its build/deploy logs, and propose a fix. Stops at suggestion — does not retrigger automatically.',\n\targs: {\n\t\tservice_id: z.string().describe('Service publicId (e.g. svc_abc123).'),\n\t},\n\thandler: async ({ service_id }) => {\n\t\tconst text = `Goal: diagnose the most recent failed deploy on service ${service_id} and propose a fix.\n\nPlan (use these tools in order):\n1. \\`list_deploys({ service_id: \"${service_id}\" })\\` — pull recent deploys, newest first. Identify the most recent deploy with status \\`failed\\` (or \\`cancelled\\` if the user wants to investigate that too). Capture its publicId, branch, and commitHash.\n2. \\`get_deploy_logs({ service_id: \"${service_id}\", deploy_id: \"<dpl_…>\" })\\` — read the full build output. Scan for: stack traces, \"ERROR\" / \"error:\" lines, exit codes, missing env vars, OOM-killed signals, network failures pulling dependencies.\n3. \\`get_service({ service_id: \"${service_id}\" })\\` — confirm the service's runtime, plan, and whether autoDeploy is on. Plan tier matters because OOMs at small tiers point at \\`maxMemoryMb\\`.\n4. (Optional) \\`list_env_vars({ service_id: \"${service_id}\" })\\` — only if the build error mentions a missing variable. Don't fetch otherwise; values are masked anyway.\n\nThen write a short diagnosis for the user with three sections:\n- **Root cause** — one sentence pointing at the actual failure line.\n- **Evidence** — 3–6 line excerpt from the logs that proves the diagnosis.\n- **Suggested fix** — concrete next steps, including which tool to call (e.g. \"set MISSING_KEY with set_env_var, then trigger_deploy\"). Do NOT call trigger_deploy automatically; the user decides.\n\nIf the deploy logs are empty or only contain queued/pending markers, surface that — the build never started, which is its own class of bug (likely a builder-pool or quota issue).`;\n\n\t\treturn { messages: [userMessage(text)] };\n\t},\n});\n\ndefinePrompt({\n\tname: 'deploy_status_check',\n\tdescription:\n\t\t\"Quick orientation prompt: who am I, what's running, and is anything currently deploying? Useful at the start of a session.\",\n\targs: {},\n\thandler: async () => {\n\t\tconst text = `Goal: produce a 30-second status check on this HostStack team.\n\nPlan:\n1. \\`get_me()\\` — confirm which user + team the API key belongs to.\n2. \\`list_services()\\` — every service with current status (running, suspended, building, deploying, failed). Note any service in a non-running state.\n3. For each service whose status is \\`building\\` or \\`deploying\\`, call \\`list_deploys({ service_id })\\` and surface the latest deploy's commitMessage + author.\n4. \\`list_activity_log({ per_page: 10 })\\` — last 10 audit events to spot recent changes (deploys triggered, env vars edited, services restarted).\n\nWrite a status report shaped like:\n\n> **Team**: <name>\n> **Services**: <n> total — <breakdown by status>\n> **In flight**: <list of building/deploying services with commit info, or \"none\">\n> **Recent activity**: <3-5 most relevant events from the audit log, newest first>\n\nKeep it tight (under 200 words). The user can drill in with follow-up tools.`;\n\n\t\treturn { messages: [userMessage(text)] };\n\t},\n});\n\ndefinePrompt({\n\tname: 'rotate_secret',\n\tdescription:\n\t\t'Guide the agent through rotating an env-var secret on a service. Lists current vars to confirm the target key exists, asks the user for the new value, sets it, and offers to trigger a deploy.',\n\targs: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t\tkey: z.string().describe('Env-var key to rotate (e.g. DATABASE_URL).'),\n\t},\n\thandler: async ({ service_id, key }) => {\n\t\tconst text = `Goal: rotate the secret \\`${key}\\` on service ${service_id}.\n\nPlan:\n1. \\`list_env_vars({ service_id: \"${service_id}\" })\\` — confirm \\`${key}\\` exists. Note its \\`isSecret\\` flag (it should be true; if not, flag this — you're about to mark it secret on rotation, which is a behaviour change).\n2. **Ask the user for the new value before calling any write tool.** The agent must NOT invent or generate the secret; it has to come from the user's clipboard / vault. Phrase the ask explicitly: \"Paste the new value for ${key} (it will be encrypted at rest and masked everywhere except the running container).\"\n3. Once the user provides the value:\n - \\`set_env_var({ service_id: \"${service_id}\", key: \"${key}\", value: \"<new value>\", is_secret: true })\\` — upserts by key.\n4. After the write succeeds, ask the user whether to redeploy (so the new value lands in the running container). If yes:\n - \\`trigger_deploy({ service_id: \"${service_id}\" })\\` — kicks off a build with the rotated secret.\n - \\`get_deploy({ service_id: \"${service_id}\", deploy_id: \"<dpl_…>\" })\\` — poll until status is \\`live\\` or \\`failed\\`. On failure, hand off to \\`diagnose_failed_deploy\\`.\n\nSafety rails:\n- Never echo the new value back in your reply; the user should verify it from their own source of truth, not from your context window.\n- If the user says \"generate a new one\", suggest \\`openssl rand -hex 32\\` (or similar) for them to run locally and paste in. Do NOT generate it for them — secrets that pass through an LLM context are no longer secrets.\n- If \\`set_env_var\\` returned \\`action: \"created\"\\` instead of \\`\"updated\"\\`, the key didn't exist before. Pause and confirm with the user — they may have typo'd the key name.`;\n\n\t\treturn { messages: [userMessage(text)] };\n\t},\n});\n","/**\n * Per-resource shape pickers. Each function takes a raw API/SDK response and\n * returns the agent-friendly subset: drops internal foreign keys (teamId,\n * userId, ...) and null-valued columns, leaves ISO-8601 timestamps as-is.\n *\n * Pickers must be permissive about input shape — different SDK methods may\n * return slightly different fields (e.g. list vs get) and the API may grow\n * fields over time. We never throw on missing fields.\n */\n\nconst INTERNAL_FIELDS = new Set([\n\t'teamId',\n\t'team_id',\n\t'userId',\n\t'user_id',\n\t'accountId',\n\t'account_id',\n\t'tenantId',\n\t'tenant_id',\n\t'createdById',\n\t'updatedById',\n]);\n\ntype Obj = Record<string, unknown>;\n\nfunction isObject(value: unknown): value is Obj {\n\treturn value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\nfunction dropNullsAndInternals(obj: Obj): Obj {\n\tconst out: Obj = {};\n\tfor (const [key, value] of Object.entries(obj)) {\n\t\tif (INTERNAL_FIELDS.has(key)) continue;\n\t\tif (value === null || value === undefined) continue;\n\t\tout[key] = value;\n\t}\n\treturn out;\n}\n\n/** Generic shape — drop internals, drop nulls, keep everything else. */\nexport function shape(value: unknown): unknown {\n\tif (Array.isArray(value)) return value.map(shape);\n\tif (isObject(value)) return dropNullsAndInternals(value);\n\treturn value;\n}\n\nfunction shapeAll<T>(arr: unknown, shaper: (item: unknown) => T): T[] {\n\tif (!Array.isArray(arr)) return [];\n\treturn arr.map(shaper);\n}\n\n/**\n * Unwrap a HostStack list response. The API returns shapes like\n * `{ projects: [...] }`, `{ services: [...] }`, etc. — the key varies by\n * resource. Pass the key explicitly so the wrapper stays type-loose.\n */\nexport function shapeList<T>(\n\tresponse: unknown,\n\tkey: string,\n\tshaper: (item: unknown) => T,\n): { items: T[] } {\n\tif (!isObject(response)) return { items: [] };\n\treturn { items: shapeAll(response[key], shaper) };\n}\n\n// ──────────────────────────────────────────────────────────────────────\n// Per-resource pickers — typed convenience wrappers around shape().\n// All return a plain Obj (never null), keeping the registry handlers simple.\n// ──────────────────────────────────────────────────────────────────────\n\nexport function shapeProject(value: unknown): Obj {\n\treturn isObject(value) ? dropNullsAndInternals(value) : {};\n}\n\nexport function shapeService(value: unknown): Obj {\n\treturn isObject(value) ? dropNullsAndInternals(value) : {};\n}\n\nexport function shapeDeploy(value: unknown): Obj {\n\treturn isObject(value) ? dropNullsAndInternals(value) : {};\n}\n\nexport function shapeDatabase(value: unknown): Obj {\n\treturn isObject(value) ? dropNullsAndInternals(value) : {};\n}\n\nexport function shapeDomain(value: unknown): Obj {\n\treturn isObject(value) ? dropNullsAndInternals(value) : {};\n}\n\nexport function shapeEnvVar(value: unknown): Obj {\n\treturn isObject(value) ? dropNullsAndInternals(value) : {};\n}\n\nexport function shapeCronExecution(value: unknown): Obj {\n\treturn isObject(value) ? dropNullsAndInternals(value) : {};\n}\n\nexport function shapeActivity(value: unknown): Obj {\n\treturn isObject(value) ? dropNullsAndInternals(value) : {};\n}\n\nexport function shapeUser(value: unknown): Obj {\n\treturn isObject(value) ? dropNullsAndInternals(value) : {};\n}\n\nexport function shapeTeam(value: unknown): Obj {\n\treturn isObject(value) ? dropNullsAndInternals(value) : {};\n}\n\nexport function shapeVolume(value: unknown): Obj {\n\treturn isObject(value) ? dropNullsAndInternals(value) : {};\n}\n","import { shapeProject, shapeService, shapeTeam, shapeUser } from '../lib/shape.js';\nimport { defineResource, jsonResource } from './registry.js';\n\ninterface MeResponse {\n\tuser: unknown;\n\tteam?: unknown;\n}\n\ndefineResource({\n\tkind: 'static',\n\tname: 'team',\n\turi: 'hoststack://team',\n\tdescription:\n\t\t'Authenticated team identity + a quick orientation snapshot. Includes the team record, the user behind the API key, and counts of projects and services so an agent can decide whether to fan out into list_projects / list_services without an extra round-trip.',\n\tmimeType: 'application/json',\n\tread: async (uri, { hoststack, api, resolveTeamId }) => {\n\t\tconst teamId = await resolveTeamId();\n\n\t\t// Resource handlers must not throw on transient downstream failures —\n\t\t// clients call resources/read for orientation and a network blip\n\t\t// shouldn't poison the entire session. Tolerate partial failure and\n\t\t// surface what we can.\n\t\tconst [me, projectsResult, servicesResult] = await Promise.all([\n\t\t\tapi.get<MeResponse>('/api/auth/me').catch(() => null),\n\t\t\thoststack.projects.list(teamId).catch(() => ({ projects: [] as unknown[] })),\n\t\t\thoststack.services.list(teamId).catch(() => ({ services: [] as unknown[] })),\n\t\t]);\n\n\t\tconst projects = (projectsResult.projects as unknown[]).slice(0, 10).map(shapeProject);\n\t\tconst services = (servicesResult.services as unknown[]).slice(0, 10).map(shapeService);\n\n\t\treturn jsonResource(uri, {\n\t\t\tuser: me?.user ? shapeUser(me.user) : null,\n\t\t\tteam: me?.team ? shapeTeam(me.team) : null,\n\t\t\tproject_count: (projectsResult.projects as unknown[]).length,\n\t\t\tservice_count: (servicesResult.services as unknown[]).length,\n\t\t\t// First 10 of each so the resource stays small while still useful\n\t\t\t// for orientation. Use list_projects / list_services for the full set.\n\t\t\tprojects_preview: projects,\n\t\t\tservices_preview: services,\n\t\t});\n\t},\n});\n","import { z } from 'zod';\n\nimport { defineTool } from '../registry.js';\nimport { respond } from '../lib/respond.js';\nimport { shape, shapeActivity } from '../lib/shape.js';\n\ninterface ActivityLogResponse {\n\tdata?: unknown[];\n\t// Older API style: pagination meta as a single nested object. Some\n\t// callers still serve this shape, so keep accepting it.\n\tmeta?: unknown;\n\t// Current API style: pagination meta fields are spread flat on the\n\t// response root (`{ data, page, perPage, total, totalPages }`).\n\tpage?: number;\n\tperPage?: number;\n\ttotal?: number;\n\ttotalPages?: number;\n}\n\ndefineTool({\n\tname: 'list_activity_log',\n\tcategory: 'activity-log',\n\tdescription: [\n\t\t'List the team activity audit log: who did what, when. Backed by /api/activity-log/:teamId.',\n\t\t'',\n\t\t'When to use: investigate why a service was changed unexpectedly (\"who deleted that env var?\"), correlate a deploy with a user, or pull recent admin events for a status update.',\n\t\t'',\n\t\t'Inputs (all optional):',\n\t\t' - page: 1-based page index (default 1).',\n\t\t' - per_page: items per page (default 25, hard cap 100).',\n\t\t' - action: filter to a specific action (e.g. \"service.created\", \"deploy.triggered\").',\n\t\t' - resource_type: filter by resource type (e.g. \"service\", \"deploy\", \"domain\").',\n\t\t' - user_id: filter by acting user numeric ID.',\n\t\t' - since/until: ISO-8601 timestamp OR a relative offset like \"-15m\" / \"-2h\" / \"-7d\" — narrow to a time window (handy for correlating a deploy with a log spike).',\n\t\t'',\n\t\t'Returns: { items: ActivityLogEntry[], meta: { page, perPage, total, totalPages } } — each entry has id, action, resourceType, resourceId, actorEmail, ipAddress, createdAt, and a context payload. Use meta.totalPages to tell whether you have more.',\n\t\t'',\n\t\t'Example: list_activity_log({ resource_type: \"deploy\", per_page: 10 }) → { items: [{ action: \"deploy.triggered\", actorEmail: \"ada@…\", … }], meta: { total: 47, page: 1 } }',\n\t].join('\\n'),\n\tinput: {\n\t\tpage: z.number().int().positive().optional().describe('Page index, 1-based.'),\n\t\tper_page: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.positive()\n\t\t\t.max(100)\n\t\t\t.optional()\n\t\t\t.describe('Items per page, hard cap 100.'),\n\t\taction: z.string().optional().describe('Action filter, e.g. \"service.created\".'),\n\t\tresource_type: z.string().optional().describe('Resource type filter.'),\n\t\tuser_id: z.number().int().positive().optional().describe('Numeric acting-user filter.'),\n\t\tsince: z\n\t\t\t.string()\n\t\t\t.optional()\n\t\t\t.describe('ISO-8601 timestamp or relative offset (e.g. \"-15m\", \"-2h\", \"-7d\").'),\n\t\tuntil: z.string().optional().describe('ISO-8601 timestamp or relative offset upper bound.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst params: Record<string, string | number | undefined> = {};\n\t\tif (args.page !== undefined) params['page'] = String(args.page);\n\t\tif (args.per_page !== undefined) params['perPage'] = String(args.per_page);\n\t\tif (args.action !== undefined) params['action'] = args.action;\n\t\tif (args.resource_type !== undefined) params['resourceType'] = args.resource_type;\n\t\tif (args.user_id !== undefined) params['userId'] = String(args.user_id);\n\t\tif (args.since !== undefined) params['since'] = args.since;\n\t\tif (args.until !== undefined) params['until'] = args.until;\n\n\t\tconst response = await ctx.api.get<ActivityLogResponse>(\n\t\t\t`/api/activity-log/${teamId}`,\n\t\t\tparams,\n\t\t);\n\t\tconst items = Array.isArray(response.data) ? response.data.map(shapeActivity) : [];\n\t\tconst data: { items: unknown[]; meta?: unknown } = { items };\n\t\t// The API spreads pagination at the top level (`{ data, page, perPage,\n\t\t// total, totalPages }`). Older variants nested it under `meta`.\n\t\t// Surface a single `meta` object regardless so MCP callers don't have\n\t\t// to learn two shapes.\n\t\tif (response.meta !== undefined) {\n\t\t\tdata.meta = shape(response.meta);\n\t\t} else if (\n\t\t\tresponse.page !== undefined ||\n\t\t\tresponse.perPage !== undefined ||\n\t\t\tresponse.total !== undefined ||\n\t\t\tresponse.totalPages !== undefined\n\t\t) {\n\t\t\tdata.meta = shape({\n\t\t\t\tpage: response.page,\n\t\t\t\tperPage: response.perPage,\n\t\t\t\ttotal: response.total,\n\t\t\t\ttotalPages: response.totalPages,\n\t\t\t});\n\t\t}\n\n\t\treturn respond({\n\t\t\tsummary:\n\t\t\t\titems.length === 0\n\t\t\t\t\t? 'No activity log entries match the given filters.'\n\t\t\t\t\t: `Returned ${items.length} activity log entr${items.length === 1 ? 'y' : 'ies'}.`,\n\t\t\tdata,\n\t\t});\n\t},\n});\n","import type { CallToolResult, ContentBlock } from '@modelcontextprotocol/sdk/types.js';\n\ninterface RespondInput {\n\tsummary: string;\n\tdata?: unknown;\n}\n\nexport const SUMMARY_MAX_LEN = 500;\n\n/**\n * Build an MCP tool response with a 1–3 line plain-text summary, a fenced\n * JSON block for human readability, and a structuredContent object that\n * agents can parse without re-tokenising the JSON text.\n */\nexport function respond({ summary, data }: RespondInput): CallToolResult {\n\tconst trimmed = summary.trim();\n\tif (trimmed.length === 0) {\n\t\tthrow new Error('respond(): summary must be non-empty');\n\t}\n\tif (trimmed.length > SUMMARY_MAX_LEN) {\n\t\tthrow new Error(`respond(): summary too long (${trimmed.length} > ${SUMMARY_MAX_LEN})`);\n\t}\n\n\tconst blocks: ContentBlock[] = [{ type: 'text', text: trimmed }];\n\n\tif (data !== undefined) {\n\t\tblocks.push({\n\t\t\ttype: 'text',\n\t\t\ttext: '```json\\n' + JSON.stringify(data, null, 2) + '\\n```',\n\t\t});\n\t}\n\n\tconst result: CallToolResult = { content: blocks };\n\tif (data !== undefined) {\n\t\tresult.structuredContent = toStructuredContent(data);\n\t}\n\treturn result;\n}\n\nexport function respondError(message: string, data?: unknown): CallToolResult {\n\tconst blocks: ContentBlock[] = [{ type: 'text', text: `Error: ${message}` }];\n\tif (data !== undefined) {\n\t\tblocks.push({\n\t\t\ttype: 'text',\n\t\t\ttext: '```json\\n' + JSON.stringify(data, null, 2) + '\\n```',\n\t\t});\n\t}\n\tconst result: CallToolResult = { content: blocks, isError: true };\n\tif (data !== undefined) {\n\t\tresult.structuredContent = toStructuredContent(data);\n\t}\n\treturn result;\n}\n\nfunction toStructuredContent(data: unknown): Record<string, unknown> {\n\tif (data !== null && typeof data === 'object' && !Array.isArray(data)) {\n\t\treturn data as Record<string, unknown>;\n\t}\n\treturn { result: data };\n}\n","import { z } from 'zod';\n\nimport { defineTool } from '../registry.js';\nimport { respond } from '../lib/respond.js';\nimport { shape } from '../lib/shape.js';\n\ninterface AlertsResponse {\n\talerts: unknown[];\n\taggregated: boolean;\n}\n\ndefineTool({\n\tname: 'list_alerts',\n\tcategory: 'alerts',\n\tdescription: [\n\t\t'List recent alert-shaped events for the team: deploy failures, git auth losses, service health failures, uptime failures (a service stopped answering its public URL), new and regressed error issues (an exception the app reported), auto-restarts, ACME cert failures, resource alerts (high CPU), database backup/restore failures, registrant verification lapses.',\n\t\t'',\n\t\t\"When to use: triage 'what's currently broken or recently broke for this team'. Pairs with list_activity_log for the full audit feed; this tool is the alert-shaped subset. For an `error.issue_new` / `error.issue_regressed` entry, `list_error_issues` and `get_error_issue` carry the stack behind it.\",\n\t\t'',\n\t\t'By default events are AGGREGATED by (action, resourceId) so flapping events collapse to one row with a fire count + first/last timestamps — e.g. \"service.auto_restarted on service 31, 8 times in the last hour, last at 14:22\". Pass aggregate=false to see every raw row.',\n\t\t'',\n\t\t'ACTIVE vs HISTORICAL (v89): each aggregated entry now has an `active` boolean and a `lastResolvedAt` timestamp. `active=true` means the most recent occurrence has NOT been resolved — the alert is still on fire. The default `active=true` filter hides resolved/historical alerts so triage starts with \"what is broken right now\". Pass `active=false` to include resolved entries too (useful for post-mortems). Resolution is automatic for some alert kinds — e.g. `deploy.failed_consecutive` is cleared when the next deploy of the same service goes live.',\n\t\t'',\n\t\t'Database backup_failed entries (v89) include `containerStatus` / `containerHealth` / `containerExitCode` in lastMetadata when the agent could resolve the container at failure time, so you can tell \"Redis is overloaded\" apart from \"Redis is restarting\" without a second tool call.',\n\t\t'',\n\t\t'Deploy.failed_consecutive entries (v89) carry the offending `commitHash` in lastMetadata, and the streak now dedupes per (service, commitHash) — one critical per bad commit, not one every 6 retries.',\n\t\t'',\n\t\t'Inputs (all optional):',\n\t\t' - since: ISO-8601 timestamp OR relative offset like \"-1h\" / \"-2d\". Default: -24h.',\n\t\t' - until: ISO-8601 upper bound (ignored when aggregating — aggregated view always extends to now).',\n\t\t' - limit: max rows (default 100, hard cap 500).',\n\t\t' - aggregate: true (default) collapses by (action, resourceId); false returns raw rows.',\n\t\t' - active: true (default) shows only alerts that are still on fire (resolved_at IS NULL on the most recent row). false includes resolved historical alerts.',\n\t\t'',\n\t\t'Returns: { alerts: Array, aggregated: boolean, activeOnly: boolean }. Each aggregated entry includes action, resourceType, resourceId, severity, count, firstFiredAt, lastFiredAt, lastResolvedAt (nullable), active (boolean), lastMetadata. Each raw entry adds resolvedAt (nullable).',\n\t\t'',\n\t\t\"Example: list_alerts({ since: '-1h' }) → { alerts: [{ action: 'deploy.failed_consecutive', resourceId: 31, severity: 'critical', active: true, count: 3, lastFiredAt: '…', lastResolvedAt: null, lastMetadata: { commitHash: 'abc1234' }, … }] }.\",\n\t].join('\\n'),\n\tinput: {\n\t\tsince: z\n\t\t\t.string()\n\t\t\t.optional()\n\t\t\t.describe('ISO-8601 timestamp or relative offset (e.g. \"-1h\", \"-2d\"). Default: -24h.'),\n\t\tuntil: z\n\t\t\t.string()\n\t\t\t.optional()\n\t\t\t.describe('ISO-8601 upper bound. Only honored when aggregate=false.'),\n\t\tlimit: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.positive()\n\t\t\t.max(500)\n\t\t\t.optional()\n\t\t\t.describe('Max rows (default 100, hard cap 500).'),\n\t\taggregate: z\n\t\t\t.boolean()\n\t\t\t.optional()\n\t\t\t.describe('Collapse by (action, resourceId). Default true.'),\n\t\tactive: z\n\t\t\t.boolean()\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t'Only return alerts still on fire (resolved_at IS NULL on the most recent row). Default true. Pass false to include resolved historical alerts.',\n\t\t\t),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst params: Record<string, string | number | undefined> = {};\n\t\tif (args.since !== undefined) params['since'] = args.since;\n\t\tif (args.until !== undefined) params['until'] = args.until;\n\t\tif (args.limit !== undefined) params['limit'] = String(args.limit);\n\t\t// API expects aggregate=1|0; default behavior on the API side is\n\t\t// aggregate=true. Pass explicit \"0\" only when caller opts out so\n\t\t// we don't ship spurious query params.\n\t\tif (args.aggregate === false) params['aggregate'] = '0';\n\t\t// v89: same convention for active=1|0. Default true on the API\n\t\t// side, so we only ship `active=0` when the caller opts in to\n\t\t// historical entries.\n\t\tif (args.active === false) params['active'] = '0';\n\n\t\tconst response = await ctx.api.get<AlertsResponse>(`/api/alerts/${teamId}`, params);\n\t\tconst items = Array.isArray(response.alerts) ? response.alerts.map(shape) : [];\n\t\tconst aggregated = Boolean(response.aggregated);\n\t\tconst activeOnly = args.active !== false;\n\t\tconst summary =\n\t\t\titems.length === 0\n\t\t\t\t? activeOnly\n\t\t\t\t\t? 'No active alerts in the requested window — everything is operating normally.'\n\t\t\t\t\t: 'No alerts in the requested window (active or resolved).'\n\t\t\t\t: aggregated\n\t\t\t\t\t? `Returned ${items.length} alert group${items.length === 1 ? '' : 's'}${activeOnly ? ' (active only — pass active=false for resolved history)' : ''} (flapping events collapsed).`\n\t\t\t\t\t: `Returned ${items.length} raw alert event${items.length === 1 ? '' : 's'}.`;\n\t\treturn respond({ summary, data: { alerts: items, aggregated, activeOnly } });\n\t},\n});\n","import { z } from 'zod';\n\nimport { defineTool } from '../registry.js';\nimport { respond } from '../lib/respond.js';\nimport { shapeCronExecution, shapeList } from '../lib/shape.js';\n\ndefineTool({\n\tname: 'list_cron_executions',\n\tcategory: 'cron',\n\tdescription: [\n\t\t'List recent execution records for a cron service (newest first).',\n\t\t'',\n\t\t'When to use: investigating whether a scheduled job ran on time, finding the most recent failure, or auditing cron run-times. Only works on services of type \"cron\"; for web services this returns an error.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the cron service.',\n\t\t' - limit (optional): how many executions to fetch (default 50, max enforced server-side).',\n\t\t'',\n\t\t'Returns: { items: CronExecution[] } — id, publicId, status (succeeded|failed|running|queued), startedAt, finishedAt, exitCode, triggeredBy.',\n\t\t'',\n\t\t'Example: list_cron_executions({ service_id: \"svc_cron\" }) → { items: [{ status: \"succeeded\", exitCode: 0, … }, …] }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Cron service publicId.'),\n\t\tlimit: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.positive()\n\t\t\t.max(200)\n\t\t\t.optional()\n\t\t\t.describe('Max executions to return (default 50).'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst opts = args.limit !== undefined ? { limit: args.limit } : undefined;\n\t\tconst response = await ctx.hoststack.cron.list(teamId, args.service_id, opts);\n\t\tconst data = shapeList(response, 'executions', shapeCronExecution);\n\t\tconst summary =\n\t\t\tdata.items.length === 0\n\t\t\t\t? `No cron executions yet for ${args.service_id}.`\n\t\t\t\t: `Found ${data.items.length} cron execution${data.items.length === 1 ? '' : 's'} for ${args.service_id}.`;\n\t\treturn respond({ summary, data });\n\t},\n});\n\ndefineTool({\n\tname: 'get_cron_execution',\n\tcategory: 'cron',\n\tdescription: [\n\t\t'Fetch a single cron execution record. Includes status, exit code, and timing.',\n\t\t'',\n\t\t'When to use: drilling into a specific run after list_cron_executions surfaced a failure, or correlating an execution timestamp with logs.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the cron service.',\n\t\t' - execution_id: publicId of the execution record.',\n\t\t'',\n\t\t'Returns: { execution: CronExecution }.',\n\t\t'',\n\t\t'Example: get_cron_execution({ service_id: \"svc_cron\", execution_id: \"exe_xyz\" }) → { execution: { status: \"failed\", exitCode: 1, … } }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Cron service publicId.'),\n\t\texecution_id: z.string().describe('Execution publicId.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.cron.get(teamId, args.service_id, args.execution_id);\n\t\tconst data = { execution: shapeCronExecution(response.execution) };\n\t\tconst status =\n\t\t\tdata.execution && 'status' in data.execution\n\t\t\t\t? (data.execution as { status: string }).status\n\t\t\t\t: 'unknown';\n\t\treturn respond({ summary: `Execution ${args.execution_id} ${status}.`, data });\n\t},\n});\n","import { z } from 'zod';\n\nimport { defineTool } from '../registry.js';\nimport { respond } from '../lib/respond.js';\nimport { shapeDatabase, shapeList } from '../lib/shape.js';\nimport { machineInput, resolveMachineId } from './machines.js';\n\n/**\n * Supported + default engine versions. DUPLICATED from\n * packages/shared/src/constants/database-versions.ts on purpose — the MCP\n * package must not depend on the private @hoststack/shared workspace package\n * (see the same note in tools/services.ts). Kept in sync by review.\n */\nconst DATABASE_VERSIONS = {\n\tpostgres: { default: '18', supported: ['18', '17', '16', '15'] },\n\tredis: { default: '8', supported: ['8', '7', '6'] },\n\tmysql: { default: '8.4', supported: ['8.4', '8.0', '5.7'] },\n\tmariadb: { default: '11.4', supported: ['11.4', '10.11'] },\n\tmongodb: { default: '8', supported: ['8', '7', '6'] },\n} as const;\n\nconst DB_ENGINES = ['postgres', 'redis', 'mysql', 'mariadb', 'mongodb'] as const;\n\nconst VERSION_HELP = (Object.keys(DATABASE_VERSIONS) as (keyof typeof DATABASE_VERSIONS)[])\n\t.map(\n\t\t(e) =>\n\t\t\t`${e}: ${DATABASE_VERSIONS[e].supported.join('/')} (default ${DATABASE_VERSIONS[e].default})`,\n\t)\n\t.join('; ');\n\ndefineTool({\n\tname: 'create_database',\n\tcategory: 'databases',\n\tdescription: [\n\t\t'Provision a MANAGED database (Postgres, Redis, MySQL, MariaDB, MongoDB) in a project. This is the correct and ONLY supported way to add a database to a HostStack app.',\n\t\t'',\n\t\t'When to use: any time an app needs a datastore — \"add a database\", \"I need Postgres\", \"set up Redis\", \"give this service a DB\". Also the right call when scaffolding a new app that will need persistence.',\n\t\t'',\n\t\t'*** Do NOT hand-roll a database as a service. *** Deploying `postgres:16` (or redis/mysql/mongo) via create_service with a docker_image is NOT how databases work on this platform: it gets no managed backups, no automated version upgrades, no HA/failover path, no credential rotation, no metrics, no persistent volume wired up, and nothing will inject its connection URL into your app. If the user says \"add a database\", \"I need Postgres\", \"set up Redis\" — call THIS tool.',\n\t\t'',\n\t\t'How a database reaches your app (the whole flow — 3 calls, no secrets handled):',\n\t\t' 1. create_database({ project_id, name, engine }) — provisions it.',\n\t\t' 2. link_resource_to_service({ service_id, resource_type: \"database\", resource_id: <database.id>, alias: \"APP_DB\" }) — binds it to the service that needs it.',\n\t\t' 3. trigger_deploy({ service_id }) — on that deploy the platform injects the connection info as env vars.',\n\t\t'After step 3 the container has `DATABASE_URL` (postgres/mysql/mariadb), `REDIS_URL` (redis) or `MONGO_URL` (mongodb) set automatically, plus alias-prefixed vars. An app reading `process.env.DATABASE_URL` needs zero code changes. You never have to fetch, generate or paste a password.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - project_id: numeric id or publicId (\"prj_…\") of the target project.',\n\t\t' - name: database name (1–100 chars).',\n\t\t` - engine: ${DB_ENGINES.join(' | ')}.`,\n\t\t` - version (optional): engine version. Supported — ${VERSION_HELP}. Omit to get the default (recommended).`,\n\t\t' - plan (optional): \"micro\" | \"starter\" | \"standard\" | \"pro\" (default \"starter\"). \"starter\" is the smallest always-on tier that includes backups.',\n\t\t' - environment_id (optional): bind to a specific environment; defaults to the project Production env.',\n\t\t' - postgis (optional): provision the PostGIS image variant so `CREATE EXTENSION postgis` works. Postgres only.',\n\t\t' - pgvector (optional): provision the pgvector image variant so `CREATE EXTENSION vector` works. Postgres only, mutually exclusive with postgis.',\n\t\t\" - machine (optional): put it on one of the team's OWN enrolled machines (name or id, see list_machines) instead of HostStack compute. It costs nothing there and is still backed up off-site, but it is reachable ONLY from that same machine — the service that queries it has to run there too, and linking it to a service anywhere else is refused. No HA and no external access on a machine you own.\",\n\t\t'',\n\t\t'Returns: { database: Database } — note BOTH `id` (numeric — this is what link_resource_to_service wants) and `publicId` (\"db_…\" — what the other database tools want).',\n\t\t'',\n\t\t'Provisioning is async: the row comes back immediately, usually as `creating`. Poll get_database until `status === \"available\"` before linking or connecting.',\n\t\t'',\n\t\t'Example: create_database({ project_id: \"prj_abc\", name: \"app-db\", engine: \"postgres\" }) → { database: { id: 42, publicId: \"db_…\", status: \"creating\" } }',\n\t].join('\\n'),\n\tinput: {\n\t\tproject_id: z\n\t\t\t.union([z.number().int().positive(), z.string()])\n\t\t\t.describe('Target project — numeric id or publicId (\"prj_…\").'),\n\t\tname: z.string().min(1).max(100).describe('Database name (1–100 chars).'),\n\t\tengine: z.enum(DB_ENGINES).describe('Database engine.'),\n\t\tversion: z\n\t\t\t.string()\n\t\t\t.max(20)\n\t\t\t.optional()\n\t\t\t.describe(`Engine version. ${VERSION_HELP}. Omit for the engine default.`),\n\t\tplan: z\n\t\t\t.enum(['micro', 'starter', 'standard', 'pro'])\n\t\t\t.optional()\n\t\t\t.describe('Plan tier (memory/CPU). Default \"starter\".'),\n\t\tenvironment_id: z\n\t\t\t.union([z.number().int().positive(), z.string()])\n\t\t\t.optional()\n\t\t\t.describe('Environment to bind to. Defaults to the project Production env.'),\n\t\tpostgis: z\n\t\t\t.boolean()\n\t\t\t.optional()\n\t\t\t.describe('Postgres only — enable the PostGIS extension image.'),\n\t\tpgvector: z\n\t\t\t.boolean()\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t'Postgres only — enable the pgvector extension image. Exclusive with postgis.',\n\t\t\t),\n\t\tmachine: machineInput,\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst projectId = await ctx.hoststack.resolveId(args.project_id, {\n\t\t\tkind: 'project',\n\t\t\tteamId,\n\t\t});\n\t\ttype CreateInput = Parameters<typeof ctx.hoststack.databases.create>[1];\n\t\tconst input: CreateInput = {\n\t\t\tname: args.name,\n\t\t\tengine: args.engine,\n\t\t\tprojectId,\n\t\t};\n\t\tif (args.version !== undefined) input.version = args.version;\n\t\tif (args.plan !== undefined) input.plan = args.plan;\n\t\tif (args.environment_id !== undefined) {\n\t\t\tinput.environmentId = await ctx.hoststack.resolveId(args.environment_id, {\n\t\t\t\tkind: 'environment',\n\t\t\t\tteamId,\n\t\t\t});\n\t\t}\n\t\tif (args.postgis !== undefined) input.postgis = args.postgis;\n\t\tif (args.pgvector !== undefined) input.pgvector = args.pgvector;\n\t\tif (args.machine !== undefined) {\n\t\t\tinput.machineId = await resolveMachineId(ctx, teamId, args.machine);\n\t\t}\n\n\t\tconst response = await ctx.hoststack.databases.create(teamId, input);\n\t\tconst data = { database: shapeDatabase(response.database) };\n\t\tconst db = response.database as { id?: number; publicId?: string; status?: string };\n\t\treturn respond({\n\t\t\tsummary: `Created ${args.engine} database \"${args.name}\" (${db.publicId ?? 'unknown'}, numeric id ${db.id ?? '?'}) — status ${db.status ?? 'creating'}. Poll get_database until status=available, then link_resource_to_service to inject its URL into a service.`,\n\t\t\tdata,\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'delete_database',\n\tcategory: 'databases',\n\tdescription: [\n\t\t'Permanently delete a managed database — the container, its volume, and ALL data it holds.',\n\t\t'',\n\t\t'When to use: the user has explicitly asked to destroy a database they no longer want. This is irreversible and takes the data with it — confirm with the user before calling, and never call it to \"clean up\" as a side effect of another task. If the goal is to stop paying for an idle database while keeping the data, use suspend_database instead.',\n\t\t'',\n\t\t\"Any service still linked to this database will lose the injected connection vars on its next deploy, and will fail at runtime if it depends on them. Check which services consume it first — the dashboard shows this under the database's Linked Services tab.\",\n\t\t'',\n\t\t'Inputs:',\n\t\t' - database_id: publicId of the database (e.g. \"db_…\").',\n\t\t'',\n\t\t'Returns: { ok: true }.',\n\t\t'',\n\t\t'Example: delete_database({ database_id: \"db_abc\" }) → { ok: true }',\n\t].join('\\n'),\n\tinput: {\n\t\tdatabase_id: z.string().describe('Database publicId (e.g. db_abc) to permanently delete.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tawait ctx.hoststack.databases.delete(teamId, args.database_id);\n\t\treturn respond({\n\t\t\tsummary: `Deleted database ${args.database_id} and all of its data. This cannot be undone.`,\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'suspend_database',\n\tcategory: 'databases',\n\tdescription: [\n\t\t'Suspend a managed database — stops its container while KEEPING the volume and all data. The reversible alternative to delete_database.',\n\t\t'',\n\t\t'When to use: a staging or preview database that nobody is using, to stop burning its compute tier. Connections fail while suspended; resume_database brings it back on the same connection URL.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - database_id: publicId of the database (e.g. \"db_…\").',\n\t\t'',\n\t\t'Returns: { ok: true }.',\n\t\t'',\n\t\t'Example: suspend_database({ database_id: \"db_abc\" }) → { ok: true }',\n\t].join('\\n'),\n\tinput: {\n\t\tdatabase_id: z.string().describe('Database publicId (e.g. db_abc) to suspend.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tawait ctx.hoststack.databases.suspend(teamId, args.database_id);\n\t\treturn respond({\n\t\t\tsummary: `Suspended database ${args.database_id}. Data is preserved; resume_database restores it on the same URL.`,\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'resume_database',\n\tcategory: 'databases',\n\tdescription: [\n\t\t'Resume a suspended managed database — restarts its container against the existing volume. The connection URL is unchanged, so linked services do NOT need a redeploy.',\n\t\t'',\n\t\t'When to use: undoing a suspend_database, or bringing back a database that was suspended for non-payment once billing is resolved.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - database_id: publicId of the database (e.g. \"db_…\").',\n\t\t'',\n\t\t'Returns: { ok: true }. Poll get_database until `status === \"available\"`.',\n\t\t'',\n\t\t'Example: resume_database({ database_id: \"db_abc\" }) → { ok: true }',\n\t].join('\\n'),\n\tinput: {\n\t\tdatabase_id: z.string().describe('Database publicId (e.g. db_abc) to resume.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tawait ctx.hoststack.databases.resume(teamId, args.database_id);\n\t\treturn respond({\n\t\t\tsummary: `Resume dispatched for ${args.database_id}. Poll get_database until status=available.`,\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'list_databases',\n\tcategory: 'databases',\n\tdescription: [\n\t\t'List managed databases (Postgres, Redis, MySQL, MariaDB, MongoDB) inside a project. Meilisearch and NATS are NOT databases — they are separate managed resources (search / queue) and do not appear here; see list_service_resources for what a service consumes.',\n\t\t'',\n\t\t'When to use: an agent needs to know what data stores exist in a project before connecting a service or running a migration. Pair with list_projects to discover project IDs.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - project_id: numeric project ID (from list_projects).',\n\t\t'',\n\t\t'Returns: { items: Database[] } — id, publicId, name, type, status, version, projectId, createdAt.',\n\t\t'',\n\t\t'Example: list_databases({ project_id: 12 }) → { items: [{ publicId: \"db_…\", type: \"postgres\", status: \"running\", … }] }',\n\t].join('\\n'),\n\tinput: {\n\t\tproject_id: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.positive()\n\t\t\t.describe('Numeric project ID (from list_projects).'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.databases.list(teamId, args.project_id);\n\t\tconst data = shapeList(response, 'databases', shapeDatabase);\n\t\tconst summary =\n\t\t\tdata.items.length === 0\n\t\t\t\t? `No databases in project ${args.project_id}.`\n\t\t\t\t: `Found ${data.items.length} database${data.items.length === 1 ? '' : 's'} in project ${args.project_id}.`;\n\t\treturn respond({ summary, data });\n\t},\n});\n\ndefineTool({\n\tname: 'update_database',\n\tcategory: 'databases',\n\tdescription: [\n\t\t'Update a managed database — rename, change plan tier (memory/CPU), or grow disk size.',\n\t\t'',\n\t\t'When to use: the user wants to resize a database (disk or plan), or rename it. Disk can only grow — shrinking is rejected because the underlying filesystem would orphan data.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - database_id: publicId of the database (e.g. \"db_…\").',\n\t\t' - name (optional): new database name.',\n\t\t' - plan (optional): \"free\" | \"micro\" | \"starter\" | \"standard\" | \"pro\" — changes memory tier.',\n\t\t' - disk_size_gb (optional): new disk size in GB (must be ≥ current).',\n\t\t'',\n\t\t'Returns: { database: Database } — the updated record.',\n\t\t'',\n\t\t'Example: update_database({ database_id: \"db_xyz\", disk_size_gb: 50 }) → { database: { diskSizeGb: 50, … } }',\n\t].join('\\n'),\n\tinput: {\n\t\tdatabase_id: z.string().describe('Database publicId (e.g. db_xyz).'),\n\t\tname: z.string().min(1).max(100).optional().describe('New database name.'),\n\t\tplan: z\n\t\t\t.enum(['free', 'micro', 'starter', 'standard', 'pro'])\n\t\t\t.optional()\n\t\t\t.describe('Plan tier (memory/CPU).'),\n\t\tdisk_size_gb: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(1)\n\t\t\t.max(1024)\n\t\t\t.optional()\n\t\t\t.describe('New disk size in GB. Must be ≥ current.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst input: {\n\t\t\tname?: string;\n\t\t\tplan?: 'free' | 'micro' | 'starter' | 'standard' | 'pro';\n\t\t\tdiskSizeGb?: number;\n\t\t} = {};\n\t\tif (args.name !== undefined) input.name = args.name;\n\t\tif (args.plan !== undefined) input.plan = args.plan;\n\t\tif (args.disk_size_gb !== undefined) input.diskSizeGb = args.disk_size_gb;\n\t\tif (Object.keys(input).length === 0) {\n\t\t\treturn respond({ summary: 'No fields to update.', data: {} });\n\t\t}\n\t\tconst response = await ctx.hoststack.databases.update(teamId, args.database_id, input);\n\t\tconst data = { database: shapeDatabase(response.database) };\n\t\tconst fields = Object.keys(input).join(', ');\n\t\treturn respond({ summary: `Updated ${fields} on database ${args.database_id}.`, data });\n\t},\n});\n\ndefineTool({\n\tname: 'get_database',\n\tcategory: 'databases',\n\tdescription: [\n\t\t'Fetch a single managed database by ID. Includes status, type, version, and project link.',\n\t\t'',\n\t\t'When to use: confirming the version of a database before generating connection strings, or checking the status of a recently created database. To retrieve the actual connection credentials, use the dashboard — credentials are intentionally not exposed via this MCP tool.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - database_id: publicId of the database (e.g. \"db_…\").',\n\t\t'',\n\t\t'Returns: { database: Database }.',\n\t\t'',\n\t\t'Example: get_database({ database_id: \"db_xyz\" }) → { database: { type: \"postgres\", version: \"16\", status: \"running\", … } }',\n\t].join('\\n'),\n\tinput: {\n\t\tdatabase_id: z.string().describe('Database publicId (e.g. db_xyz).'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.databases.get(teamId, args.database_id);\n\t\tconst data = { database: shapeDatabase(response.database) };\n\t\tconst type =\n\t\t\tdata.database && 'type' in data.database\n\t\t\t\t? (data.database as { type: string }).type\n\t\t\t\t: 'unknown';\n\t\tconst status =\n\t\t\tdata.database && 'status' in data.database\n\t\t\t\t? (data.database as { status: string }).status\n\t\t\t\t: 'unknown';\n\t\treturn respond({ summary: `Database ${args.database_id} (${type}) is ${status}.`, data });\n\t},\n});\n\ndefineTool({\n\tname: 'query_database',\n\tcategory: 'databases',\n\tdescription: [\n\t\t'Run a single READ-ONLY SQL statement against a managed Postgres database.',\n\t\t'',\n\t\t'When to use: ad-hoc data inspection — checking row counts, sampling rows, debugging a constraint. For schema changes or seed data, ship a Drizzle migration instead.',\n\t\t'',\n\t\t'Safety model (server-side, not bypassable):',\n\t\t' - Wrapped in `BEGIN TRANSACTION READ ONLY` — INSERT/UPDATE/DELETE/DDL fail with a clear \"cannot execute … in a read-only transaction\" error.',\n\t\t' - 30s statement_timeout — runaway queries are killed.',\n\t\t' - 1000-row cap; result CSV is also size-capped at 1 MiB. Both surface via `truncated: true`.',\n\t\t' - Postgres engine only (v1). Other engines return a 400.',\n\t\t' - Single statement only; no embedded `;` and no psql meta-commands (`\\\\…`).',\n\t\t' - Every call is audit-logged (success or failure) as `database.query` so an admin can answer \"who queried this DB, when, with what SQL\".',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - database_id: publicId of the postgres database (e.g. \"db_…\"). Resolved via the SDK\\'s publicId helper.',\n\t\t' - sql: a single SELECT/WITH/SHOW/EXPLAIN statement, no trailing `;`. Max 16 KiB.',\n\t\t'',\n\t\t'Returns: { columns: string[], rows: string[][], rowCount: number, truncated: boolean, durationMs: number }. Values come back as strings (psql --csv); cast on the caller side when needed.',\n\t\t'',\n\t\t'Example:',\n\t\t' - query_database({ database_id: \"db_abc\", sql: \"SELECT count(*) FROM users\" }) → { columns: [\"count\"], rows: [[\"1234\"]], … }',\n\t\t' - query_database({ database_id: \"db_abc\", sql: \"SELECT id, email FROM users WHERE created_at > now() - interval \\'1 day\\' LIMIT 50\" })',\n\t].join('\\n'),\n\tinput: {\n\t\tdatabase_id: z.string().describe('Database publicId (e.g. db_abc).'),\n\t\tsql: z\n\t\t\t.string()\n\t\t\t.min(1)\n\t\t\t.max(16_384)\n\t\t\t.describe(\n\t\t\t\t'A single READ-ONLY SQL statement. No trailing `;`, no embedded `;`, no psql meta-commands.',\n\t\t\t),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst result = await ctx.hoststack.databases.query(teamId, args.database_id, args.sql);\n\t\tconst summary = result.truncated\n\t\t\t? `Query returned ${result.rowCount} row${result.rowCount === 1 ? '' : 's'} (truncated; raise LIMIT if needed) in ${result.durationMs}ms.`\n\t\t\t: `Query returned ${result.rowCount} row${result.rowCount === 1 ? '' : 's'} in ${result.durationMs}ms.`;\n\t\treturn respond({ summary, data: result });\n\t},\n});\n\ndefineTool({\n\tname: 'upgrade_database_to_ha',\n\tcategory: 'databases',\n\tdescription: [\n\t\t'Trigger a single-node → HA migration on a managed Postgres database. The agent runs pg_dump → bootstrap 3-node Patroni cluster (1 leader + 2 sync replicas + HAProxy) → pg_restore. Customer connections to this database are briefly read-only during the cutover; the standalone container stays running (read-only) for 24 h as a rollback target before automatic decommission.',\n\t\t'',\n\t\t'Requires: PATRONI_ENABLED env on the HostStack deployment + `teams.ha_beta = true` on the calling team. Returns 403 otherwise. Returns 400 if the database is already HA, not Postgres, not on the standard or pro tier, or has a pg_database_size over 1 GB (in-memory archive transfer cap).',\n\t\t'',\n\t\t'When to use: a customer needs failover protection on an existing standalone Postgres. For new databases on an HA-eligible team, just call create_database — the cluster path is automatic.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - database_id: publicId of the postgres database to upgrade.',\n\t\t'',\n\t\t'Returns: 202 Accepted. The upgrade is async — poll get_database until `pg_engine_type === \"patroni\"` and `status === \"available\"` to confirm completion. The Cluster tab on the dashboard mirrors the same data.',\n\t\t'',\n\t\t'Example: upgrade_database_to_ha({ database_id: \"db_abc\" }) → { ok: true }',\n\t].join('\\n'),\n\tinput: {\n\t\tdatabase_id: z.string().describe('Database publicId (e.g. db_abc) to upgrade to HA.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tawait ctx.hoststack.databases.upgradeToHa(teamId, args.database_id);\n\t\treturn respond({\n\t\t\tsummary: `HA migration started for ${args.database_id}. Poll get_database until pg_engine_type=patroni to confirm.`,\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'upgrade_database_version',\n\tcategory: 'databases',\n\tdescription: [\n\t\t'Upgrade a standalone managed database (postgres or redis) to a newer engine version in place. The agent dumps the live data, recreates the container at the target version under the same DNS name, and restores — so connection URLs stay valid and services do NOT need a redeploy. The database is briefly unavailable (seconds) during the cut-over; on any failure the original container is rolled back automatically.',\n\t\t'',\n\t\t'`version` must be a supported, strictly-newer version for the engine (postgres: 18/17/16/15, redis: 8/7/6). Downgrades are rejected. Returns 400 for unsupported engines (mysql/mariadb/mongodb upgrades are not implemented yet), for HA (Patroni) clusters, or if the database is not currently `available`.',\n\t\t'',\n\t\t'When to use: a customer wants to move to a newer Postgres/Redis major version. Check get_database first — the `version` field shows the current version; compare against the engine latest.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - database_id: publicId of the postgres/redis database to upgrade.',\n\t\t' - version: target engine version (e.g. \"18\" for postgres, \"8\" for redis).',\n\t\t'',\n\t\t'Returns: 202 Accepted. The upgrade is async — poll get_database until `version` matches the target and `status === \"available\"` to confirm.',\n\t\t'',\n\t\t'Example: upgrade_database_version({ database_id: \"db_abc\", version: \"18\" }) → { ok: true }',\n\t].join('\\n'),\n\tinput: {\n\t\tdatabase_id: z.string().describe('Database publicId (e.g. db_abc) to upgrade.'),\n\t\tversion: z\n\t\t\t.string()\n\t\t\t.describe('Target engine version, e.g. \"18\" for postgres or \"8\" for redis.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tawait ctx.hoststack.databases.upgradeVersion(teamId, args.database_id, args.version);\n\t\treturn respond({\n\t\t\tsummary: `Version upgrade to v${args.version} started for ${args.database_id}. Poll get_database until version=${args.version} and status=available to confirm.`,\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'restart_database',\n\tcategory: 'databases',\n\tdescription: [\n\t\t'Restart a managed database in place — `docker restart` of its container: same container, same volume, same connection URL. The database is briefly unavailable (a few seconds) while it bounces; no redeploy of connected services is needed.',\n\t\t'',\n\t\t'When to use: a database is wedged (stuck connection, needs to reload a config change, leaking memory) and a clean bounce is the fix — the database-equivalent of restarting a service.',\n\t\t'',\n\t\t'The database must be `available`. A suspended database has no running container (resume it first → 409); a creating/migrating one is mid-operation (409). For a Patroni HA cluster this bounces the current leader, which triggers a normal failover while replicas keep serving reads.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - database_id: publicId of the database to restart.',\n\t\t'',\n\t\t'Returns: { ok: true } once the restart command reaches an agent (dispatched optimistically, like a service restart). Poll get_service_logs / get_database to confirm it came back.',\n\t\t'',\n\t\t'Example: restart_database({ database_id: \"db_abc\" }) → { ok: true }',\n\t].join('\\n'),\n\tinput: {\n\t\tdatabase_id: z.string().describe('Database publicId (e.g. db_abc) to restart.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tawait ctx.hoststack.databases.restart(teamId, args.database_id);\n\t\treturn respond({\n\t\t\tsummary: `Restart dispatched for ${args.database_id}. It will be briefly unavailable while the container bounces.`,\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'get_database_cluster',\n\tcategory: 'databases',\n\tdescription: [\n\t\t'Get the live + retired cluster members and failover history for a Patroni HA Postgres database.',\n\t\t'',\n\t\t'When to use: confirm an HA cluster is healthy (1 leader + 2 sync replicas + 1 proxy + 1 etcd live) or investigate a failover that fired. For standalone databases this returns 400 — call get_database first if you need to branch.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - database_id: publicId of the postgres database (must be HA).',\n\t\t'',\n\t\t\"Returns: { members: { id, memberRole: 'etcd' | 'primary-candidate' | 'replica' | 'proxy', containerId, workerHostId, joinedAt, leftAt }[], failovers: { id, createdAt, oldLeader, newLeader }[] }.\",\n\t\t'',\n\t\t'Example: get_database_cluster({ database_id: \"db_abc\" }) → { members: [...5 live rows], failovers: [] }',\n\t].join('\\n'),\n\tinput: {\n\t\tdatabase_id: z.string().describe('Database publicId for the HA cluster.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst result = await ctx.hoststack.databases.getCluster(teamId, args.database_id);\n\t\tconst live = result.members.filter((m) => m.leftAt === null);\n\t\tconst summary = `${live.length} live members (${live.map((m) => m.memberRole).join(', ')}); ${result.failovers.length} historical failovers.`;\n\t\treturn respond({ summary, data: result });\n\t},\n});\n","import type { Machine } from '@hoststack.dev/sdk';\nimport { z } from 'zod';\n\nimport { respond } from '../lib/respond.js';\nimport { shape, shapeList } from '../lib/shape.js';\nimport { defineTool, type ToolContext } from '../registry.js';\n\n/**\n * The `machine` input every \"create something\" tool accepts.\n *\n * A name rather than an id, because that is what the user says (\"put it on my\n * desktop\") and the numeric id is an internal one an agent has no other reason\n * to hold. A numeric id is still accepted for callers that already have one.\n */\nexport const machineInput = z\n\t.union([z.number().int().positive(), z.string()])\n\t.optional()\n\t.describe(\n\t\t'Run this on one of the team\\'s OWN enrolled machines instead of HostStack compute — the machine\\'s name (e.g. \"desktop\") or its numeric id. List them with list_machines. Omit for HostStack compute, which is the default and the right answer unless the user asked for their own hardware.',\n\t);\n\n/**\n * Resolve a machine name (or id) to the numeric `machineId` the API pins on.\n *\n * Fails loudly and with the list of real names rather than guessing. Placement\n * is pinned at creation and never changed afterwards — the container and its\n * disk live on that machine — so a wrong guess here is not a setting somebody\n * corrects later, it is a rebuild.\n */\nexport async function resolveMachineId(\n\tctx: ToolContext,\n\tteamId: number,\n\tmachine: number | string,\n): Promise<number> {\n\tconst { machines } = await ctx.hoststack.machines.list(teamId);\n\tif (machines.length === 0) {\n\t\tthrow new Error(\n\t\t\t'This team has no enrolled machines. Enrol one from the dashboard (Machines → Add machine) or with `hoststack machines add`, then run the install command it prints on that machine.',\n\t\t);\n\t}\n\n\tconst known = machines.map((m) => `\"${m.name}\" (id ${m.id})`).join(', ');\n\tif (typeof machine === 'number') {\n\t\tconst byId = machines.find((m) => m.id === machine);\n\t\tif (!byId) throw new Error(`No machine with id ${machine}. This team has: ${known}.`);\n\t\treturn byId.id;\n\t}\n\n\tconst needle = machine.trim().toLowerCase();\n\tconst matches = machines.filter(\n\t\t(m) => m.name.toLowerCase() === needle || m.hostname.toLowerCase() === needle,\n\t);\n\tif (matches.length === 0) {\n\t\tthrow new Error(`No machine named \"${machine}\". This team has: ${known}.`);\n\t}\n\t// Names are not unique — two machines can both be called \"desktop\". Refuse\n\t// rather than pick one, because the two are different physical computers and\n\t// the work cannot be moved between them afterwards.\n\tif (matches.length > 1) {\n\t\tthrow new Error(\n\t\t\t`More than one machine is named \"${machine}\" (ids ${matches.map((m) => m.id).join(', ')}). Pass the numeric id.`,\n\t\t);\n\t}\n\treturn matches[0]!.id;\n}\n\n/** A machine's state in one sentence, for the summary line. */\nfunction describeMachine(m: Machine): string {\n\tif (m.kind === 'infra') {\n\t\tconst state = m.status === 'active' ? 'online' : 'offline';\n\t\treturn `${m.name}: infrastructure machine for project ${m.infraProjectId}, ${state} (only that project's services can be pinned to it)`;\n\t}\n\tif (!m.enrolled) return `${m.name}: registered but never paired`;\n\tif (m.status !== 'active') return `${m.name}: offline`;\n\tif (m.agentBuild === 'from-source') return `${m.name}: online, agent running from source`;\n\tif (m.agentBuild === 'unknown') return `${m.name}: online, agent too old to report its build`;\n\treturn `${m.name}: online`;\n}\n\ndefineTool({\n\tname: 'list_machines',\n\tcategory: 'machines',\n\tdescription: [\n\t\t\"List the team's OWN enrolled machines — hardware they supply (a spare desktop, a home server, a VPS they already pay for) that HostStack runs work on. These are NOT HostStack's servers; you cannot enrol, resize or reboot them from here.\",\n\t\t'',\n\t\t'When to use: before creating a service, database or dev box that the user wants on their own hardware (pass the machine name to create_service / create_database / create_standalone_dev_environment), or to explain why something pinned to a machine is down — a machine that is switched off takes everything on it with it.',\n\t\t'',\n\t\t'Inputs: none.',\n\t\t'',\n\t\t'Returns: { items: Machine[] } — each entry has id (pass this as `machine`), name, status (active|offline|provisioning), enrolled, agentBuild (current|behind|from-source|unknown), agentVersion, lastHeartbeatAt, totalMemoryMb, totalCpuCores, and workloads { devBoxes, services, databases }.',\n\t\t'',\n\t\t'Notes: a machine is only reachable while its owner has it switched on, and a database pinned to one is reachable ONLY from that same machine — put the app that queries it there too. Enrolment needs a person at the machine to run an installer, so there is no tool for it: point the user at the dashboard (Machines → Add machine) or `hoststack machines add`.',\n\t\t'',\n\t\t'Example: list_machines() → { items: [{ id: 12452, name: \"desktop\", status: \"active\", workloads: { devBoxes: 8, services: 0, databases: 2 } }] }',\n\t].join('\\n'),\n\tinput: {},\n\thandler: async (_args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.machines.list(teamId);\n\t\tconst data = shapeList(response, 'machines', shape);\n\t\tconst summary =\n\t\t\tresponse.machines.length === 0\n\t\t\t\t? 'This team has no enrolled machines. Everything runs on HostStack compute.'\n\t\t\t\t: `${response.machines.length} machine${response.machines.length === 1 ? '' : 's'} — ${response.machines.map(describeMachine).join('; ')}.`;\n\t\treturn respond({ summary, data });\n\t},\n});\n\ndefineTool({\n\tname: 'get_machine',\n\tcategory: 'machines',\n\tdescription: [\n\t\t\"One of the team's own enrolled machines, with what is actually running on it — rather than the counts list_machines carries.\",\n\t\t'',\n\t\t'When to use: before advising the user to switch a machine off, move work off it, or remove it — `running` is the list of things that go away with the machine. Also the fastest answer to \"what is on my desktop?\".',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - machine: the machine name (e.g. \"desktop\") or its numeric id.',\n\t\t'',\n\t\t'Returns: { machine: Machine, running: Workload[] } — each workload has kind (\"dev_box\" | \"service\" | \"database\"), name, status and projectId. The kinds differ in consequence: a dev box waits for its machine to come back, a service is down while it is off, and a database takes down everything reading from it.',\n\t\t'',\n\t\t'Example: get_machine({ machine: \"desktop\" }) → { machine: { name: \"desktop\", status: \"active\" }, running: [{ kind: \"database\", name: \"postgres-58\" }] }',\n\t].join('\\n'),\n\tinput: {\n\t\tmachine: z\n\t\t\t.union([z.number().int().positive(), z.string()])\n\t\t\t.describe('Machine name (e.g. \"desktop\") or numeric id. List them with list_machines.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst machineId = await resolveMachineId(ctx, teamId, args.machine);\n\t\tconst response = await ctx.hoststack.machines.get(teamId, machineId);\n\t\tconst data = {\n\t\t\tmachine: shape(response.machine),\n\t\t\trunning: shapeList({ running: response.running }, 'running', shape).items,\n\t\t};\n\t\tconst running = response.running ?? [];\n\t\tconst summary =\n\t\t\trunning.length === 0\n\t\t\t\t? `${describeMachine(response.machine)}. Nothing is running on it.`\n\t\t\t\t: `${describeMachine(response.machine)}. Running ${running.length} thing${running.length === 1 ? '' : 's'}: ${running.map((w) => `${w.name} (${w.kind})`).join(', ')}.`;\n\t\treturn respond({ summary, data });\n\t},\n});\n","import { z } from 'zod';\n\nimport { defineTool } from '../registry.js';\nimport { respond } from '../lib/respond.js';\nimport { shape, shapeDeploy, shapeList } from '../lib/shape.js';\n\ndefineTool({\n\tname: 'list_deploys',\n\tcategory: 'deploys',\n\tdescription: [\n\t\t'List deploys for a service in reverse-chronological order (newest first).',\n\t\t'',\n\t\t'When to use: investigating a recent deploy outcome, finding the last successful deploy ID, comparing run-times, or just checking deploy history before triggering a new one.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service (e.g. \"svc_abc123\"). Use list_services to find it.',\n\t\t'',\n\t\t'Returns: { items: Deploy[] } — each deploy includes id, publicId, status (pending|building|deploying|live|failed|cancelled), commitHash (the resolved commit SHA; the key is OMITTED entirely when the deploy recorded none, so a missing key means \"no commit\", not \"wrong key\"), commitMessage, branch, triggeredBy, startedAt, finishedAt, imageBuildMs (docker build / image-pull only; null on skip-build redeploys — v89), containerBootMs (deploying → live wall-clock; null on builds that failed before container start — v89), buildDurationMs (legacy alias of imageBuildMs kept for back-compat), totalDurationMs (full deploy wall-clock = finishedAt − startedAt). Use imageBuildMs + containerBootMs together to tell \"build is slow\" apart from \"boot is slow\".',\n\t\t'',\n\t\t'Example: list_deploys({ service_id: \"svc_abc\" }) → { items: [{ publicId: \"dpl_…\", status: \"live\", commitMessage: \"Fix login\", … }, …] }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId (e.g. svc_abc123).'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.deploys.list(teamId, args.service_id);\n\t\t// Deploys list is paginated and returns `{ data, page, ... }` — not the\n\t\t// bare `{ deploys: [] }` shape every other list endpoint uses.\n\t\tconst data = shapeList(response, 'data', shapeDeploy);\n\t\tconst summary =\n\t\t\tdata.items.length === 0\n\t\t\t\t? `No deploys yet for service ${args.service_id}.`\n\t\t\t\t: `Found ${data.items.length} deploy${data.items.length === 1 ? '' : 's'} for service ${args.service_id} (page ${response.page} of ${response.totalPages}, ${response.total} total).`;\n\t\treturn respond({ summary, data });\n\t},\n});\n\ndefineTool({\n\tname: 'get_deploy',\n\tcategory: 'deploys',\n\tdescription: [\n\t\t'Fetch a single deploy by ID, including its current status, commit metadata, and timestamps.',\n\t\t'',\n\t\t\"When to use: drilling into the details of a specific deploy after list_deploys, polling a build's status, or grabbing the commit SHA so you can correlate logs with code.\",\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t' - deploy_id: publicId of the deploy (e.g. \"dpl_…\").',\n\t\t'',\n\t\t'Returns: { deploy: Deploy } — full deploy record (status, commitHash (omitted when the deploy recorded no commit), commitMessage, branch, imageBuildMs + containerBootMs (v89 split timings; legacy buildDurationMs preserved as alias), totalDurationMs, finishedAt, etc).',\n\t\t'',\n\t\t'Example: get_deploy({ service_id: \"svc_abc\", deploy_id: \"dpl_xyz\" }) → { deploy: { status: \"live\", … } }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t\tdeploy_id: z.string().describe('Deploy publicId.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.deploys.get(teamId, args.service_id, args.deploy_id);\n\t\tconst data = { deploy: shapeDeploy(response.deploy) };\n\t\tconst status =\n\t\t\tdata.deploy && typeof data.deploy === 'object' && 'status' in data.deploy\n\t\t\t\t? (data.deploy as { status: string }).status\n\t\t\t\t: 'unknown';\n\t\treturn respond({ summary: `Deploy ${args.deploy_id} is ${status}.`, data });\n\t},\n});\n\ndefineTool({\n\tname: 'trigger_deploy',\n\tcategory: 'deploys',\n\tdescription: [\n\t\t'Kick off a new deploy. Defaults to the latest commit on the service branch; pass commit_hash or branch to deploy something else.',\n\t\t'',\n\t\t'When to use: the user explicitly asks to deploy (\"ship it\", \"redeploy\", \"kick off a new build\"). For services with auto-deploy enabled, a fresh git push already triggers a deploy automatically — only call this for manual triggers or after a config change.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t' - commit_hash (optional): build a specific commit instead of HEAD on the configured branch.',\n\t\t' - branch (optional): build the tip of a non-default branch.',\n\t\t'',\n\t\t'Returns: { deploy: Deploy } — the new deploy record. Status will start as \"pending\" then transition through \"building\" → \"deploying\" → \"live\" or \"failed\".',\n\t\t'',\n\t\t'Example: trigger_deploy({ service_id: \"svc_abc\" }) → { deploy: { publicId: \"dpl_…\", status: \"pending\", … } }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t\tcommit_hash: z\n\t\t\t.string()\n\t\t\t.max(40)\n\t\t\t.optional()\n\t\t\t.describe('Specific commit to build (default: branch HEAD).'),\n\t\tbranch: z\n\t\t\t.string()\n\t\t\t.max(200)\n\t\t\t.optional()\n\t\t\t.describe('Branch to build (default: service configured branch).'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst input: { commitHash?: string; branch?: string } = {};\n\t\tif (args.commit_hash !== undefined) input.commitHash = args.commit_hash;\n\t\tif (args.branch !== undefined) input.branch = args.branch;\n\t\tconst response = await ctx.hoststack.deploys.trigger(teamId, args.service_id, input);\n\t\tconst data = { deploy: shapeDeploy(response.deploy) };\n\t\tconst publicId =\n\t\t\tdata.deploy && 'publicId' in data.deploy\n\t\t\t\t? (data.deploy as { publicId: string }).publicId\n\t\t\t\t: 'unknown';\n\t\treturn respond({\n\t\t\tsummary: `Triggered deploy ${publicId} on service ${args.service_id}.`,\n\t\t\tdata,\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'cancel_deploy',\n\tcategory: 'deploys',\n\tdescription: [\n\t\t'Cancel a running deploy. Stops the build/deploy pipeline mid-flight; the previously live revision keeps serving traffic.',\n\t\t'',\n\t\t'When to use: the user notices a bad commit was pushed and wants to abort before it lands, or a build is hanging. Also the right call for a deploy stuck in `deploying` because the new container is crash-looping against its health check — that state is cancellable and cancelling it frees the service (and the build queue) immediately instead of waiting out the grace period. Has no effect on already-finished deploys.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t' - deploy_id: publicId of the deploy to cancel.',\n\t\t'',\n\t\t'Returns: { ok: true }.',\n\t\t'',\n\t\t'Example: cancel_deploy({ service_id: \"svc_abc\", deploy_id: \"dpl_xyz\" }) → { ok: true }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t\tdeploy_id: z.string().describe('Deploy publicId.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tawait ctx.hoststack.deploys.cancel(teamId, args.service_id, args.deploy_id);\n\t\treturn respond({\n\t\t\tsummary: `Cancelled deploy ${args.deploy_id} on service ${args.service_id}.`,\n\t\t\tdata: { ok: true },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'get_deploy_logs',\n\tcategory: 'deploys',\n\tdescription: [\n\t\t'Fetch the build/deploy logs for a single deploy. Returns the entire log buffer the agent can then search for errors.',\n\t\t'',\n\t\t'When to use: a deploy failed and you need to read the build output to diagnose. Pair with get_deploy to find a failed deploy_id, then call this. For runtime (post-deploy) logs of the running container, use get_service_logs instead.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t' - deploy_id: publicId of the deploy.',\n\t\t'',\n\t\t'Returns: { logs: string, lineCount: number, nextAfterId: number | null } — flattened build output joined with newlines, line count, and a cursor for pagination (pass back as after_id to fetch the next page).',\n\t\t'',\n\t\t'Example: get_deploy_logs({ service_id: \"svc_abc\", deploy_id: \"dpl_xyz\" }) → { logs: \"Building…\\\\nStep 1/8…\\\\n…\", lineCount: 42, nextAfterId: 1234 }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t\tdeploy_id: z.string().describe('Deploy publicId.'),\n\t\tafter_id: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.optional()\n\t\t\t.describe('Pagination cursor from a previous response’s nextAfterId.'),\n\t\tlimit: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(1)\n\t\t\t.max(5000)\n\t\t\t.optional()\n\t\t\t.describe('Max log entries to fetch (default 500, hard cap 5000).'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.deploys.getLogs(\n\t\t\tteamId,\n\t\t\targs.service_id,\n\t\t\targs.deploy_id,\n\t\t\t{\n\t\t\t\t...(args.after_id !== undefined ? { afterId: args.after_id } : {}),\n\t\t\t\t...(args.limit !== undefined ? { limit: args.limit } : {}),\n\t\t\t},\n\t\t);\n\t\tconst entries = response.logs;\n\t\tconst logs = entries.map((e) => e.message).join('\\n');\n\t\treturn respond({\n\t\t\tsummary: `Fetched ${entries.length} log line${entries.length === 1 ? '' : 's'} for deploy ${args.deploy_id}.`,\n\t\t\tdata: { logs, lineCount: entries.length, nextAfterId: response.nextAfterId },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'diagnose_deploy',\n\tcategory: 'deploys',\n\tdescription: [\n\t\t\"One-shot diagnostic bundle for a deploy: full deploy record + tail of build logs + tail of runtime logs starting at the deploy's start time. Lets you investigate any deploy failure mode (build-time failure, health-check timeout, post-deploy runtime crash) without juggling two log endpoints and two IDs.\",\n\t\t'',\n\t\t'When to use: a deploy is failed/superseded/stuck and you want the whole story in one call. For just the build output, use get_deploy_logs. For just runtime logs of the running service, use get_service_logs.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t' - deploy_id: publicId of the deploy to diagnose.',\n\t\t' - build_log_lines (optional): how many trailing build log lines to include. Default 200, max 2000.',\n\t\t' - runtime_log_lines (optional): how many trailing runtime log lines (since the deploy started) to include. Default 100, max 1000. Set to 0 to skip runtime logs (saves a round-trip when investigating a pure build-time failure).',\n\t\t'',\n\t\t'Returns: { deploy, build: { logs, lineCount }, runtime: { logs, lineCount } } — runtime is omitted when runtime_log_lines is 0 or the deploy never started a container.',\n\t\t'',\n\t\t'Example: diagnose_deploy({ service_id: \"svc_abc\", deploy_id: \"dpl_xyz\" }) → all three sections in one call.',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t\tdeploy_id: z.string().describe('Deploy publicId.'),\n\t\tbuild_log_lines: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(1)\n\t\t\t.max(2000)\n\t\t\t.optional()\n\t\t\t.describe('Trailing build log lines. Default 200, max 2000.'),\n\t\truntime_log_lines: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(0)\n\t\t\t.max(1000)\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t'Trailing runtime log lines since deploy start. Default 100, max 1000. 0 = skip.',\n\t\t\t),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst buildLimit = args.build_log_lines ?? 200;\n\t\tconst runtimeLimit = args.runtime_log_lines ?? 100;\n\n\t\t// Pull deploy + build logs in parallel — build logs don't depend on\n\t\t// the deploy record's metadata. Runtime logs need the deploy's\n\t\t// startedAt for the `since` window so they wait for the deploy fetch.\n\t\tconst [deployResp, buildResp] = await Promise.all([\n\t\t\tctx.hoststack.deploys.get(teamId, args.service_id, args.deploy_id),\n\t\t\tctx.hoststack.deploys.getLogs(teamId, args.service_id, args.deploy_id, {\n\t\t\t\tlimit: buildLimit,\n\t\t\t}),\n\t\t]);\n\n\t\tconst buildEntries = buildResp.logs;\n\t\tconst buildLogs = buildEntries.map((e) => e.message).join('\\n');\n\n\t\tconst deployShape = shapeDeploy(deployResp.deploy);\n\t\tconst deployStatus =\n\t\t\tdeployShape && 'status' in deployShape\n\t\t\t\t? (deployShape as { status: string }).status\n\t\t\t\t: 'unknown';\n\t\tconst startedAtRaw =\n\t\t\tdeployShape && 'startedAt' in deployShape\n\t\t\t\t? (deployShape as { startedAt: unknown }).startedAt\n\t\t\t\t: undefined;\n\t\tconst startedAt = typeof startedAtRaw === 'string' ? startedAtRaw : undefined;\n\n\t\tconst data: Record<string, unknown> = {\n\t\t\tdeploy: deployShape,\n\t\t\tbuild: { logs: buildLogs, lineCount: buildEntries.length },\n\t\t};\n\n\t\t// Skip the runtime tail when caller asked us to OR when the deploy\n\t\t// never reached the container-start phase (build-time failure).\n\t\t// `startedAt` is set by the API when the deploy row is created, so\n\t\t// it's almost always present; the more reliable container-started\n\t\t// signal is whether status ever passed the build phase.\n\t\tconst hadContainer = deployStatus !== 'pending' && deployStatus !== 'building';\n\t\tif (runtimeLimit > 0 && hadContainer) {\n\t\t\tconst runtimeResp = await ctx.hoststack.services.getRuntimeLogs(\n\t\t\t\tteamId,\n\t\t\t\targs.service_id,\n\t\t\t\t{\n\t\t\t\t\tlines: runtimeLimit,\n\t\t\t\t\t...(startedAt ? { since: startedAt } : {}),\n\t\t\t\t},\n\t\t\t);\n\t\t\tif ('count' in runtimeResp) {\n\t\t\t\t// getRuntimeLogs without countOnly should never return this\n\t\t\t\t// shape; defensive branch for future-proofing.\n\t\t\t\tdata['runtime'] = { logs: '', lineCount: runtimeResp.count };\n\t\t\t} else {\n\t\t\t\tconst runtimeEntries = Array.isArray(runtimeResp.logs)\n\t\t\t\t\t? runtimeResp.logs\n\t\t\t\t\t: typeof runtimeResp.logs === 'string'\n\t\t\t\t\t\t? runtimeResp.logs.split('\\n').map((line) => ({ message: line }))\n\t\t\t\t\t\t: [];\n\t\t\t\tconst runtimeText = runtimeEntries\n\t\t\t\t\t.map((e) =>\n\t\t\t\t\t\ttypeof e === 'object' && 'message' in e ? String(e.message) : String(e),\n\t\t\t\t\t)\n\t\t\t\t\t.join('\\n');\n\t\t\t\tdata['runtime'] = {\n\t\t\t\t\tlogs: runtimeText,\n\t\t\t\t\tlineCount: runtimeEntries.length,\n\t\t\t\t\tsinceWindow: startedAt ?? null,\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\t// Surface anything that might explain a failure — these field names\n\t\t// vary slightly across deploy phases (build, deploy, healthcheck) so\n\t\t// we look at the deploy shape directly rather than hard-coding.\n\t\tconst failureSummary = shape({\n\t\t\tstatus: deployStatus,\n\t\t\terrorMessage:\n\t\t\t\tdeployShape && 'errorMessage' in deployShape\n\t\t\t\t\t? (deployShape as { errorMessage: unknown }).errorMessage\n\t\t\t\t\t: undefined,\n\t\t});\n\t\tdata['summary'] = failureSummary;\n\n\t\treturn respond({\n\t\t\tsummary: `Diagnostic bundle for deploy ${args.deploy_id} (${deployStatus}): ${buildEntries.length} build log line${buildEntries.length === 1 ? '' : 's'}${runtimeLimit > 0 && hadContainer ? ', runtime log tail included' : ''}.`,\n\t\t\tdata,\n\t\t});\n\t},\n});\n","import { z } from 'zod';\nimport type { DnsRecord, DnsRecordType, DnsZone, HostStack } from '@hoststack.dev/sdk';\n\nimport { defineTool } from '../registry.js';\nimport { respond, respondError } from '../lib/respond.js';\nimport { shape } from '../lib/shape.js';\n\n/**\n * DNS-zone + record management. These tools delegate to the SDK's\n * `DnsResource` (`ctx.hoststack.dns`), which wraps the same HTTP routes\n * the dashboard uses — `/api/dns-zones/:teamId/...` — so tenancy, audit\n * logging, PowerDNS sync, and idempotency behavior all match the\n * dashboard exactly. We never bypass the service layer.\n *\n * The HTTP-domain tools (list_domains, add_domain, verify_domain,\n * remove_domain) are about binding a hostname to a service for HTTP\n * routing + Let's Encrypt; they don't touch the underlying DNS zone.\n * Use these tools for actual record CRUD (TXT for verification, A/AAAA\n * for custom routing, MX for email, etc.).\n */\n// Source of truth for record types is `@hoststack/shared`'s\n// `dnsRecordTypeSchema`, surfaced through the SDK's `DnsRecordType`. This array\n// drives the zod input shape (a value, so it can't be the bare type); the\n// `satisfies` clause makes the build fail if it ever drifts from the SDK union.\nconst DNS_RECORD_TYPES = [\n\t'A',\n\t'AAAA',\n\t'CNAME',\n\t'MX',\n\t'TXT',\n\t'NS',\n\t'SRV',\n\t'CAA',\n\t'ALIAS',\n] as const satisfies readonly DnsRecordType[];\n\n// Exhaustiveness guard the other direction: every DnsRecordType must appear in\n// the array above. If the SDK adds a record type, this errors until it's added.\nconst _dnsTypesExhaustive: Record<DnsRecordType, true> = {\n\tA: true,\n\tAAAA: true,\n\tCNAME: true,\n\tMX: true,\n\tTXT: true,\n\tNS: true,\n\tSRV: true,\n\tCAA: true,\n\tALIAS: true,\n};\nvoid _dnsTypesExhaustive;\n\n/**\n * Resolve `{ zone_id?, domain? }` to the zone's `publicId`. Accepts\n * either the zone publicId directly or a domain name (apex or\n * subdomain) and walks parent labels to find the longest-matching\n * hosted zone, same suffix-match semantics as\n * `DnsService.findHostedZoneForDomain` (apps/api/src/services/dns.service.ts:193).\n *\n * Returns the zone's publicId so the caller can address subsequent\n * routes by it, plus a human-readable hint about which zone matched.\n */\nasync function resolveZonePublicId(\n\thoststack: HostStack,\n\tteamId: number,\n\tinput: { zone_id?: string; domain?: string },\n): Promise<{ publicId: string; domainName: string }> {\n\tif (input.zone_id) {\n\t\tconst { zones } = await hoststack.dns.listZones(teamId);\n\t\tconst match = zones.find((z) => z.publicId === input.zone_id);\n\t\tif (!match) {\n\t\t\tthrow new Error(`Zone ${input.zone_id} not found on this team.`);\n\t\t}\n\t\treturn { publicId: match.publicId, domainName: match.domainName };\n\t}\n\tif (!input.domain) {\n\t\tthrow new Error('Provide either zone_id or domain.');\n\t}\n\tconst { zones } = await hoststack.dns.listZones(teamId);\n\tconst fqdn = input.domain.toLowerCase().replace(/\\.$/, '');\n\tconst labels = fqdn.split('.');\n\tfor (let i = 0; i < labels.length - 1; i++) {\n\t\tconst candidate = labels.slice(i).join('.');\n\t\tconst match = zones.find((z) => z.domainName.toLowerCase() === candidate);\n\t\tif (match && match.status !== 'deleting') {\n\t\t\treturn { publicId: match.publicId, domainName: match.domainName };\n\t\t}\n\t}\n\tthrow new Error(\n\t\t`No hosted zone matches ${input.domain}. Create the zone in the dashboard (Domains → DNS) first, then retry.`,\n\t);\n}\n\ndefineTool({\n\tname: 'list_dns_zones',\n\tcategory: 'dns',\n\tdescription: [\n\t\t\"List authoritative DNS zones the team owns on HostStack's PowerDNS infrastructure. Each zone is the apex domain (e.g. example.com) under which DNS records live.\",\n\t\t'',\n\t\t'When to use: discover which apex domains support record management before calling create_dns_record / list_dns_records. The dashboard equivalent is Domains → DNS.',\n\t\t'',\n\t\t'Returns: { items: Zone[] } — each zone exposes publicId, domainName, status (\"active\" | \"syncing\" | \"failed\" | \"deleting\"), nsRecords (the nameservers the parent registry must delegate to), provider, createdAt, plus the delegation reading below.',\n\t\t'',\n\t\t'READ delegationStatus, NOT status, to answer \"is this domain live?\". `status` describes the zone on OUR side — \"active\" means PowerDNS holds it and answers for it. `delegationStatus` describes whether the parent registry actually SENDS anyone here:',\n\t\t' - \"delegated\" — the registry names our nameservers; queries reach us.',\n\t\t' - \"foreign\" — the registry names somebody else (delegationObservedNs says who). The zone is authoritative for nobody: its records are correct and unreachable, public lookups return the OLD host or nothing, and no domain under it can pass verification or get a certificate. This is the state a half-finished migration sits in, and it looks identical to a healthy zone on every other field.',\n\t\t' - \"unknown\" — no usable answer (SERVFAIL/timeout/never checked). NOT the same as \"foreign\"; do not tell a user to change their registrar on the strength of it. Call check_dns_delegation for a live reading.',\n\t\t'',\n\t\t'delegationObservedNs is the NS set the parent publishes today, and delegationCheckedAt is when it was read (refreshed hourly). For an answer current to the second — e.g. just after a registrar change — call check_dns_delegation.',\n\t\t'',\n\t\t'Example: list_dns_zones() → { items: [{ publicId: \"dnz_abc\", domainName: \"micci.dk\", status: \"active\", nsRecords: [\"ns1.hoststack.dev\",\"ns2.hoststack.dev\"], delegationStatus: \"foreign\", delegationObservedNs: [\"docks15.rzone.de\",\"shades13.rzone.de\"] }] }',\n\t\t'',\n\t\t'Each zone ALSO carries what the registry says about the REGISTRATION, refreshed daily over RDAP: registrar, registryStatus (EPP codes such as clientHold / pendingTransfer / pendingDelete / redemptionPeriod), registryExpiresAt, registryCheckedAt and registryCheckOutcome (\"ok\" | \"not_found\" | \"unsupported\" | \"rate_limited\" | \"error\").',\n\t\t'',\n\t\t'This is a different question from delegation, and a zone can fail either one alone. Delegation asks whether the world is being sent here; these fields ask whether the domain is still going to be the customer\\'s next month. `status: \"active\"` answers neither — it describes our nameservers only.',\n\t\t'',\n\t\t'SILENCE IS NOT GOOD NEWS HERE. A null registryExpiresAt or registrar on a zone that HAS been checked means the registry publishes neither — DENIC publishes no expiry and no registrar for any .de — and registryCheckOutcome \"unsupported\" means the TLD has no RDAP service at all (there is none for .dk), so every field above it is unknowable rather than fine. Read registryCheckOutcome before drawing any conclusion from an empty field.',\n\t\t'',\n\t\t'HostStack is not the registrar for a domain whose DNS it merely hosts. Nothing in this MCP, the CLI or the dashboard can renew, transfer or un-hold a registration — if registryStatus or registryExpiresAt shows trouble, the action is at the registrar named in `registrar`.',\n\t\t'',\n\t\t'Example: list_dns_zones() → { items: [{ publicId: \"dnz_abc\", domainName: \"micci.dk\", status: \"active\", nsRecords: [\"ns1.hoststack.dev\",\"ns2.hoststack.dev\"], registrar: \"GoDaddy.com, LLC\", registryStatus: [\"clientTransferProhibited\"], registryExpiresAt: \"2027-03-04T00:00:00Z\", registryCheckOutcome: \"ok\" }] }',\n\t].join('\\n'),\n\tinput: {},\n\thandler: async (_args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.dns.listZones(teamId);\n\t\t// The `Array.isArray` guard is load-bearing, not defensive noise: an older\n\t\t// API build (or an error body that got this far) has no `zones` array,\n\t\t// and this tool must degrade to \"no zones\" rather than throw.\n\t\tconst zones = Array.isArray(response.zones) ? response.zones : [];\n\t\tconst items = zones.map(shape);\n\t\t// A zone the registry points elsewhere is the one thing in this list that\n\t\t// is wrong while looking right, so it goes in the SUMMARY rather than\n\t\t// waiting to be noticed in a field. Naming the incumbent nameservers is\n\t\t// what makes it an instruction instead of an alarm.\n\t\tconst foreign = zones.filter((z) => z.delegationStatus === 'foreign');\n\t\tconst foreignNote =\n\t\t\tforeign.length === 0\n\t\t\t\t? ''\n\t\t\t\t: ` WARNING: ${foreign.length} zone${foreign.length === 1 ? ' is' : 's are'} hosted here but still delegated elsewhere by the registry — ` +\n\t\t\t\t\tforeign\n\t\t\t\t\t\t.map(\n\t\t\t\t\t\t\t(z) =>\n\t\t\t\t\t\t\t\t`${z.domainName} → ${(z.delegationObservedNs ?? []).join(', ') || 'unknown nameservers'}`,\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.join('; ') +\n\t\t\t\t\t`. Records on ${foreign.length === 1 ? 'it' : 'them'} are correct but unreachable; the nameservers must be changed at the registrar.`;\n\t\tconst summary =\n\t\t\titems.length === 0\n\t\t\t\t? 'No DNS zones hosted on this team. Create one in the dashboard (Domains → DNS).'\n\t\t\t\t: `Found ${items.length} hosted DNS zone${items.length === 1 ? '' : 's'}.${foreignNote}`;\n\t\treturn respond({ summary, data: { items } });\n\t},\n});\n\ndefineTool({\n\tname: 'check_dns_delegation',\n\tcategory: 'dns',\n\tdescription: [\n\t\t\"Read, live, whether the parent registry actually delegates a zone's apex to HostStack — and store the reading on the zone.\",\n\t\t'',\n\t\t'WHAT THIS ANSWERS that nothing else does: a hosted zone can be `status: \"active\"`, DNSSEC-signed, full of correct records, and authoritative for NOBODY, because the registry still points the domain at the previous host. Public lookups then return the old host (or nothing) while a query against our own nameservers returns the right answer — and every other field we expose says healthy. This is the normal shape of a half-finished migration, and it is what to check FIRST when a zone looks perfect but the site is not live, a domain will not verify, or a certificate will not issue.',\n\t\t'',\n\t\t'When to use: right after someone changes nameservers at a registrar (list_dns_zones carries the hourly reading, which will be stale by minutes); before telling anyone a cutover is done; or to explain why verify_domain keeps returning pending.',\n\t\t'',\n\t\t'Provide EITHER zone_id (the zone publicId) OR domain (apex or subdomain — the longest-matching hosted apex wins).',\n\t\t'',\n\t\t'Returns: { zone, delegation: { status, observedNameservers, expectedNameservers, checkedAt } }.',\n\t\t' - status \"delegated\": at least one of our nameservers is in the parent\\'s NS set. Any, not all — a staged migration legitimately runs split for a while, and queries already reach us during it.',\n\t\t' - status \"foreign\": the parent answered and named somebody else. observedNameservers is who. The fix is at the REGISTRAR of the apex, never in our records; expect up to 48h of propagation after the change.',\n\t\t' - status \"unknown\": no usable answer (SERVFAIL, timeout, NXDOMAIN). This is NOT \"not delegated\" — never tell someone to re-point a registrar on the strength of it. Retry, or check the domain is registered at all.',\n\t\t'',\n\t\t'Note for `.dk` and other strict registries: they refuse the nameserver change until we already answer authoritatively for the domain, so a zone there is legitimately \"foreign\" for a while by design. That is the ordering the registry imposes, not a mistake.',\n\t\t'',\n\t\t'Example: check_dns_delegation({ domain: \"wohnwagen-wuckert.de\" }) → { delegation: { status: \"foreign\", observedNameservers: [\"docks15.rzone.de\",\"shades13.rzone.de\"], expectedNameservers: [\"ns1.hoststack.dev\",\"ns2.hoststack.dev\"] } }',\n\t].join('\\n'),\n\tinput: {\n\t\tzone_id: z.string().optional().describe('Zone publicId (e.g. \"dnz_abc\").'),\n\t\tdomain: z\n\t\t\t.string()\n\t\t\t.optional()\n\t\t\t.describe('Apex or subdomain — resolves to the longest-matching hosted zone.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst zoneInput: { zone_id?: string; domain?: string } = {};\n\t\tif (args.zone_id !== undefined) zoneInput.zone_id = args.zone_id;\n\t\tif (args.domain !== undefined) zoneInput.domain = args.domain;\n\t\tconst zone = await resolveZonePublicId(ctx.hoststack, teamId, zoneInput);\n\t\tconst { delegation } = await ctx.hoststack.dns.checkDelegation(teamId, zone.publicId);\n\n\t\tconst observed = delegation.observedNameservers.join(', ');\n\t\tconst expected = delegation.expectedNameservers.join(' / ');\n\t\tconst summary =\n\t\t\tdelegation.status === 'delegated'\n\t\t\t\t? `${zone.domainName} is delegated to HostStack (${observed}). Queries for it reach this zone.`\n\t\t\t\t: delegation.status === 'foreign'\n\t\t\t\t\t? `${zone.domainName} is HOSTED here but NOT delegated here: the registry still points it at ${observed}. ` +\n\t\t\t\t\t\t`Records in this zone are correct but nobody is being sent to read them — public lookups return the old host. ` +\n\t\t\t\t\t\t`Change the nameservers at the registrar of ${zone.domainName} to ${expected}; propagation can take up to 48 hours.`\n\t\t\t\t\t: `Could not determine the delegation for ${zone.domainName} — the NS lookup returned no usable answer (SERVFAIL, timeout, or the domain is not registered). ` +\n\t\t\t\t\t\t`This is NOT evidence that the delegation is wrong; do not change a registrar on the strength of it. Retry in a moment.`;\n\n\t\treturn respond({\n\t\t\tsummary,\n\t\t\tdata: {\n\t\t\t\tzone: { publicId: zone.publicId, domainName: zone.domainName },\n\t\t\t\tdelegation,\n\t\t\t},\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'list_dns_records',\n\tcategory: 'dns',\n\tdescription: [\n\t\t'List every active record in a hosted DNS zone. Records are addressed by their type + name + value tuple; PowerDNS gathers records with the same (name, type) into an RRset on the wire.',\n\t\t'',\n\t\t'When to use: audit what is currently published for a zone before editing, verify a recently-created record landed, or read existing TXT verification strings.',\n\t\t'',\n\t\t'Provide EITHER zone_id (the zone publicId, e.g. \"dnz_abc\") OR domain (an apex or subdomain — the longest-matching hosted apex wins; \"app.example.com\" matches a hosted \"example.com\" zone).',\n\t\t'',\n\t\t'Returns: { zone: { publicId, domainName }, items: Record[] } — each record includes publicId, type, name (\"@\" for apex, or label), value, ttl, priority?, status (\"active\" | \"syncing\" | \"failed\"), managedBy (\"user\" | \"hoststack\" | \"poststack\"), createdAt.',\n\t\t'',\n\t\t'Example: list_dns_records({ domain: \"micci.dk\" }) → { zone: { domainName: \"micci.dk\", … }, items: [{ type: \"TXT\", name: \"@\", value: \"google-site-verification=…\", ttl: 300, … }] }',\n\t].join('\\n'),\n\tinput: {\n\t\tzone_id: z.string().optional().describe('Zone publicId (e.g. \"dnz_abc\").'),\n\t\tdomain: z\n\t\t\t.string()\n\t\t\t.optional()\n\t\t\t.describe('Apex or subdomain — resolves to the longest-matching hosted zone.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst zoneInput: { zone_id?: string; domain?: string } = {};\n\t\tif (args.zone_id !== undefined) zoneInput.zone_id = args.zone_id;\n\t\tif (args.domain !== undefined) zoneInput.domain = args.domain;\n\t\tconst zone = await resolveZonePublicId(ctx.hoststack, teamId, zoneInput);\n\t\tconst response = await ctx.hoststack.dns.listRecords(teamId, zone.publicId);\n\t\tconst items = Array.isArray(response.records) ? response.records.map(shape) : [];\n\t\tconst summary =\n\t\t\titems.length === 0\n\t\t\t\t? `Zone ${zone.domainName} has no records yet.`\n\t\t\t\t: `Returned ${items.length} record${items.length === 1 ? '' : 's'} for ${zone.domainName}.`;\n\t\treturn respond({\n\t\t\tsummary,\n\t\t\tdata: { zone: { publicId: zone.publicId, domainName: zone.domainName }, items },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'get_dns_record',\n\tcategory: 'dns',\n\tdescription: [\n\t\t'Fetch a single DNS record by its publicId. Useful for confirming the record landed after create_dns_record / update_dns_record (status transitions from \"syncing\" to \"active\" once PowerDNS acknowledges).',\n\t\t'',\n\t\t'When to use: verify a record exists, inspect its current ttl/value, or check the sync status of an in-flight write.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - record_id: record publicId (e.g. \"dnr_abc\"). Returned by list_dns_records and create_dns_record.',\n\t\t'',\n\t\t'Returns: { record: Record }. Mirrors the shape returned by list_dns_records.',\n\t\t'',\n\t\t'Example: get_dns_record({ record_id: \"dnr_abc\" }) → { record: { type: \"TXT\", name: \"@\", value: \"…\", status: \"active\", … } }',\n\t].join('\\n'),\n\tinput: {\n\t\trecord_id: z.string().describe('Record publicId.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\t// Records have no direct GET-by-id route — walk the team's zones\n\t\t// and scan each for the matching publicId. Zones-per-team is tiny\n\t\t// (≤ a few dozen even on heavy users) so this is cheap.\n\t\tconst { zones } = await ctx.hoststack.dns.listZones(teamId);\n\t\tfor (const zone of zones) {\n\t\t\tconst { records } = await ctx.hoststack.dns.listRecords(teamId, zone.publicId);\n\t\t\tconst match = records.find((r) => r.publicId === args.record_id);\n\t\t\tif (match) {\n\t\t\t\treturn respond({\n\t\t\t\t\tsummary: `Record ${match.type} ${match.name} on ${zone.domainName}.`,\n\t\t\t\t\tdata: {\n\t\t\t\t\t\tzone: { publicId: zone.publicId, domainName: zone.domainName },\n\t\t\t\t\t\trecord: shape(match),\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\treturn respondError(`Record ${args.record_id} not found on any zone owned by this team.`);\n\t},\n});\n\ndefineTool({\n\tname: 'create_dns_record',\n\tcategory: 'dns',\n\tdescription: [\n\t\t'Create a DNS record on a hosted zone. Writes to the DB first, then mirrors to PowerDNS — record returns with status=\"syncing\" until the provider acknowledges, then flips to \"active\". Re-creating the same (zone, type, name, value) tuple is idempotent and returns the existing row.',\n\t\t'',\n\t\t'When to use: add TXT verification records (Google Search Console, domain ownership challenges, DKIM), A/AAAA records pointing a subdomain at an IP, CNAME aliases, MX records, etc.',\n\t\t'',\n\t\t'Provide EITHER zone_id (zone publicId) OR domain (resolves via longest-suffix match against hosted zones).',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - type: \"A\" | \"AAAA\" | \"CNAME\" | \"MX\" | \"TXT\" | \"NS\" | \"SRV\" | \"CAA\" | \"ALIAS\".',\n\t\t' - name: \"@\" for the zone apex, or a subdomain label (\"www\", \"api\", \"_acme-challenge\"). Do NOT include the zone suffix — the backend prepends it. \"bounce\" on zone \"micci.dk\" publishes \"bounce.micci.dk\"; passing \"bounce.micci.dk\" works (the backend strips the suffix) but is non-idiomatic.',\n\t\t' - content: the literal record value. For TXT pass the unquoted string — the PowerDNS layer handles quoting. For CNAME / MX / NS / SRV / ALIAS the target may be passed with or without a trailing dot; the adapter FQDN-izes it.',\n\t\t' - ttl (optional): 60–86400 seconds, default 3600.',\n\t\t' - priority (optional, required for MX and SRV): 0–65535.',\n\t\t'',\n\t\t'Returns: { record: Record } — see get_dns_record for the shape.',\n\t\t'',\n\t\t'Notes:',\n\t\t' - CNAME at the apex is rejected (RFC 1034 forbids CNAME coexisting with NS/SOA). Use type=\"ALIAS\" for apex aliasing.',\n\t\t'',\n\t\t'Example: create_dns_record({ domain: \"micci.dk\", type: \"TXT\", name: \"@\", content: \"google-site-verification=C0V0scK48g2…\", ttl: 300 })',\n\t].join('\\n'),\n\tinput: {\n\t\tzone_id: z.string().optional().describe('Zone publicId.'),\n\t\tdomain: z.string().optional().describe('Apex or subdomain to resolve a hosted zone.'),\n\t\ttype: z.enum(DNS_RECORD_TYPES).describe('DNS record type.'),\n\t\tname: z\n\t\t\t.string()\n\t\t\t.min(1)\n\t\t\t.max(253)\n\t\t\t.describe('\"@\" for apex, or a bare label. Do NOT include the zone suffix.'),\n\t\tcontent: z.string().min(1).max(2000).describe('Record value; TXT values are unquoted.'),\n\t\tttl: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(60)\n\t\t\t.max(86_400)\n\t\t\t.optional()\n\t\t\t.describe('TTL in seconds (default 3600).'),\n\t\tpriority: z.number().int().min(0).max(65_535).optional().describe('Required for MX / SRV.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\t// MX/SRV records carry priority on the wire; the shared upsert\n\t\t// schema enforces this with a superRefine. The MCP `input` shape\n\t\t// is a flat ZodRawShape so we can't express cross-field rules\n\t\t// there — surface a clean error from the handler instead of\n\t\t// letting the backend's 400 bubble up.\n\t\tif ((args.type === 'MX' || args.type === 'SRV') && args.priority === undefined) {\n\t\t\treturn respondError(`Priority is required for ${args.type} records (0–65535).`);\n\t\t}\n\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst zoneInput: { zone_id?: string; domain?: string } = {};\n\t\tif (args.zone_id !== undefined) zoneInput.zone_id = args.zone_id;\n\t\tif (args.domain !== undefined) zoneInput.domain = args.domain;\n\t\tconst zone = await resolveZonePublicId(ctx.hoststack, teamId, zoneInput);\n\n\t\tconst body: {\n\t\t\ttype: DnsRecordType;\n\t\t\tname: string;\n\t\t\tvalue: string;\n\t\t\tttl?: number;\n\t\t\tpriority?: number;\n\t\t} = {\n\t\t\ttype: args.type,\n\t\t\tname: args.name,\n\t\t\tvalue: args.content,\n\t\t};\n\t\tif (args.ttl !== undefined) body.ttl = args.ttl;\n\t\tif (args.priority !== undefined) body.priority = args.priority;\n\n\t\tconst response = await ctx.hoststack.dns.createRecord(teamId, zone.publicId, body);\n\t\treturn respond({\n\t\t\tsummary: `Created ${args.type} ${args.name} on ${zone.domainName}.`,\n\t\t\tdata: {\n\t\t\t\tzone: { publicId: zone.publicId, domainName: zone.domainName },\n\t\t\t\trecord: shape(response.record),\n\t\t\t},\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'update_dns_record',\n\tcategory: 'dns',\n\tdescription: [\n\t\t'Update an existing DNS record by its publicId. Use this to change a TTL, swap a value, or move the record to a different (name, type) bucket. Full-record PUT semantics: every mutable field must be provided.',\n\t\t'',\n\t\t'When to use: rotate a DKIM key, repoint an A record at a new IP, bump TTL ahead of a planned change.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - record_id: record publicId.',\n\t\t' - type, name, content: the new values (the route is full-replace, not patch — fetch with get_dns_record first if you only want to tweak one field).',\n\t\t' - ttl (optional), priority (optional, required for MX / SRV).',\n\t\t'',\n\t\t'Returns: { record: Record } reflecting the new state.',\n\t\t'',\n\t\t'Example: update_dns_record({ record_id: \"dnr_abc\", type: \"A\", name: \"api\", content: \"203.0.113.42\", ttl: 300 })',\n\t].join('\\n'),\n\tinput: {\n\t\trecord_id: z.string().describe('Record publicId.'),\n\t\ttype: z.enum(DNS_RECORD_TYPES).describe('DNS record type.'),\n\t\tname: z\n\t\t\t.string()\n\t\t\t.min(1)\n\t\t\t.max(253)\n\t\t\t.describe('\"@\" for apex, or a bare label. Do NOT include the zone suffix.'),\n\t\tcontent: z.string().min(1).max(2000).describe('Record value; TXT unquoted.'),\n\t\tttl: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(60)\n\t\t\t.max(86_400)\n\t\t\t.optional()\n\t\t\t.describe('TTL in seconds (default 3600).'),\n\t\tpriority: z.number().int().min(0).max(65_535).optional().describe('Required for MX / SRV.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tif ((args.type === 'MX' || args.type === 'SRV') && args.priority === undefined) {\n\t\t\treturn respondError(`Priority is required for ${args.type} records (0–65535).`);\n\t\t}\n\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst target = await findRecordZone(ctx.hoststack, teamId, args.record_id);\n\t\tif (!target) {\n\t\t\treturn respondError(\n\t\t\t\t`Record ${args.record_id} not found on any zone owned by this team.`,\n\t\t\t);\n\t\t}\n\n\t\tconst body: {\n\t\t\ttype: DnsRecordType;\n\t\t\tname: string;\n\t\t\tvalue: string;\n\t\t\tttl?: number;\n\t\t\tpriority?: number;\n\t\t} = {\n\t\t\ttype: args.type,\n\t\t\tname: args.name,\n\t\t\tvalue: args.content,\n\t\t};\n\t\tif (args.ttl !== undefined) body.ttl = args.ttl;\n\t\tif (args.priority !== undefined) body.priority = args.priority;\n\n\t\tconst response = await ctx.hoststack.dns.updateRecord(\n\t\t\tteamId,\n\t\t\ttarget.zone.publicId,\n\t\t\targs.record_id,\n\t\t\tbody,\n\t\t);\n\t\treturn respond({\n\t\t\tsummary: `Updated ${args.type} ${args.name} on ${target.zone.domainName}.`,\n\t\t\tdata: {\n\t\t\t\tzone: {\n\t\t\t\t\tpublicId: target.zone.publicId,\n\t\t\t\t\tdomainName: target.zone.domainName,\n\t\t\t\t},\n\t\t\t\trecord: shape(response.record),\n\t\t\t},\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'delete_dns_record',\n\tcategory: 'dns',\n\tdescription: [\n\t\t'Soft-delete a DNS record and remove it from PowerDNS. DESTRUCTIVE: resolvers stop returning the value as soon as the change propagates. Same code path as the dashboard delete button — the row is marked deleted and the RRset is re-pushed without it.',\n\t\t'',\n\t\t'When to use: retire a TXT verification record once the challenge has been confirmed; drop an A record for a decommissioned subdomain.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - record_id: record publicId.',\n\t\t'',\n\t\t'Returns: { ok: true }.',\n\t\t'',\n\t\t'Example: delete_dns_record({ record_id: \"dnr_abc\" }) → { ok: true }',\n\t].join('\\n'),\n\tinput: {\n\t\trecord_id: z.string().describe('Record publicId.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst target = await findRecordZone(ctx.hoststack, teamId, args.record_id);\n\t\tif (!target) {\n\t\t\treturn respondError(\n\t\t\t\t`Record ${args.record_id} not found on any zone owned by this team.`,\n\t\t\t);\n\t\t}\n\t\tawait ctx.hoststack.dns.deleteRecord(teamId, target.zone.publicId, args.record_id);\n\t\treturn respond({\n\t\t\tsummary: `Deleted record ${args.record_id} from ${target.zone.domainName}.`,\n\t\t\tdata: { ok: true },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'resync_dns_record',\n\tcategory: 'dns',\n\tdescription: [\n\t\t'Re-push a DNS record to PowerDNS without changing its value. Recovers a record stuck at status=\"failed\" after a transient provider outage — including managedBy=\"hoststack\" records (auto-created for a service/domain) that create/update/delete refuse to touch.',\n\t\t'',\n\t\t'When to use: a record shows status=\"failed\" with a lastSyncError, or a freshly-attached domain\\'s auto-created A record never went active. Safe and idempotent — a rejected re-push leaves the live RRset untouched (PowerDNS PATCH is atomic), so this can never drop a working record.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - record_id: record publicId (e.g. \"dnr_abc\").',\n\t\t'',\n\t\t'Returns: { record: Record } — the record with its refreshed status (\"active\" on success).',\n\t\t'',\n\t\t'Example: resync_dns_record({ record_id: \"dnr_abc\" }) → { record: { status: \"active\", ... } }',\n\t].join('\\n'),\n\tinput: {\n\t\trecord_id: z.string().describe('Record publicId.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst target = await findRecordZone(ctx.hoststack, teamId, args.record_id);\n\t\tif (!target) {\n\t\t\treturn respondError(\n\t\t\t\t`Record ${args.record_id} not found on any zone owned by this team.`,\n\t\t\t);\n\t\t}\n\t\tconst response = await ctx.hoststack.dns.resyncRecord(\n\t\t\tteamId,\n\t\t\ttarget.zone.publicId,\n\t\t\targs.record_id,\n\t\t);\n\t\treturn respond({\n\t\t\tsummary: `Re-synced record ${args.record_id} on ${target.zone.domainName} (status: ${response.record.status}).`,\n\t\t\tdata: {\n\t\t\t\tzone: {\n\t\t\t\t\tpublicId: target.zone.publicId,\n\t\t\t\t\tdomainName: target.zone.domainName,\n\t\t\t\t},\n\t\t\t\trecord: shape(response.record),\n\t\t\t},\n\t\t});\n\t},\n});\n\n/**\n * The DNS HTTP routes nest records under their zone, so update/delete\n * by record publicId needs the owning zone's publicId too. Walk the\n * team's zones until we find a record match. Cheap — zone count is\n * bounded by team-tier limits.\n */\nasync function findRecordZone(\n\thoststack: HostStack,\n\tteamId: number,\n\trecordPublicId: string,\n): Promise<{ zone: DnsZone; record: DnsRecord } | null> {\n\tconst { zones } = await hoststack.dns.listZones(teamId);\n\tfor (const zone of zones) {\n\t\tconst { records } = await hoststack.dns.listRecords(teamId, zone.publicId);\n\t\tconst match = records.find((r) => r.publicId === recordPublicId);\n\t\tif (match) return { zone, record: match };\n\t}\n\treturn null;\n}\n","import { z } from 'zod';\n\nimport { defineTool } from '../registry.js';\nimport { respond } from '../lib/respond.js';\nimport { shapeDomain, shapeList } from '../lib/shape.js';\n\ndefineTool({\n\tname: 'list_domains',\n\tcategory: 'domains',\n\tdescription: [\n\t\t\"List every custom domain attached to the team's services.\",\n\t\t'',\n\t\t'When to use: enumerate live domains, audit DNS verification status, or find which service a hostname resolves to before troubleshooting routing.',\n\t\t'',\n\t\t'Returns: { items: Domain[] } — id, publicId, hostname, serviceId, verified, dnsTargets, sslStatus, isPrimary, createdAt.',\n\t\t'',\n\t\t\"`isPrimary` is the one the platform treats as the service's address: what `${service.url}` resolves to, what its uptime check probes, and what the monitoring page shows. When NO domain on a service is primary — the default, since nothing sets it automatically — that choice falls to the OLDEST domain on the service, which on a renamed host is usually the retired alias rather than the canonical name. Nominate one with update_domain.\",\n\t\t'',\n\t\t'Example: list_domains() → { items: [{ hostname: \"api.example.com\", verified: true, sslStatus: \"active\", isPrimary: false, … }] }',\n\t].join('\\n'),\n\tinput: {},\n\thandler: async (_args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.domains.list(teamId);\n\t\tconst data = shapeList(response, 'domains', shapeDomain);\n\t\tconst summary =\n\t\t\tdata.items.length === 0\n\t\t\t\t? 'No custom domains configured.'\n\t\t\t\t: `Found ${data.items.length} custom domain${data.items.length === 1 ? '' : 's'}.`;\n\t\treturn respond({ summary, data });\n\t},\n});\n\ndefineTool({\n\tname: 'add_domain',\n\tcategory: 'domains',\n\tdescription: [\n\t\t'Attach a custom hostname to the team — optionally pinned to a specific service. After adding, point your DNS at the targets returned and call verify_domain.',\n\t\t'',\n\t\t'When to use: the user wants to put a custom domain in front of a HostStack service (e.g. point api.example.com at svc_abc). The hostname must be unique across the team.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - hostname: the domain to add (e.g. \"api.example.com\").',\n\t\t' - service_id: publicId of the service to bind the domain to.',\n\t\t' - path_prefix (optional): when set, only requests under this URL prefix route to this service. Use for path-based fan-out (e.g. `/api` to an api service, `/` to a web service on the same hostname).',\n\t\t'',\n\t\t'Returns: { domain: Domain } — includes dnsTargets you must configure (CNAME / A records).',\n\t\t'',\n\t\t'Example: add_domain({ hostname: \"api.example.com\", service_id: \"svc_abc\" }) → { domain: { hostname: \"api.example.com\", dnsTargets: [...], verified: false, … } }',\n\t].join('\\n'),\n\tinput: {\n\t\thostname: z\n\t\t\t.string()\n\t\t\t.min(3)\n\t\t\t.max(253)\n\t\t\t.describe('Fully-qualified hostname (e.g. api.example.com).'),\n\t\tservice_id: z.string().describe('Service publicId to bind to.'),\n\t\tpath_prefix: z\n\t\t\t.string()\n\t\t\t.optional()\n\t\t\t.describe('Optional URL prefix for path-based routing (e.g. \"/api\").'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst input: { domain: string; serviceId: string; pathPrefix?: string } = {\n\t\t\tdomain: args.hostname,\n\t\t\tserviceId: args.service_id,\n\t\t};\n\t\tif (args.path_prefix !== undefined) input.pathPrefix = args.path_prefix;\n\t\tconst response = await ctx.hoststack.domains.add(teamId, input);\n\t\t// When HostStack hosts the zone it auto-creates the A record; if that\n\t\t// write can't reach the provider the API returns the domain plus a\n\t\t// dnsSyncWarning. Relay it so the caller doesn't read the bare pending\n\t\t// status as success. The record recovers via the reconcile sweep or a\n\t\t// manual resync_dns_record call.\n\t\tconst { dnsSyncWarning, delegationWarning } = response.domain;\n\t\t// `delegationWarning` outranks the sync warning: a record that has not\n\t\t// synced yet will sync, but an apex the registry points elsewhere never\n\t\t// resolves here at all, and no amount of calling verify_domain changes\n\t\t// that. Saying so now is the difference between one registrar edit and\n\t\t// an hour of re-checking a domain that was never going to verify.\n\t\tconst data = {\n\t\t\tdomain: shapeDomain(response.domain),\n\t\t\t...(dnsSyncWarning ? { dnsSyncWarning } : {}),\n\t\t\t...(delegationWarning ? { delegationWarning } : {}),\n\t\t};\n\t\treturn respond({\n\t\t\tsummary: delegationWarning\n\t\t\t\t? `Added domain ${args.hostname}, but it CANNOT verify yet: ${delegationWarning}`\n\t\t\t\t: dnsSyncWarning\n\t\t\t\t\t? `Added domain ${args.hostname}, but its auto-created DNS record hasn't synced yet — ${dnsSyncWarning}`\n\t\t\t\t\t: `Added domain ${args.hostname}. Configure DNS, then call verify_domain.`,\n\t\t\tdata,\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'verify_domain',\n\tcategory: 'domains',\n\tdescription: [\n\t\t'Trigger DNS verification for a previously-added domain. HostStack checks that the dnsTargets returned by add_domain are configured at the registrar.',\n\t\t'',\n\t\t'When to use: the user has set up DNS records and is ready to flip the domain live. Returns success when verification passes; the domain stays in pending state if DNS is still propagating.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - domain_id: publicId of the domain (from list_domains or add_domain).',\n\t\t'',\n\t\t'Returns: { domain, verified }. `verified` is the outcome of THIS check — the call does not merely queue one.',\n\t\t'',\n\t\t'If it fails and HostStack hosts the zone, the response may carry `delegationWarning`: the records are ours and correct, but the registry still delegates the apex to another host, so queries never reach us. That will NOT resolve by waiting or by calling this again — the nameservers have to change at the registrar. Use check_dns_delegation to see who the apex currently points at.',\n\t\t'',\n\t\t'It may instead carry `dnsSyncWarning`: the record HostStack creates for this hostname could not be published, usually because a record already at that name conflicts with it (a CNAME and an A cannot share a name). Also not fixable by retrying — resolve the conflict it names with list_dns_records / delete_dns_record first.',\n\t\t'',\n\t\t'Example: verify_domain({ domain_id: \"dom_xyz\" }) → { verified: false, delegationWarning: \"…the registry still delegates wohnwagen-wuckert.de to docks15.rzone.de…\" }',\n\t].join('\\n'),\n\tinput: {\n\t\tdomain_id: z.string().describe('Domain publicId.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\t// The route has always returned the post-check domain; this tool used to\n\t\t// discard it and report \"Triggered…\", which reads as success whatever\n\t\t// happened. Report the outcome.\n\t\tconst { domain } = await ctx.hoststack.domains.verify(teamId, args.domain_id);\n\t\tconst verified = domain.status === 'active';\n\t\t// Both warnings mean \"calling this again changes nothing\", which is the\n\t\t// opposite of what a bare `pending` invites. `delegationWarning` outranks:\n\t\t// if nothing reaches our nameservers, what we did or did not publish on\n\t\t// them is not yet the customer's problem.\n\t\tconst summary = verified\n\t\t\t? `Verified ${domain.domain} — it is now active.`\n\t\t\t: domain.delegationWarning\n\t\t\t\t? `${domain.domain} did NOT verify, and retrying will not help: ${domain.delegationWarning}`\n\t\t\t\t: domain.dnsSyncWarning\n\t\t\t\t\t? `${domain.domain} did NOT verify, and retrying will not help: ${domain.dnsSyncWarning}`\n\t\t\t\t\t: `${domain.domain} did not verify yet (status: ${domain.status}). DNS changes can take time to propagate; check the records resolve publicly, then call this again.`;\n\t\treturn respond({\n\t\t\tsummary,\n\t\t\tdata: {\n\t\t\t\tdomain: shapeDomain(domain),\n\t\t\t\tverified,\n\t\t\t\t...(domain.delegationWarning\n\t\t\t\t\t? { delegationWarning: domain.delegationWarning }\n\t\t\t\t\t: {}),\n\t\t\t\t...(domain.dnsSyncWarning ? { dnsSyncWarning: domain.dnsSyncWarning } : {}),\n\t\t\t},\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'update_domain',\n\tcategory: 'domains',\n\tdescription: [\n\t\t\"Change a domain's settings — most usefully, nominate it as the service's PRIMARY hostname.\",\n\t\t'',\n\t\t'When to use: a service answers on more than one hostname and you need to say which one is canonical — before setting an uptime check on it, after renaming a storefront, or when `${service.url}` is resolving to the wrong name. Also for toggling https on a domain or turning one into a redirect.',\n\t\t'',\n\t\t'WHY PRIMARY MATTERS. A service can answer on several hostnames that behave differently — the canonical one serves the page, a retired alias 301s to it — and three parts of the platform have to pick ONE of them: the address a service advertises to itself in `${service.url}`, the host its uptime check probes, and the hostname shown on the monitoring page. All three pick the primary, and fall back to the OLDEST domain when no primary is nominated. So a service with two hostnames and no primary has its uptime check pointed at whichever domain was added first, which may well be the alias that redirects — and `set_uptime_check` takes a path, never a host, so nominating the primary here is the only way to say which name is checked.',\n\t\t'',\n\t\t'One primary per service: promoting a domain demotes its sibling in the same write, so there is never a pair to choose between.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - domain_id: publicId of the domain (from list_domains).',\n\t\t\" - isPrimary: make this the service's primary hostname.\",\n\t\t' - sslEnabled: serve it over https. Applies on the next deploy, which this triggers.',\n\t\t' - redirectTo: send every request to an absolute http(s) URL instead of the service. Pass null to clear.',\n\t\t'',\n\t\t'Returns: { domain } — the updated row, including isPrimary.',\n\t\t'',\n\t\t'Example: update_domain({ domain_id: \"dom_xyz\", isPrimary: true }) → { domain: { hostname: \"shop.example.com\", isPrimary: true } }',\n\t].join('\\n'),\n\tinput: {\n\t\tdomain_id: z.string().describe('Domain publicId.'),\n\t\tisPrimary: z.boolean().optional(),\n\t\tsslEnabled: z.boolean().optional(),\n\t\tredirectTo: z\n\t\t\t.string()\n\t\t\t.max(2000)\n\t\t\t.nullable()\n\t\t\t.optional()\n\t\t\t.describe('Absolute http(s) URL, or null to clear.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst { domain_id: domainId, ...patch } = args;\n\t\tconst body = Object.fromEntries(\n\t\t\tObject.entries(patch).filter(([, v]) => v !== undefined),\n\t\t) as Parameters<typeof ctx.hoststack.domains.update>[2];\n\t\tif (Object.keys(body).length === 0) {\n\t\t\treturn respond({\n\t\t\t\tsummary: 'Nothing to change — pass isPrimary, sslEnabled or redirectTo.',\n\t\t\t\tdata: { ok: false },\n\t\t\t});\n\t\t}\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.domains.update(teamId, domainId, body);\n\t\tconst domain = shapeDomain(response.domain);\n\t\treturn respond({\n\t\t\tsummary:\n\t\t\t\tbody.isPrimary === true\n\t\t\t\t\t? `${String(domain.hostname ?? domainId)} is now the primary hostname for its service — it is what \\`\\${service.url}\\` resolves to and what the uptime check probes.`\n\t\t\t\t\t: `Updated ${String(domain.hostname ?? domainId)}.`,\n\t\t\tdata: { domain },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'remove_domain',\n\tcategory: 'domains',\n\tdescription: [\n\t\t'Detach a custom hostname from the team. DESTRUCTIVE: the domain stops routing immediately and any pinned service must fall back to its default hostname.',\n\t\t'',\n\t\t'When to use: the user wants to retire a custom domain. Confirm with the user first — the change is immediate and cannot be undone except by re-adding the domain and re-verifying DNS.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - domain_id: publicId of the domain to remove.',\n\t\t'',\n\t\t'Returns: { ok: true }.',\n\t\t'',\n\t\t'Example: remove_domain({ domain_id: \"dom_xyz\" }) → { ok: true }',\n\t].join('\\n'),\n\tinput: {\n\t\tdomain_id: z.string().describe('Domain publicId.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tawait ctx.hoststack.domains.remove(teamId, args.domain_id);\n\t\treturn respond({ summary: `Removed domain ${args.domain_id}.`, data: { ok: true } });\n\t},\n});\n","import { z } from 'zod';\n\nimport { defineTool } from '../registry.js';\nimport { respond, respondError } from '../lib/respond.js';\nimport { shapeEnvVar, shapeList } from '../lib/shape.js';\n\ninterface ExistingEnvVar {\n\tid: number;\n\tpublicId?: string;\n\tkey: string;\n\tisSecret: boolean;\n}\n\ndefineTool({\n\tname: 'list_env_vars',\n\tcategory: 'env-vars',\n\tdescription: [\n\t\t'List environment variables for a service. Secret values are masked server-side (\"••••••\"); only non-secret values come through in the clear.',\n\t\t'',\n\t\t'When to use: auditing what configuration a service has, finding the key for a value the user mentions by name, or confirming a variable was set after a write. Never assume the value field is plaintext for secret rows — it is masked.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t'',\n\t\t'Returns: { items: EnvVar[] } — id, publicId, key, value (masked if isSecret), isSecret, target, linkedDatabaseId, createdAt.',\n\t\t'',\n\t\t'Example: list_env_vars({ service_id: \"svc_abc\" }) → { items: [{ key: \"DATABASE_URL\", value: \"••••••\", isSecret: true }, { key: \"PORT\", value: \"3000\", isSecret: false }] }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.envVars.list(teamId, args.service_id);\n\t\tconst data = shapeList(response, 'envVars', shapeEnvVar);\n\t\tconst summary =\n\t\t\tdata.items.length === 0\n\t\t\t\t? `No env vars set on service ${args.service_id}.`\n\t\t\t\t: `Found ${data.items.length} env var${data.items.length === 1 ? '' : 's'} on service ${args.service_id}.`;\n\t\treturn respond({ summary, data });\n\t},\n});\n\ndefineTool({\n\tname: 'set_env_var',\n\tcategory: 'env-vars',\n\tdescription: [\n\t\t'Upsert a single environment variable on a service: creates the key if missing, updates the value if it already exists. Match is by key (case-sensitive).',\n\t\t'',\n\t\t'When to use: rotate a secret, add a new config knob, or change a value the user dictated. The MCP layer looks up the existing var by key first; you do not need to know the env-var ID.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t' - key: env-var name (e.g. \"DATABASE_URL\").',\n\t\t' - value: new value (will be encrypted at rest if is_secret=true).',\n\t\t' - is_secret (optional): true marks the value as secret (masked on read). On create, defaults to true for safety. On update, omitting it leaves the existing flag untouched — pass it explicitly only when you want to change classification.',\n\t\t' - target (optional): where the var is injected — \"build\", \"runtime\", or \"both\". On create, defaults to \"both\". On update, omitting it leaves the existing target untouched.',\n\t\t' IMPORTANT for build-time values (a frontend VITE_*/NEXT_PUBLIC_*, anything a bundler inlines): \"both\" and \"build\" reach the build, but a SECRET var never does on \"both\" — build args are baked into image history. Since is_secret defaults to TRUE here, set is_secret:false for a public build-time value or it will silently be missing from the built artifact. Use \"build\" to force a secret into the build anyway.',\n\t\t'',\n\t\t'Returns: { envVar: EnvVar, action: \"created\" | \"updated\" }.',\n\t\t'',\n\t\t'Example: set_env_var({ service_id: \"svc_abc\", key: \"DATABASE_URL\", value: \"postgres://…\", is_secret: true }) → { envVar: { key: \"DATABASE_URL\", value: \"••••••\", … }, action: \"updated\" }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t\tkey: z.string().min(1).max(128).describe('Env-var key.'),\n\t\tvalue: z.string().describe('New value.'),\n\t\tis_secret: z\n\t\t\t.boolean()\n\t\t\t.optional()\n\t\t\t.describe('Mark as secret (encrypted, masked on read). Default true.'),\n\t\ttarget: z\n\t\t\t.enum(['build', 'runtime', 'both'])\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t'Injection target: build, runtime, or both. Default both. A secret var on \"both\" is runtime-only (build args land in image history) — pass is_secret:false for a public build-time value, or use \"build\" to force it.',\n\t\t\t),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst existing = await ctx.hoststack.envVars.list(teamId, args.service_id);\n\t\tconst match = (existing.envVars as ExistingEnvVar[]).find((v) => v.key === args.key);\n\n\t\tif (match) {\n\t\t\t// On update, only forward `isSecret`/`target` when the caller\n\t\t\t// passed them explicitly. Defaulting `isSecret` to `true` here\n\t\t\t// would silently flip previously-plaintext keys (PORT, NODE_ENV,\n\t\t\t// …) to secret on every value change.\n\t\t\tconst updatePayload: {\n\t\t\t\tvalue: string;\n\t\t\t\tisSecret?: boolean;\n\t\t\t\ttarget?: 'build' | 'runtime' | 'both';\n\t\t\t} = { value: args.value };\n\t\t\tif (args.is_secret !== undefined) updatePayload.isSecret = args.is_secret;\n\t\t\tif (args.target !== undefined) updatePayload.target = args.target;\n\t\t\tconst response = await ctx.hoststack.envVars.update(\n\t\t\t\tteamId,\n\t\t\t\targs.service_id,\n\t\t\t\tString(match.id),\n\t\t\t\tupdatePayload,\n\t\t\t);\n\t\t\tconst data = {\n\t\t\t\tenvVar: shapeEnvVar(response.envVar),\n\t\t\t\taction: 'updated' as const,\n\t\t\t};\n\t\t\treturn respond({ summary: `Updated ${args.key} on service ${args.service_id}.`, data });\n\t\t}\n\n\t\tconst response = await ctx.hoststack.envVars.create(teamId, args.service_id, {\n\t\t\tkey: args.key,\n\t\t\tvalue: args.value,\n\t\t\tisSecret: args.is_secret ?? true,\n\t\t\t...(args.target !== undefined ? { target: args.target } : {}),\n\t\t});\n\t\tconst data = {\n\t\t\tenvVar: shapeEnvVar(response.envVar),\n\t\t\taction: 'created' as const,\n\t\t};\n\t\treturn respond({ summary: `Created ${args.key} on service ${args.service_id}.`, data });\n\t},\n});\n\ndefineTool({\n\tname: 'delete_env_var',\n\tcategory: 'env-vars',\n\tdescription: [\n\t\t'Remove an environment variable from a service by key (case-sensitive).',\n\t\t'',\n\t\t'When to use: cleaning up unused config, or rotating away from a leaked secret. The MCP looks up the env-var ID from the key automatically.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t' - key: env-var name to delete.',\n\t\t'',\n\t\t'Returns: { ok: true } on success. Returns an error if the key was not found.',\n\t\t'',\n\t\t'Example: delete_env_var({ service_id: \"svc_abc\", key: \"OLD_FLAG\" }) → { ok: true }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t\tkey: z.string().min(1).max(128).describe('Env-var key to delete.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst existing = await ctx.hoststack.envVars.list(teamId, args.service_id);\n\t\tconst match = (existing.envVars as ExistingEnvVar[]).find((v) => v.key === args.key);\n\t\tif (!match) {\n\t\t\treturn respondError(`Env var \"${args.key}\" not found on service ${args.service_id}.`, {\n\t\t\t\tkey: args.key,\n\t\t\t\tservice_id: args.service_id,\n\t\t\t});\n\t\t}\n\t\tawait ctx.hoststack.envVars.delete(teamId, args.service_id, String(match.id));\n\t\treturn respond({\n\t\t\tsummary: `Deleted ${args.key} from service ${args.service_id}.`,\n\t\t\tdata: { ok: true },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'bulk_set_env_vars',\n\tcategory: 'env-vars',\n\tdescription: [\n\t\t'Replace the entire env-var set on a service with the given list. Equivalent to deleting all current vars and creating the supplied ones — use with care.',\n\t\t'',\n\t\t'When to use: importing a .env file, mirroring config from a sibling service, or doing a clean reset. For incremental changes, use set_env_var per key — bulk_set is destructive for any key not in the supplied list.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t' - env_vars: array of { key, value, is_secret?, target? }. is_secret defaults to FALSE per row (a row is stored in the clear unless you set is_secret:true); target defaults to \"both\". Note this differs from set_env_var, which defaults a newly-created var to secret.',\n\t\t' A var reaches the image BUILD when target is \"build\", or when target is \"both\" and is_secret is false. A secret on \"both\" is runtime-only, because build args are visible in image history.',\n\t\t'',\n\t\t'Returns: { ok: true }. Re-list with list_env_vars to confirm the new state.',\n\t\t'',\n\t\t'Example: bulk_set_env_vars({ service_id: \"svc_abc\", env_vars: [{ key: \"PORT\", value: \"3000\", is_secret: false }, { key: \"DATABASE_URL\", value: \"…\", is_secret: true }] }) → { ok: true }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t\tenv_vars: z\n\t\t\t.array(\n\t\t\t\tz.object({\n\t\t\t\t\tkey: z.string().min(1).max(128),\n\t\t\t\t\tvalue: z.string(),\n\t\t\t\t\tis_secret: z.boolean().optional(),\n\t\t\t\t\ttarget: z.enum(['build', 'runtime', 'both']).optional(),\n\t\t\t\t}),\n\t\t\t)\n\t\t\t.max(500)\n\t\t\t.describe('Array of env-var rows. Hard cap 500.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst payload = {\n\t\t\tvars: args.env_vars.map((v) => {\n\t\t\t\tconst row: {\n\t\t\t\t\tkey: string;\n\t\t\t\t\tvalue: string;\n\t\t\t\t\tisSecret?: boolean;\n\t\t\t\t\ttarget?: 'build' | 'runtime' | 'both';\n\t\t\t\t} = {\n\t\t\t\t\tkey: v.key,\n\t\t\t\t\tvalue: v.value,\n\t\t\t\t};\n\t\t\t\tif (v.is_secret !== undefined) row.isSecret = v.is_secret;\n\t\t\t\tif (v.target !== undefined) row.target = v.target;\n\t\t\t\treturn row;\n\t\t\t}),\n\t\t};\n\t\tawait ctx.hoststack.envVars.bulkSet(teamId, args.service_id, payload);\n\t\treturn respond({\n\t\t\tsummary: `Replaced env-var set on service ${args.service_id} with ${args.env_vars.length} entr${args.env_vars.length === 1 ? 'y' : 'ies'}.`,\n\t\t\tdata: { ok: true, count: args.env_vars.length },\n\t\t});\n\t},\n});\n","import { z } from 'zod';\n\nimport { respond, respondError } from '../lib/respond.js';\nimport { shape, shapeList } from '../lib/shape.js';\nimport { defineTool } from '../registry.js';\n\n/**\n * v66 P5: MCP tools for project environment management.\n *\n * Environments isolate services in the same project — staging vs\n * production, for example. Each project automatically gets a Production\n * environment; users can add staging/development/preview envs to run\n * sibling services side-by-side and promote builds between them.\n */\n\ndefineTool({\n\tname: 'list_environments',\n\tcategory: 'environments',\n\tdescription: [\n\t\t'List every environment for a project (production / staging / development / preview).',\n\t\t'',\n\t\t'When to use: the user wants to see what envs exist before creating a service in one or promoting a deploy. Every project has at least Production.',\n\t\t'',\n\t\t// §2.2: these are project deployment environments, not cloud Dev Boxes — point agents at the\n\t\t// right surface so the two \"dev\" concepts stay distinct (bidirectional cross-reference).\n\t\t'NOTE — these are project deployment environments (Production / Staging / Preview), NOT agentic cloud Dev Boxes. To list Dev Boxes use list_dev_environments; to create one use create_dev_environment / create_standalone_dev_environment / spin_up_dev_environment.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - project_id: project publicId (e.g. \"prj_abc123\").',\n\t\t'',\n\t\t'Returns: { items: Environment[] } — id, publicId, name, type, isDefault, isProtected.',\n\t\t'',\n\t\t'Example: list_environments({ project_id: \"prj_abc\" }) → { items: [{ name: \"Production\", type: \"production\", isDefault: true }, { name: \"Staging\", type: \"staging\" }] }',\n\t].join('\\n'),\n\tinput: {\n\t\tproject_id: z.string().describe('Project publicId.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.environments.list(teamId, args.project_id);\n\t\tconst data = shapeList(response, 'environments', shape);\n\t\tconst summary =\n\t\t\tdata.items.length === 0\n\t\t\t\t? 'No environments yet — every project should have Production by default.'\n\t\t\t\t: `Found ${data.items.length} environment${data.items.length === 1 ? '' : 's'}.`;\n\t\treturn respond({ summary, data });\n\t},\n});\n\ndefineTool({\n\tname: 'create_environment',\n\tcategory: 'environments',\n\tdescription: [\n\t\t'Create a new environment in a project (e.g. Staging, QA, Preview).',\n\t\t'',\n\t\t'When to use: the user wants to add an env so they can run a sibling service alongside production for testing or staging before release.',\n\t\t'',\n\t\t// §2.1/§2.2: steer callers away from the agentic cloud Dev Box tools (which live in\n\t\t// services.ts) — an environment is a deployment target, NOT a Dev Box. Bidirectional with\n\t\t// the cross-references those dev-box tools point back here.\n\t\t'NOTE — this is NOT a cloud Dev Box. An environment is a project deployment target (Production / Staging / Preview); type=\"development\" just gives services a -dev hostname suffix. For an agentic cloud Dev Box (a terminal you drive coding agents in) use create_dev_environment / create_standalone_dev_environment / spin_up_dev_environment instead.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - project_id: project publicId.',\n\t\t' - name: human-readable name (1–64 chars). Shown in the env switcher.',\n\t\t' - type: \"production\" | \"staging\" | \"development\" | \"preview\". Determines the hostname suffix on services in this env (production stays clean; others get -staging / -dev / -preview). type=\"development\" creates a PROJECT deployment environment (a -dev hostname suffix), NOT an agentic Dev Box — for a cloud dev box use create_dev_environment / create_standalone_dev_environment / spin_up_dev_environment.',\n\t\t' - is_protected (optional): require admin role for destructive actions in this env. Default false.',\n\t\t'',\n\t\t'Returns: { environment: Environment }.',\n\t\t'',\n\t\t'Example: create_environment({ project_id: \"prj_abc\", name: \"Staging\", type: \"staging\" }) → { environment: { id: 7, publicId: \"env_…\", name: \"Staging\", type: \"staging\" } }',\n\t].join('\\n'),\n\tinput: {\n\t\tproject_id: z.string().describe('Project publicId.'),\n\t\tname: z.string().min(1).max(64).describe('Environment name (1–64 chars).'),\n\t\ttype: z.enum(['production', 'staging', 'development', 'preview']).describe(\n\t\t\t// §2.1: \"development\" here is a project deployment env, NOT a cloud Dev Box.\n\t\t\t'Environment type — drives the hostname suffix. type=\"development\" creates a PROJECT deployment environment (a -dev hostname suffix), NOT an agentic Dev Box — for a cloud dev box use create_dev_environment / create_standalone_dev_environment / spin_up_dev_environment.',\n\t\t),\n\t\tis_protected: z\n\t\t\t.boolean()\n\t\t\t.optional()\n\t\t\t.describe('Require admin role for destructive actions. Default false.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst input: {\n\t\t\tname: string;\n\t\t\ttype: 'production' | 'staging' | 'development' | 'preview';\n\t\t\tisProtected?: boolean;\n\t\t} = {\n\t\t\tname: args.name,\n\t\t\ttype: args.type,\n\t\t};\n\t\tif (args.is_protected !== undefined) input.isProtected = args.is_protected;\n\t\tconst response = await ctx.hoststack.environments.create(teamId, args.project_id, input);\n\t\tconst data = { environment: shape(response.environment) };\n\t\treturn respond({\n\t\t\tsummary: `Created environment \"${args.name}\" (${args.type}) in project ${args.project_id}.`,\n\t\t\tdata,\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'update_environment',\n\tcategory: 'environments',\n\tdescription: [\n\t\t\"Rename an environment, change whether it is protected, or make it the project's default.\",\n\t\t'',\n\t\t'When to use: correcting a name, turning protection on for an env that has become production-like, or promoting a different env to default. Everything else about an environment is fixed at creation — `type` in particular cannot be changed, because the hostname suffix it drives is already baked into every service in the env.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - project_id: project publicId.',\n\t\t' - environment_id: environment publicId or numeric id.',\n\t\t' - name (optional): new name, 1–64 chars.',\n\t\t' - is_protected (optional): require admin role for destructive actions in this env.',\n\t\t' - is_default (optional): make this the project default. See below.',\n\t\t'',\n\t\t'is_default is a MOVE, not a flag. Setting it true demotes whichever env is currently default, because a project has exactly one — and for the same reason you cannot set it false: that would leave the project with none, and every create that omits an envId falls back to the default, so the next `create_service` would fail with \"Project has no Production environment\". To change which env is default, set is_default true on the one you want; the old one is demoted for you.',\n\t\t'',\n\t\t'Returns: { environment: Environment }.',\n\t\t'',\n\t\t'Example: update_environment({ project_id: \"prj_abc\", environment_id: \"env_xyz\", name: \"Staging EU\" }) → { environment: { publicId: \"env_xyz\", name: \"Staging EU\" } }',\n\t].join('\\n'),\n\tinput: {\n\t\tproject_id: z.string().describe('Project publicId.'),\n\t\tenvironment_id: z\n\t\t\t.union([z.string(), z.number()])\n\t\t\t.describe('Environment publicId or numeric id.'),\n\t\tname: z.string().min(1).max(64).optional().describe('New name (1–64 chars).'),\n\t\tis_protected: z\n\t\t\t.boolean()\n\t\t\t.optional()\n\t\t\t.describe('Require admin role for destructive actions in this env.'),\n\t\tis_default: z\n\t\t\t.boolean()\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t\"Make this the project's default, demoting the current one. Cannot be set false — promote a different env instead.\",\n\t\t\t),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst input: { name?: string; isProtected?: boolean; isDefault?: boolean } = {};\n\t\tif (args.name !== undefined) input.name = args.name;\n\t\tif (args.is_protected !== undefined) input.isProtected = args.is_protected;\n\t\tif (args.is_default !== undefined) input.isDefault = args.is_default;\n\n\t\t// The API answers an empty patch with the unchanged row, so without\n\t\t// this the tool would report a successful update that changed nothing.\n\t\tif (Object.keys(input).length === 0) {\n\t\t\treturn respondError(\n\t\t\t\t'Nothing to update. Pass at least one of name, is_protected or is_default.',\n\t\t\t);\n\t\t}\n\n\t\tconst response = await ctx.hoststack.environments.update(\n\t\t\tteamId,\n\t\t\targs.project_id,\n\t\t\targs.environment_id,\n\t\t\tinput,\n\t\t);\n\t\tconst data = { environment: shape(response.environment) };\n\t\tconst fields = Object.keys(input).join(', ');\n\t\treturn respond({\n\t\t\tsummary: `Updated ${fields} on environment ${response.environment.name}.`,\n\t\t\tdata,\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'delete_environment',\n\tcategory: 'environments',\n\tdescription: [\n\t\t'Delete an environment from a project.',\n\t\t'',\n\t\t'When to use: an env is no longer used (e.g. tearing down a feature branch staging). Blocked when the env still has live services or databases attached — destroy or move them first. The default env (usually Production) cannot be deleted.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - project_id: project publicId.',\n\t\t' - environment_id: environment publicId or numeric id.',\n\t\t'',\n\t\t'Returns: { success: true } on success.',\n\t\t'',\n\t\t'Example: delete_environment({ project_id: \"prj_abc\", environment_id: \"env_xyz\" }) → { success: true }',\n\t].join('\\n'),\n\tinput: {\n\t\tproject_id: z.string().describe('Project publicId.'),\n\t\tenvironment_id: z\n\t\t\t.union([z.string(), z.number()])\n\t\t\t.describe('Environment publicId or numeric id.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tawait ctx.hoststack.environments.delete(teamId, args.project_id, args.environment_id);\n\t\treturn respond({\n\t\t\tsummary: `Deleted environment ${String(args.environment_id)} from project ${args.project_id}.`,\n\t\t\tdata: { success: true },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'promote_deploy',\n\tcategory: 'environments',\n\tdescription: [\n\t\t'Promote a built deploy from one environment to another (build-once, run-many).',\n\t\t'',\n\t\t'When to use: the user has tested a deploy in staging and wants to ship that exact image to production without rebuilding. Atomic and image-based — same docker image runs on the sibling service in the target env.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: source service publicId (the one that owns the deploy).',\n\t\t' - deploy_id: source deploy publicId. Must have `dockerImageId` set (i.e. a successful build).',\n\t\t\" - target_environment_id: env publicId or numeric id to promote into. Must be in the same project. Cannot be the source service's own env.\",\n\t\t'',\n\t\t\"Behaviour: if a sibling service with the same name already exists in the target env, the new deploy lands on it. Otherwise the API auto-clones the source service's build/runtime config into the target env first. Env-specific config (env vars, secret files, volumes, IP allowlists, custom domains) is NOT copied — those are per-env by design.\",\n\t\t'',\n\t\t'Returns: { deploy: Deploy } — the new deploy on the target service.',\n\t\t'',\n\t\t'Example: promote_deploy({ service_id: \"svc_staging\", deploy_id: \"dpl_built\", target_environment_id: \"env_prod\" }) → { deploy: { id: 99, status: \"pending\", trigger: \"rollback\", dockerImageId: \"sha256:…\" } }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Source service publicId.'),\n\t\tdeploy_id: z.string().describe('Source deploy publicId (must have a built image).'),\n\t\ttarget_environment_id: z\n\t\t\t.union([z.string(), z.number()])\n\t\t\t.describe('Target environment publicId or numeric id.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\t// Resolve target env to numeric id; the API expects a number.\n\t\tconst targetEnvId = await ctx.hoststack.resolveId(args.target_environment_id, {\n\t\t\tkind: 'environment',\n\t\t\tteamId: await ctx.hoststack.resolveId(teamId, { kind: 'team' }),\n\t\t});\n\t\tconst response = await ctx.hoststack.deploys.promote(\n\t\t\tteamId,\n\t\t\targs.service_id,\n\t\t\targs.deploy_id,\n\t\t\ttargetEnvId,\n\t\t);\n\t\tconst data = { deploy: shape(response.deploy) };\n\t\treturn respond({\n\t\t\tsummary: `Promoted deploy ${args.deploy_id} from service ${args.service_id} to env ${String(args.target_environment_id)}.`,\n\t\t\tdata,\n\t\t});\n\t},\n});\n","import { z } from 'zod';\n\nimport { formatCount } from '../lib/format.js';\nimport { respond } from '../lib/respond.js';\nimport { shape } from '../lib/shape.js';\nimport { defineTool } from '../registry.js';\n\ninterface MetricWindow {\n\tpageviews: number;\n\tvisitors: number;\n\tbounceRate: number | null;\n\tavgDurationMs: number | null;\n}\n\ninterface SitesResponse {\n\tsites: (Record<string, unknown> & { id: number; domain: string })[];\n}\n\ninterface SummaryResponse {\n\trange: string;\n\tsites: (Record<string, unknown> & {\n\t\tdomain: string;\n\t\tcurrent: MetricWindow;\n\t\tvisitorsAreSummedDailies: boolean;\n\t})[];\n}\n\ninterface OverviewResponse {\n\trange: string;\n\tsource: 'raw' | 'rollup';\n\tvisitorsAreSummedDailies: boolean;\n\tfiltersSupported: boolean;\n\tsummary: { current: MetricWindow; previous: MetricWindow };\n\t[key: string]: unknown;\n}\n\n/** Turn a domain into the site id the API wants, or fail with a useful message. */\nasync function resolveSiteIds(\n\tdomains: string[] | undefined,\n\tteamId: number,\n\tapi: {\n\t\tget: <T>(path: string, params?: Record<string, string | number | undefined>) => Promise<T>;\n\t},\n): Promise<string | undefined> {\n\tif (!domains || domains.length === 0) return undefined;\n\tconst { sites } = await api.get<SitesResponse>(`/api/analytics/${teamId}/sites`);\n\tconst ids: number[] = [];\n\tconst missing: string[] = [];\n\tfor (const domain of domains) {\n\t\tconst needle = domain\n\t\t\t.toLowerCase()\n\t\t\t.replace(/^https?:\\/\\//, '')\n\t\t\t.replace(/\\/.*$/, '');\n\t\tconst match = sites.find((s) => s.domain === needle);\n\t\tif (match) ids.push(match.id);\n\t\telse missing.push(domain);\n\t}\n\tif (missing.length > 0) {\n\t\tthrow new Error(\n\t\t\t`Not tracking ${missing.join(', ')}. Known sites: ${sites.map((s) => s.domain).join(', ') || 'none yet'}.`,\n\t\t);\n\t}\n\treturn ids.join(',');\n}\n\nconst ALLOWED_ORIGINS_INPUT = z\n\t.array(z.string().trim().min(1).max(253))\n\t.max(20)\n\t.describe(\n\t\t'Extra origins allowed to post events, beyond the site domain and its subdomains (both are always allowed). Bare hostnames, e.g. [\"staging.example.com\", \"localhost:5173\"]. Replaces the whole list; pass [] to clear it.',\n\t);\n\nconst RETENTION_DAYS_INPUT = z\n\t.number()\n\t.int()\n\t.positive()\n\t.describe('How many days of raw events to keep. Rollups outlive this.');\n\ndefineTool({\n\tname: 'list_analytics_sites',\n\tcategory: 'analytics',\n\tdescription: [\n\t\t'List the websites this team tracks — domains, their site keys, retention, and which service (if any) serves each one.',\n\t\t'',\n\t\t'When to use: before any other analytics call, to learn which domains exist; or to fetch the script tag a site needs.',\n\t\t'',\n\t\t'The site key is PUBLIC by design — it ships in a `data-site-key` attribute that anyone can view-source, like a Sentry DSN. It is write-only and scoped to one site. Do not treat it as a secret, and do not confuse it with the team API key.',\n\t\t'',\n\t\t'Returns: { sites: Array }. Each site has id, publicId, name, domain, ingestKey, allowedOrigins, serviceId, serviceName, retentionDays, droppedCount (events refused by the hourly quota and therefore MISSING from every count for that site), and keyGraceEndsAt when a rotation is still in its grace window.',\n\t\t'',\n\t\t'Each site also carries lastEventAt (null means NOTHING has ever reached ingest for that key) plus lastRefusalReason and refusedRecently, a 7-day tally by reason. An empty chart with lastEventAt set is a site with no traffic; an empty chart with lastEventAt null is a broken setup. Do not report \"no visitors\" without checking which one you are looking at — call check_analytics_site for the diagnosis.',\n\t\t'',\n\t\t\"Example: list_analytics_sites({}) → { sites: [{ id: 3, domain: 'micci.dk', ingestKey: 'site_…', retentionDays: 30, serviceName: 'micci-web' }] }.\",\n\t].join('\\n'),\n\tinput: {},\n\thandler: async (_args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.api.get<SitesResponse>(`/api/analytics/${teamId}/sites`);\n\t\tconst items = Array.isArray(response.sites) ? response.sites.map(shape) : [];\n\t\tconst summary =\n\t\t\titems.length === 0\n\t\t\t\t? 'No analytics sites yet. Create one with create_analytics_site.'\n\t\t\t\t: `Tracking ${items.length} site${items.length === 1 ? '' : 's'}.`;\n\t\treturn respond({ summary, data: { sites: items } });\n\t},\n});\n\ndefineTool({\n\tname: 'check_analytics_site',\n\tcategory: 'analytics',\n\tdescription: [\n\t\t'Why a site is reporting nothing — the diagnosis, without having to generate traffic and guess.',\n\t\t'',\n\t\t'When to use: any time a chart is empty, a site was just set up, a key was rotated, or someone asks whether the snippet is installed correctly. Call this BEFORE telling anyone their traffic dropped.',\n\t\t'',\n\t\t'The ingest endpoint answers HTTP 204 to a real site key and to a typo alike — deliberately, so it cannot be used to enumerate keys — which means five completely different situations produce the same empty chart: a stale or mistyped key in the deployed snippet, an origin the site does not allow, a blown hourly quota, events blocked in the browser before they leave (CORS, ad blocker, CSP), and simply no visitors yet. This tool tells them apart.',\n\t\t'',\n\t\t\"Returns: health ('receiving' | 'quiet' | 'refusing' | 'never'), a headline and detail sentence, lastEventAt, lastRefusalAt/Reason/Origin, refusedRecently (a 7-day tally by reason), refusedOrigins (which origins were turned away for bad_origin and how often, over the same window), the site key the snippet must carry, allowed origins, and quota usage this hour.\",\n\t\t'',\n\t\t\"Use refusedOrigins, not lastRefusalOrigin, to decide what to allow: lastRefusalOrigin is the newest refusal of ANY reason, and bots outnumber everything, so one crawler hit overwrites the origin behind a real outage. Feed what you find to update_analytics_site — and if the origin is NOT the user's, the site key is public by design and somebody has pasted it into their own page, so the answer is to rotate the key rather than allow them.\",\n\t\t'',\n\t\t\"health='never' means nothing has EVER reached ingest for this key, so the request is not leaving the browser — check the snippet is in the deployed HTML and tell the user to add `data-debug` to the script tag, which makes the tracker log its resolved endpoint and every accepted 204 to the console.\",\n\t\t'',\n\t\t\"Example: check_analytics_site({ domain: 'example.com' }) → { health: 'refusing', headline: 'Reaching us, and being refused.', detail: 'Something on example.com posted 214 event(s) with a site key we do not recognise…', lastEventAt: null, refusedRecently: { unknown_key: 214, bad_origin: 0, bot: 3, over_quota: 0 } }.\",\n\t].join('\\n'),\n\tinput: {\n\t\tdomain: z.string().max(253).describe('Bare hostname of a site this team tracks.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst siteIds = await resolveSiteIds([args.domain], teamId, ctx.api);\n\t\tconst status = await ctx.api.get<Record<string, unknown> & { headline: string }>(\n\t\t\t`/api/analytics/${teamId}/sites/${siteIds}/status`,\n\t\t);\n\t\treturn respond({ summary: `${args.domain}: ${status.headline}`, data: shape(status) });\n\t},\n});\n\ndefineTool({\n\tname: 'get_analytics_summary',\n\tcategory: 'analytics',\n\tdescription: [\n\t\t'One row per site: visitors, pageviews, bounce rate, average visit and live visitors, each with the previous period to compare against.',\n\t\t'',\n\t\t'When to use: \"how are my sites doing\", a weekly check, or spotting which of several domains moved. This is the cross-site view; use get_analytics_overview for the breakdowns behind one of them.',\n\t\t'',\n\t\t\"READ THE VISITOR NUMBER CAREFULLY. Inside a site's raw-event window (35 days by default, so 24h through 30d are raw) `visitors` is a true distinct count over the range. Beyond it the answer comes from a daily rollup and `visitors` is each day's uniques ADDED TOGETHER — someone who visits daily counts once per day. `visitorsAreSummedDailies` says which you got; quote it when reporting a 90d or 12mo number.\",\n\t\t'',\n\t\t'Visitors are never summed ACROSS sites. The same person on two domains is two session ids, and reconciling them would require the cross-domain identity a cookieless tracker exists not to build. Add pageviews if you need a total; do not add visitors.',\n\t\t'',\n\t\t'Inputs: range (24h|7d|30d|90d|12mo, default 7d), domains (optional list; omit for every site).',\n\t\t'',\n\t\t'Returns: the range asked for, and one row per site — domain, `current` and `previous` blocks of visitors / pageviews / bounceRate / avgVisitSeconds, the live visitor count, and `visitorsAreSummedDailies`, which says whether that visitor number is a distinct count or a sum of daily uniques. Quote that flag with any 90d or 12mo figure.',\n\t\t'',\n\t\t\"Example: get_analytics_summary({ range: '30d' }) → { range: '30d', sites: [{ domain: 'hoststack.dev', current: { visitors: 4210, pageviews: 11890, bounceRate: 47.2 }, previous: {…}, live: 3, visitorsAreSummedDailies: false }] }.\",\n\t].join('\\n'),\n\tinput: {\n\t\trange: z.enum(['24h', '7d', '30d', '90d', '12mo']).optional().describe('Default 7d.'),\n\t\tdomains: z\n\t\t\t.array(z.string().max(253))\n\t\t\t.max(50)\n\t\t\t.optional()\n\t\t\t.describe('Bare hostnames. Omit for every site this team tracks.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst siteIds = await resolveSiteIds(args.domains, teamId, ctx.api);\n\t\tconst response = await ctx.api.get<SummaryResponse>(`/api/analytics/${teamId}/summary`, {\n\t\t\trange: args.range ?? '7d',\n\t\t\t...(siteIds ? { siteIds } : {}),\n\t\t});\n\t\tconst sites = Array.isArray(response.sites) ? response.sites : [];\n\t\tconst summed = sites.some((s) => s.visitorsAreSummedDailies);\n\t\tconst summary =\n\t\t\tsites.length === 0\n\t\t\t\t? 'No sites to report on.'\n\t\t\t\t: `${sites.length} site${sites.length === 1 ? '' : 's'} over ${response.range}.${\n\t\t\t\t\t\tsummed\n\t\t\t\t\t\t\t? ' Visitor counts are daily uniques summed, not distinct over the range — this range is past the raw-event window.'\n\t\t\t\t\t\t\t: ''\n\t\t\t\t\t}`;\n\t\treturn respond({ summary, data: { range: response.range, sites: sites.map(shape) } });\n\t},\n});\n\ndefineTool({\n\tname: 'get_analytics_overview',\n\tcategory: 'analytics',\n\tdescription: [\n\t\t'The full breakdown for one site (or several): timeseries, top paths, referrers, custom events, browsers, operating systems, languages, screen sizes, campaigns, devices and countries.',\n\t\t'',\n\t\t'When to use: after get_analytics_summary has told you WHICH site moved, and you need to see what drove it — a referrer that appeared, a campaign that landed, a path that spiked.',\n\t\t'',\n\t\t'Two things change past the raw-event window (35 days by default, so 24h through 30d are raw): `visitors` becomes daily uniques summed rather than a distinct count (`visitorsAreSummedDailies`), and FILTERS ARE IGNORED because the rollup stores per-day aggregates with no events left to filter (`filtersSupported: false`). Check both fields before drawing a conclusion; do not silently present a filtered request that was not filtered.',\n\t\t'',\n\t\t'Inputs: range (24h|7d|30d|90d|12mo, default 7d), domains (optional list; omit for every site), filters (optional map — country, browser, os, device, language, path, referrer, utmSource, utmMedium, utmCampaign, utmTerm, utmContent).',\n\t\t'',\n\t\t\"Returns: `source` ('raw' | 'rollup') and `filtersSupported`, which together say how much of the request was actually honoured; a `summary` block of current-vs-previous totals; a timeseries; and the breakdowns — topPaths, topReferrers, topEvents, browsers, operatingSystems, languages, screenSizes, campaigns, devices and countries. Read `source` and `filtersSupported` before quoting any of it.\",\n\t\t'',\n\t\t\"Example: get_analytics_overview({ domains: ['hoststack.dev'], range: '7d', filters: { country: 'DK' } }) → { source: 'raw', filtersSupported: true, summary: { current: { visitors: 310, … } }, topPaths: [{ path: '/pricing', pageviews: 214 }], topReferrers: [{ referrer: 'Direct', visits: 190 }], … }.\",\n\t].join('\\n'),\n\tinput: {\n\t\trange: z.enum(['24h', '7d', '30d', '90d', '12mo']).optional().describe('Default 7d.'),\n\t\tdomains: z\n\t\t\t.array(z.string().max(253))\n\t\t\t.max(50)\n\t\t\t.optional()\n\t\t\t.describe('Bare hostnames. Omit for every site this team tracks.'),\n\t\tfilters: z\n\t\t\t.record(z.string(), z.string().max(200))\n\t\t\t.optional()\n\t\t\t.describe('Dimension filters. Ignored for ranges past the raw-event window.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst siteIds = await resolveSiteIds(args.domains, teamId, ctx.api);\n\t\tconst response = await ctx.api.get<OverviewResponse>(`/api/analytics/${teamId}/overview`, {\n\t\t\trange: args.range ?? '7d',\n\t\t\t...(siteIds ? { siteIds } : {}),\n\t\t\t...(args.filters ?? {}),\n\t\t});\n\t\tconst { current } = response.summary;\n\t\tconst caveats: string[] = [];\n\t\tif (response.visitorsAreSummedDailies) {\n\t\t\tcaveats.push(\n\t\t\t\t'visitors are daily uniques summed (this range is past the raw-event window)',\n\t\t\t);\n\t\t}\n\t\tif (args.filters && Object.keys(args.filters).length > 0 && !response.filtersSupported) {\n\t\t\tcaveats.push(\n\t\t\t\t'the filters you passed were NOT applied — they only work on shorter ranges',\n\t\t\t);\n\t\t}\n\t\tconst summary = `${formatCount(current.pageviews)} pageviews from ${formatCount(current.visitors)} visitors over ${response.range}${\n\t\t\tcaveats.length > 0 ? `. Note: ${caveats.join('; ')}.` : '.'\n\t\t}`;\n\t\treturn respond({ summary, data: response });\n\t},\n});\n\ndefineTool({\n\tname: 'create_analytics_site',\n\tcategory: 'analytics',\n\tdescription: [\n\t\t'Start tracking a domain, and return the script tag to paste into its <head>.',\n\t\t'',\n\t\t'When to use: the user wants analytics for a site that is not in list_analytics_sites yet. Works for any domain, whether or not it is hosted on HostStack.',\n\t\t'',\n\t\t'If the domain is already attached to a service on this team, the new site links itself to that service and appears on its Analytics tab. Nothing is counted until the snippet is actually on the page — creating the site alone produces an empty dashboard, which is expected, not a fault.',\n\t\t'',\n\t\t'Inputs: domain (required, bare hostname — \"example.com\", not a URL), name (optional display name, defaults to the domain), allowed_origins (optional extra origins beyond the domain and its subdomains), retention_days (optional raw-event retention).',\n\t\t'',\n\t\t'To change any of these later — or to attach the site to a service — use update_analytics_site. Creating is not the only chance to set them.',\n\t\t'',\n\t\t'Returns: the created site (id, domain, name, ingestKey, retentionDays) and the ready-made `snippet` to paste into the page — the key is in that snippet, so hand it over rather than describing it.',\n\t\t'',\n\t\t\"Example: create_analytics_site({ domain: 'micci.dk' }) → { site: { id: 3, domain: 'micci.dk', ingestKey: 'site_…' }, snippet: '<script defer src=\\\"https://hoststack.dev/t.js\\\" data-site-key=\\\"site_…\\\"></script>' }.\",\n\t].join('\\n'),\n\tinput: {\n\t\tdomain: z.string().min(1).max(253).describe('Bare hostname, e.g. example.com'),\n\t\tname: z\n\t\t\t.string()\n\t\t\t.min(1)\n\t\t\t.max(100)\n\t\t\t.optional()\n\t\t\t.describe('Display name. Defaults to the domain.'),\n\t\tallowed_origins: ALLOWED_ORIGINS_INPUT.optional(),\n\t\tretention_days: RETENTION_DAYS_INPUT.optional(),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst site = await ctx.api.post<\n\t\t\tRecord<string, unknown> & { domain: string; ingestKey: string }\n\t\t>(`/api/analytics/${teamId}/sites`, {\n\t\t\tdomain: args.domain,\n\t\t\t...(args.name ? { name: args.name } : {}),\n\t\t\t...(args.allowed_origins ? { allowedOrigins: args.allowed_origins } : {}),\n\t\t\t...(args.retention_days ? { retentionDays: args.retention_days } : {}),\n\t\t});\n\t\tconst snippet = `<script defer src=\"https://hoststack.dev/t.js\" data-site-key=\"${site.ingestKey}\"></script>`;\n\t\treturn respond({\n\t\t\tsummary: `Now tracking ${site.domain}. Paste the snippet into its <head> — nothing is counted until you do.`,\n\t\t\tdata: { site: shape(site), snippet },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'update_analytics_site',\n\tcategory: 'analytics',\n\tdescription: [\n\t\t'Change an existing analytics site: its allowed origins, display name, retention, or which service it belongs to.',\n\t\t'',\n\t\t\"When to use: events are being refused with `bad_origin` because they come from an origin the site does not list (check_analytics_site names it), a site needs a clearer name, retention should change, or the site should appear on a service's Analytics tab.\",\n\t\t'',\n\t\t'Inputs: domain (required — the site to change, by its bare hostname), then any of allowed_origins, name, retention_days, service_id. Everything is optional except domain; only the fields you pass are touched.',\n\t\t'',\n\t\t'allowed_origins REPLACES the list rather than appending, so read the current one from list_analytics_sites first and send it back with your addition. The site domain and its subdomains are always allowed and never need listing.',\n\t\t'',\n\t\t'Returns: { site } — the site as it now stands.',\n\t\t'',\n\t\t\"Example: update_analytics_site({ domain: 'example.com', allowed_origins: ['staging.example.com'] }) → { site: { domain: 'example.com', allowedOrigins: ['staging.example.com'], … } }.\",\n\t].join('\\n'),\n\tinput: {\n\t\tdomain: z.string().min(1).max(253).describe('Bare hostname of a site this team tracks.'),\n\t\tallowed_origins: ALLOWED_ORIGINS_INPUT.optional(),\n\t\tname: z.string().trim().min(1).max(100).optional().describe('New display name.'),\n\t\tretention_days: RETENTION_DAYS_INPUT.optional(),\n\t\tservice_id: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.positive()\n\t\t\t.nullable()\n\t\t\t.optional()\n\t\t\t.describe('Numeric service id to attach this site to, or null to detach it.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst siteId = await resolveSiteIds([args.domain], teamId, ctx.api);\n\n\t\tconst patch: Record<string, unknown> = {};\n\t\tif (args.allowed_origins !== undefined) patch['allowedOrigins'] = args.allowed_origins;\n\t\tif (args.name !== undefined) patch['name'] = args.name;\n\t\tif (args.retention_days !== undefined) patch['retentionDays'] = args.retention_days;\n\t\tif (args.service_id !== undefined) patch['serviceId'] = args.service_id;\n\t\t// A PATCH with nothing in it is a caller mistake, not a no-op worth a\n\t\t// round trip — say which fields exist rather than returning the site\n\t\t// unchanged and letting them think it worked.\n\t\tif (Object.keys(patch).length === 0) {\n\t\t\tthrow new Error(\n\t\t\t\t'Nothing to update. Pass at least one of allowed_origins, name, retention_days or service_id.',\n\t\t\t);\n\t\t}\n\n\t\tconst site = await ctx.api.patch<Record<string, unknown> & { domain: string }>(\n\t\t\t`/api/analytics/${teamId}/sites/${siteId}`,\n\t\t\tpatch,\n\t\t);\n\t\tconst changed = Object.keys(patch).join(', ');\n\t\treturn respond({\n\t\t\tsummary: `Updated ${site.domain} (${changed}).`,\n\t\t\tdata: { site: shape(site) },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'verify_site_domain',\n\tcategory: 'analytics',\n\tdescription: [\n\t\t\"Prove the team owns a site's domain, by publishing a TXT record.\",\n\t\t'',\n\t\t'When to use: before turning on an uptime check for a site that HostStack does not host. Analytics itself needs no proof — the ingest key only labels events the site posts about itself — so do NOT send a user through this just to get a dashboard. It exists for the capabilities where the PLATFORM acts on the hostname.',\n\t\t'',\n\t\t'Call it once with no `check` to get the record to publish, then again with check=true once the record is live. The token is stable across calls, so it is safe to re-read the instructions while the user is mid-paste.',\n\t\t'',\n\t\t'A site whose domain is already verified on a service in this team is proven ALREADY and does not need this — `list_analytics_sites` reports that as domainProven. This is for the site with no service behind it, which can never have such a link.',\n\t\t'',\n\t\t'The record is read from the domain\\'s own authoritative nameservers rather than through a cache, so \"I just added it\" works immediately instead of being masked by a negative-cache TTL.',\n\t\t'',\n\t\t'Inputs: siteId (required), check (optional, default false — true means \"look at DNS now and tell me the verdict\").',\n\t\t'',\n\t\t'Returns: the record to publish plus `verified`. A `verified: false` is a successful check with a negative answer, not an error — most often the record simply has not propagated yet.',\n\t\t'',\n\t\t\"Example: verify_site_domain({ siteId: 3 }) → { recordName: '_hoststack-verify.poststack.dev', recordType: 'TXT', recordValue: 'hoststack-verify=9f3c…', verified: false }.\",\n\t].join('\\n'),\n\tinput: {\n\t\tsiteId: z.number().int().positive(),\n\t\tcheck: z\n\t\t\t.boolean()\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t'True to read DNS now and return the verdict. False just returns the record.',\n\t\t\t),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst instructions = await ctx.api.get<{\n\t\t\trecordName: string;\n\t\t\trecordType: string;\n\t\t\trecordValue: string;\n\t\t\tverified: boolean;\n\t\t}>(`/api/analytics/${teamId}/sites/${args.siteId}/verification`);\n\n\t\tif (!args.check) {\n\t\t\treturn respond({\n\t\t\t\tsummary: instructions.verified\n\t\t\t\t\t? 'Already verified.'\n\t\t\t\t\t: `Publish a ${instructions.recordType} record at ${instructions.recordName} with the value shown, then call again with check=true.`,\n\t\t\t\tdata: shape(instructions),\n\t\t\t});\n\t\t}\n\n\t\tconst verdict = await ctx.api.post<{ verified: boolean; detail?: string }>(\n\t\t\t`/api/analytics/${teamId}/sites/${args.siteId}/verify`,\n\t\t\t{},\n\t\t);\n\t\treturn respond({\n\t\t\tsummary: verdict.verified\n\t\t\t\t? 'Verified. This site can now be given an uptime check.'\n\t\t\t\t: `Not verified yet — ${verdict.detail ?? 'the record was not found'}.`,\n\t\t\tdata: { record: shape(instructions), result: shape(verdict) },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'get_site_uptime_check',\n\tcategory: 'analytics',\n\tdescription: [\n\t\t\"Read a site's uptime check without touching it.\",\n\t\t'',\n\t\t'When to use: answer \"has it probed yet?\", \"is it up?\", \"how many failures in a row?\" — the site counterpart of get_uptime_check.',\n\t\t'',\n\t\t'READ THIS BEFORE REACHING FOR set_site_uptime_check TO INSPECT ONE. The setter RESETS the accumulated state it returns (status back to \"unknown\", consecutiveFailures to 0, lastCheckedAt to null), because a check whose shape just changed has not observed the new shape failing. So using it to look at a check is what stops the check ever showing a probe: every read restarts the measurement, and a working check reads as one that never runs. This tool exists because that trap cost a real investigation an afternoon.',\n\t\t'',\n\t\t\"Returns: { check } — enabled, path, method, expectedStatus, timeoutMs, intervalSeconds, failureThreshold, plus live state: status ('up' | 'down' | 'unknown'), consecutiveFailures, lastCheckedAt, lastStatusCode, lastLatencyMs, lastError, lastChangedAt. `null` when the site has no check.\",\n\t\t'',\n\t\t\"Example: get_site_uptime_check({ siteId: 12 }) → { check: { status: 'up', lastStatusCode: 200, lastCheckedAt: '2026-09-09T05:27:34Z' } }.\",\n\t].join('\\n'),\n\tinput: {\n\t\tsiteId: z.number().int().positive(),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.api.get<{ check: Record<string, unknown> | null }>(\n\t\t\t`/api/analytics/${teamId}/sites/${args.siteId}/uptime-check`,\n\t\t);\n\t\tif (!response.check) {\n\t\t\treturn respond({\n\t\t\t\tsummary: 'No uptime check on this site — nothing is watching it.',\n\t\t\t\tdata: { check: null },\n\t\t\t});\n\t\t}\n\t\tconst check = shape(response.check) as Record<string, unknown>;\n\t\tconst lastCheckedAt = check.lastCheckedAt;\n\t\treturn respond({\n\t\t\tsummary: `Uptime check is ${String(check.status ?? 'unknown')}${\n\t\t\t\tlastCheckedAt ? `, last probed ${String(lastCheckedAt)}` : ', not probed yet'\n\t\t\t}.`,\n\t\t\tdata: { check },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'set_site_uptime_check',\n\tcategory: 'analytics',\n\tdescription: [\n\t\t'Watch a site HostStack does not host — request its URL on a schedule and alert when it stops answering.',\n\t\t'',\n\t\t'When to use: the user has a site running somewhere else (their own box, another provider) and wants to know when it goes down. This is the one observability capability an off-platform site cannot provide for itself: analytics is a script tag and error reporting is an HTTP POST, but an outside-in probe has to come from outside.',\n\t\t'',\n\t\t\"Works on a site HostStack DOES host too, and that is not a worse option: this check names ONE hostname — the site's own proven domain — where set_uptime_check follows whichever of the service's domains is primary at probe time. A service answering on a canonical host plus a 301 alias has no single correct expectedStatus, so a site check per hostname is how each one gets asserted, and nothing in the prober treats a service-backed site differently. Use set_uptime_check when the check should follow the service's domain; use this one when a named host is the point.\",\n\t\t'',\n\t\t'To READ a check, call get_site_uptime_check. Changing the shape of a check RESETS its accumulated state (status to \"unknown\", consecutiveFailures to 0, lastCheckedAt to null) — the new shape has not been measured yet, so carrying failures forward would alert about a condition nobody observed. A write that resolves to the shape ALREADY STORED changes nothing and leaves that state alone. The inputs are defaults rather than a partial edit, though: omitting intervalSeconds on a check currently set to 30 resolves to 60, which IS a change.',\n\t\t'',\n\t\t'REQUIRES a proven domain. Call verify_site_domain first if `domainProven` is false; an unverified target is refused with 400. This is not paperwork: an uptime check makes the control plane fetch the hostname every interval, forever, from our IP, so it must be a name the team has shown it owns.',\n\t\t'',\n\t\t\"Always https, and the host is the site's domain — `path` is a path, not a URL. Redirects are NOT followed: a 301 is an answer, and following one can walk the probe onto a parked page and report a dead site as healthy. Set expectedStatus to the redirect if one is expected.\",\n\t\t'',\n\t\t'Inputs (all optional except siteId): enabled, path (default \"/\"), method (GET|HEAD), expectedStatus (default 200), timeoutMs (1000–60000, default 10000 — this is what catches a hung server), intervalSeconds (30–3600, default 60), failureThreshold (1–10, default 3).',\n\t\t'',\n\t\t\"Returns: the stored check — enabled, the resolved target URL, method, expectedStatus, timeoutMs, intervalSeconds, failureThreshold — plus its live state: status ('up' | 'down' | 'unknown'), lastCheckedAt, lastStatusCode and consecutiveFailures. A freshly created check is 'unknown' until the first probe runs.\",\n\t\t'',\n\t\t\"Example: set_site_uptime_check({ siteId: 3, path: '/health', intervalSeconds: 60 }) → { check: { status: 'unknown', … } }.\",\n\t].join('\\n'),\n\tinput: {\n\t\tsiteId: z.number().int().positive(),\n\t\tenabled: z.boolean().optional(),\n\t\tpath: z.string().max(500).optional().describe('Must start with /. Default \"/\".'),\n\t\tmethod: z.enum(['GET', 'HEAD']).optional(),\n\t\texpectedStatus: z.number().int().min(100).max(599).optional(),\n\t\ttimeoutMs: z.number().int().min(1000).max(60_000).optional(),\n\t\tintervalSeconds: z.number().int().min(30).max(3600).optional(),\n\t\tfailureThreshold: z.number().int().min(1).max(10).optional(),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst { siteId, ...body } = args;\n\t\tconst response = await ctx.api.put<{ check: Record<string, unknown> }>(\n\t\t\t`/api/analytics/${teamId}/sites/${siteId}/uptime-check`,\n\t\t\tbody,\n\t\t);\n\t\tconst check = shape(response.check) as Record<string, unknown>;\n\t\t// Say which of the two happened, because 'unknown' with a null lastCheckedAt\n\t\t// is exactly what a never-probed check and a just-reset one look like — and\n\t\t// reading that as \"it does not work\" is the mistake this wording exists to\n\t\t// prevent. A non-null lastCheckedAt means the write changed nothing, so the\n\t\t// state survived.\n\t\tconst probed = check['lastCheckedAt'] !== null && check['lastCheckedAt'] !== undefined;\n\t\treturn respond({\n\t\t\tsummary: probed\n\t\t\t\t? `Uptime check saved — the shape was already stored, so nothing was reset: still ${String(check['status'])}, last probed ${String(check['lastCheckedAt'])}.`\n\t\t\t\t: 'Uptime check saved, and not probed yet — \"unknown\" with no lastCheckedAt is the state of a check that has not run once, not a broken one. It reports within a minute or two: read it with get_site_uptime_check, never by saving it again.',\n\t\t\tdata: { check },\n\t\t});\n\t},\n});\n","/**\n * Number rendering for the text summaries this server returns.\n *\n * Pinned to the platform's locale rather than the host machine's:\n * `toLocaleString()` with no argument formats in whatever locale the process\n * happens to run under, so the same summary reached one agent as \"1.762\n * pageviews\" and another as \"1,762\" — and the first reads as one-point-seven\n * to a model that has no `LANG` to interpret it by.\n *\n * Copied from `@hoststack/shared`'s `APP_LOCALE` rather than imported: this\n * package publishes to npm and `@hoststack/shared` is `private: true`, so\n * nothing the bundle reaches may import it. `shared-parity.test.ts` — which\n * the bundle does NOT reach — compares the two.\n */\nexport const MCP_LOCALE = 'en-IE';\n\nconst COUNT_FORMAT = new Intl.NumberFormat(MCP_LOCALE);\n\n/** A plain count with thousands separators, e.g. 1762 → \"1,762\". */\nexport function formatCount(value: number): string {\n\treturn COUNT_FORMAT.format(value);\n}\n","import { z } from 'zod';\n\nimport { respond } from '../lib/respond.js';\nimport { shape } from '../lib/shape.js';\nimport { defineTool } from '../registry.js';\n\ninterface IssuesResponse {\n\tissues: unknown[];\n\ttotal: number;\n}\n\ninterface IssueResponse {\n\tissue: Record<string, unknown>;\n}\n\ninterface OccurrencesResponse {\n\toccurrences: unknown[];\n}\n\ninterface FixResponse {\n\ttask: { publicId?: string; title?: string; body?: string };\n\talreadyExisted: boolean;\n\tbox: { serviceId: number; name: string; asleep: boolean };\n\tautomodeEnabled: boolean;\n}\n\ninterface IngestKeysResponse {\n\tkeys: unknown[];\n}\n\ninterface IngestKeyResponse {\n\tkey: Record<string, unknown> & { key?: string };\n}\n\ndefineTool({\n\tname: 'list_error_issues',\n\tcategory: 'errors',\n\tdescription: [\n\t\t\"List error issues for the team — exceptions the team's own applications reported, grouped by cause rather than listed one per event.\",\n\t\t'',\n\t\t'When to use: \"what is throwing in production\", triaging after a deploy, or finding the bug behind a user report. Pairs with list_alerts (which tells you an alert fired) and get_service_logs (which shows the raw output around it).',\n\t\t'',\n\t\t'Grouping: an issue is one distinct problem. Its fingerprint is the exception class, the message with variable parts removed (so \"user 41 not found\" and \"user 9002 not found\" are ONE issue), and the topmost stack frame in the team\\'s own code — not the framework\\'s. `culprit` is that frame, and it is the line to open first.',\n\t\t'',\n\t\t'Counts are exact; occurrence samples are not. `occurrenceCount` is the true number of times it happened. A bounded number of full occurrences is retained per issue (see get_error_issue), and `droppedCount` is how many were counted but not stored because the service exceeded its hourly ingest quota.',\n\t\t'',\n\t\t'Defaults to status=unresolved, because the list exists to answer \"what is broken\". Pass status=resolved or status=ignored for the others.',\n\t\t'',\n\t\t'Inputs (all optional): serviceId, status (unresolved|resolved|ignored), q (substring match on title/culprit), limit (default 50, max 200), offset, sort (last_seen|first_seen|count).',\n\t\t'',\n\t\t'Returns: { issues: Array, total: number }. Each issue has id, publicId, serviceId, serviceName, type, title, culprit, level, status, occurrenceCount, droppedCount, affectedUsers (capped at 500 — 500 means \"500 or more\"), firstSeenAt, lastSeenAt, firstSeenRelease, lastSeenRelease, environment, sourceTaskPublicId (a dev-box task already open for it).',\n\t\t'',\n\t\t\"Example: list_error_issues({ serviceId: 48, sort: 'count' }) → { issues: [{ id: 12, type: 'TypeError', title: 'TypeError: cart.total is not a function', culprit: 'src/checkout.ts:12 in checkout', occurrenceCount: 4187, affectedUsers: 96, lastSeenRelease: 'a91f3c2', … }], total: 3 }.\",\n\t].join('\\n'),\n\tinput: {\n\t\tserviceId: z.number().int().positive().optional().describe('Only issues for this service.'),\n\t\tstatus: z\n\t\t\t.enum(['unresolved', 'resolved', 'ignored'])\n\t\t\t.optional()\n\t\t\t.describe('Default unresolved.'),\n\t\tq: z.string().max(200).optional().describe('Substring match on title or culprit.'),\n\t\tlimit: z.number().int().positive().max(200).optional().describe('Default 50, cap 200.'),\n\t\toffset: z.number().int().min(0).optional(),\n\t\tsort: z\n\t\t\t.enum(['last_seen', 'first_seen', 'count'])\n\t\t\t.optional()\n\t\t\t.describe('Default last_seen.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst params: Record<string, string | number | undefined> = {};\n\t\tfor (const key of ['serviceId', 'status', 'q', 'limit', 'offset', 'sort'] as const) {\n\t\t\tconst value = args[key];\n\t\t\tif (value !== undefined) params[key] = String(value);\n\t\t}\n\t\tconst response = await ctx.api.get<IssuesResponse>(`/api/errors/${teamId}/issues`, params);\n\t\tconst items = Array.isArray(response.issues) ? response.issues.map(shape) : [];\n\t\tconst summary =\n\t\t\titems.length === 0\n\t\t\t\t? `No ${args.status ?? 'unresolved'} error issues${args.serviceId ? ' for that service' : ''}.`\n\t\t\t\t: `Returned ${items.length} of ${response.total} ${args.status ?? 'unresolved'} issue${response.total === 1 ? '' : 's'}.`;\n\t\treturn respond({ summary, data: { issues: items, total: response.total } });\n\t},\n});\n\ndefineTool({\n\tname: 'get_error_issue',\n\tcategory: 'errors',\n\tdescription: [\n\t\t'Fetch one error issue plus its most recent stored occurrences — the stack traces, request context and releases behind the aggregate.',\n\t\t'',\n\t\t'When to use: after list_error_issues has told you WHICH problem to look at, and you need the actual stack to reason about the cause.',\n\t\t'',\n\t\t'Occurrences are SAMPLES, not the full history: a bounded number is kept per issue while the count stays exact, and they age out after 30 days while the issue itself remains. An issue whose samples have expired returns an empty occurrences array — that is normal, not an error.',\n\t\t'',\n\t\t'Each occurrence has stack (raw, as the runtime printed it), context (tenant-supplied JSON, with sensitive keys already redacted), requestId, release, environment and createdAt.',\n\t\t'',\n\t\t'Inputs: issueId (required), occurrences (how many samples, default 5, max 50).',\n\t\t'',\n\t\t'Returns: { issue: {...}, occurrences: Array }.',\n\t\t'',\n\t\t\"Example: get_error_issue({ issueId: 12 }) → { issue: { title: 'TypeError: cart.total is not a function', occurrenceCount: 4187, … }, occurrences: [{ stack: ' at checkout (/app/src/checkout.ts:12:9)…', requestId: 'req_9f3c', release: 'a91f3c2', createdAt: '…' }] }.\",\n\t].join('\\n'),\n\tinput: {\n\t\tissueId: z.number().int().positive().describe('Numeric issue id from list_error_issues.'),\n\t\toccurrences: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.positive()\n\t\t\t.max(50)\n\t\t\t.optional()\n\t\t\t.describe('How many samples to include. Default 5.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst [issueRes, occRes] = await Promise.all([\n\t\t\tctx.api.get<IssueResponse>(`/api/errors/${teamId}/issues/${args.issueId}`),\n\t\t\tctx.api.get<OccurrencesResponse>(\n\t\t\t\t`/api/errors/${teamId}/issues/${args.issueId}/occurrences`,\n\t\t\t\t{ limit: String(args.occurrences ?? 5) },\n\t\t\t),\n\t\t]);\n\t\tconst occurrences = Array.isArray(occRes.occurrences) ? occRes.occurrences.map(shape) : [];\n\t\tconst issue = shape(issueRes.issue) as Record<string, unknown>;\n\t\treturn respond({\n\t\t\tsummary: `${String(issue['title'] ?? 'Issue')} — ${String(issue['occurrenceCount'] ?? 0)} occurrence(s), ${occurrences.length} sample(s) available.`,\n\t\t\tdata: { issue, occurrences },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'update_error_issue',\n\tcategory: 'errors',\n\tdescription: [\n\t\t'Resolve, ignore or reopen an error issue.',\n\t\t'',\n\t\t'When to use: after fixing a bug (resolve, so a recurrence is reported as a regression), or to silence noise you have decided to live with (ignore).',\n\t\t'',\n\t\t'`resolved` records the release it was resolved in. If the issue occurs again afterwards it reopens itself and fires an `error.issue_regressed` alert — which is the point of resolving rather than ignoring, and the reason not to resolve something you have not actually fixed.',\n\t\t'',\n\t\t'`ignored` keeps counting occurrences and stops telling anyone. An ignored issue never reopens itself, so this is the right choice for noise you have decided to live with, and the wrong choice for something you intend to fix.',\n\t\t'',\n\t\t'`unresolved` reopens it.',\n\t\t'',\n\t\t'Inputs: issueId (required), status (required).',\n\t\t'',\n\t\t'Returns: { issue: {...} } with the updated row.',\n\t\t'',\n\t\t\"Example: update_error_issue({ issueId: 12, status: 'resolved' }) → { issue: { status: 'resolved', resolvedInRelease: 'a91f3c2', … } }.\",\n\t].join('\\n'),\n\tinput: {\n\t\tissueId: z.number().int().positive(),\n\t\tstatus: z.enum(['unresolved', 'resolved', 'ignored']),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.api.patch<IssueResponse>(\n\t\t\t`/api/errors/${teamId}/issues/${args.issueId}`,\n\t\t\t{ status: args.status },\n\t\t);\n\t\treturn respond({\n\t\t\tsummary: `Issue ${args.issueId} is now ${args.status}.`,\n\t\t\tdata: { issue: shape(response.issue) },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'fix_error_in_dev_box',\n\tcategory: 'errors',\n\tdescription: [\n\t\t\"Turn an error issue into a coding-agent task in the project's dev box, with the repository already checked out.\",\n\t\t'',\n\t\t\"When to use: the issue is real, it is in the team's own code, and somebody is going to have to open the repository anyway. This writes the whole briefing so nobody has to reconstruct it from the dashboard.\",\n\t\t'',\n\t\t\"This is the thing a third-party error tracker structurally cannot do. The task carries the exception, the stack with the team's OWN frames marked (`>>`), the release it happened on, how often and to how many users, and a real request context — plus hard boundaries: work on a branch, do not deploy, do not delete tests, and stop and say so if the cause turns out not to be in this repository.\",\n\t\t'',\n\t\t\"It CREATES the task; it does not start the agent. Running spends the team's own agent tokens and is a separate decision made in the dev box UI.\",\n\t\t'',\n\t\t'Refused with 422 when the cause cannot be attributed to the repository — every stack frame inside a dependency, no usable stack at all, a browser extension, or a connection failure with no in-app frame. That gate is deliberate: an agent told to fix something it cannot reach does not refuse, it produces a confident and useless diff.',\n\t\t'',\n\t\t'Pressing this twice for one issue returns the existing task (alreadyExisted=true) rather than queueing a duplicate run.',\n\t\t'',\n\t\t\"Inputs: issueId (required), serviceId (optional — pin to a specific dev box; omitted, the project's box is used).\",\n\t\t'',\n\t\t'Returns: { task, alreadyExisted, box: { serviceId, name, asleep }, automodeEnabled }.',\n\t\t'',\n\t\t\"Example: fix_error_in_dev_box({ issueId: 12 }) → { task: { title: 'Fix error: TypeError in src/checkout.ts:12' }, alreadyExisted: false, box: { name: 'shop-dev', asleep: true }, automodeEnabled: false }.\",\n\t].join('\\n'),\n\tinput: {\n\t\tissueId: z.number().int().positive(),\n\t\tserviceId: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.positive()\n\t\t\t.optional()\n\t\t\t.describe(\"Dev box to pin the task to. Omitted, the project's box is used.\"),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst body: Record<string, number> = { issueId: args.issueId };\n\t\tif (args.serviceId !== undefined) body['serviceId'] = args.serviceId;\n\t\tconst response = await ctx.api.post<FixResponse>(\n\t\t\t`/api/dev-env-tasks/${teamId}/from-issue`,\n\t\t\tbody,\n\t\t);\n\t\tconst verb = response.alreadyExisted ? 'Already queued' : 'Queued';\n\t\tconst note = response.box.asleep\n\t\t\t? ' That box is asleep — resume it before running the task.'\n\t\t\t: '';\n\t\treturn respond({\n\t\t\tsummary: `${verb} in dev box \"${response.box.name}\": ${response.task.title ?? 'task'}.${note}`,\n\t\t\tdata: shape(response),\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'list_ingest_keys',\n\tcategory: 'errors',\n\tdescription: [\n\t\t\"List a service's error-ingest keys. Only prefixes and last-used timestamps — the key itself is stored as a hash and is never readable again.\",\n\t\t'',\n\t\t'When to use: checking whether a service is wired up for error reporting at all, or whether an old key is still in use before revoking it (`lastUsedAt` is stamped lazily, at most once a minute).',\n\t\t'',\n\t\t'Inputs: serviceId (required).',\n\t\t'',\n\t\t'Returns: { keys: Array } with id, publicId, name, prefix, lastUsedAt, createdAt.',\n\t\t'',\n\t\t\"Example: list_ingest_keys({ serviceId: 48 }) → { keys: [{ id: 3, name: 'default', prefix: 'ing_9f3c1ab2', lastUsedAt: '2026-08-21T09:14:00Z' }] }.\",\n\t].join('\\n'),\n\tinput: { serviceId: z.number().int().positive() },\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.api.get<IngestKeysResponse>(\n\t\t\t`/api/services/${teamId}/${args.serviceId}/ingest-keys`,\n\t\t);\n\t\tconst keys = Array.isArray(response.keys) ? response.keys.map(shape) : [];\n\t\treturn respond({\n\t\t\tsummary:\n\t\t\t\tkeys.length === 0\n\t\t\t\t\t? 'No ingest keys on this service — it cannot report errors yet.'\n\t\t\t\t\t: `${keys.length} ingest key${keys.length === 1 ? '' : 's'} on this service.`,\n\t\t\tdata: { keys },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'create_ingest_key',\n\tcategory: 'errors',\n\tdescription: [\n\t\t'Mint a write-only error-ingest key for a service, so its application can report exceptions.',\n\t\t'',\n\t\t'When to use: setting a service up for error tracking for the first time, or rotating a key that has leaked or is being retired.',\n\t\t'',\n\t\t'The response is the ONLY place the plaintext key ever exists — it is stored as a SHA-256 hash and cannot be shown again. Put it wherever the application reads it from (an env var, usually) before discarding the response.',\n\t\t'',\n\t\t'The key is deliberately low-privilege: it can create error events for this ONE service and can do nothing else — it cannot read the issues it created, list services, or touch anything on the team. That is why shipping it inside the application, including a browser bundle where it is world-readable, is expected rather than a mistake.',\n\t\t'',\n\t\t'Reporting is a plain HTTP POST, no SDK required: POST {baseUrl}/api/ingest/errors/{key} with {\"events\":[{\"type\",\"value\",\"stack\",\"level\",\"context\"}]}, up to 100 events per request.',\n\t\t'',\n\t\t'Inputs: serviceId (required), name (optional label, default \"default\"). Max 5 keys per service — the extras exist so a key can be rotated without downtime (create the second, ship it, delete the first).',\n\t\t'',\n\t\t'Returns: { key: { id, publicId, name, prefix, key } } where `key` is the plaintext.',\n\t\t'',\n\t\t\"Example: create_ingest_key({ serviceId: 48 }) → { id: 4, name: 'default', prefix: 'ing_1b7d40ae', key: 'ing_1b7d40ae…' }.\",\n\t].join('\\n'),\n\tinput: {\n\t\tserviceId: z.number().int().positive(),\n\t\tname: z.string().min(1).max(100).optional().describe('Label. Default \"default\".'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.api.post<IngestKeyResponse>(\n\t\t\t`/api/services/${teamId}/${args.serviceId}/ingest-keys`,\n\t\t\t{ name: args.name ?? 'default' },\n\t\t);\n\t\treturn respond({\n\t\t\tsummary:\n\t\t\t\t'Ingest key created. The plaintext key is in the payload and is not retrievable again — store it now.',\n\t\t\tdata: shape(response.key),\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'delete_ingest_key',\n\tcategory: 'errors',\n\tdescription: [\n\t\t'Revoke an error-ingest key. It stops working immediately, including on API replicas that had it cached.',\n\t\t'',\n\t\t'When to use: finishing a key rotation, or containing a key that leaked somewhere it should not have.',\n\t\t'',\n\t\t'Anything still posting with it starts getting 401s, so revoke the OLD key after the new one is deployed, not before.',\n\t\t'',\n\t\t'Inputs: serviceId, keyId (both required).',\n\t\t'',\n\t\t'Returns: { success: true }.',\n\t\t'',\n\t\t'Example: delete_ingest_key({ serviceId: 48, keyId: 3 }) → { success: true }.',\n\t].join('\\n'),\n\tinput: {\n\t\tserviceId: z.number().int().positive(),\n\t\tkeyId: z.number().int().positive(),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tawait ctx.api.delete(`/api/services/${teamId}/${args.serviceId}/ingest-keys/${args.keyId}`);\n\t\treturn respond({ summary: `Ingest key ${args.keyId} revoked.` });\n\t},\n});\n","import { z } from 'zod';\n\nimport { respond, respondError } from '../lib/respond.js';\nimport { defineTool } from '../registry.js';\n\ninterface Installation {\n\tid: number;\n\taccountLogin: string;\n}\n\ninterface SyncedRepo {\n\tid: number;\n\tfullName: string;\n}\n\ninterface ConnectedRepo {\n\tid: number;\n\tfullName: string;\n\tname: string;\n\tdefaultBranch: string;\n\tisPrivate: boolean;\n\thtmlUrl: string;\n\tlanguage: string | null;\n\taccountLogin: string;\n}\n\n/** The agent-facing shape: the id create_service wants, and enough to pick it. */\nfunction shapeRepo(repo: ConnectedRepo) {\n\treturn {\n\t\tid: repo.id,\n\t\tfullName: repo.fullName,\n\t\tdefaultBranch: repo.defaultBranch,\n\t\tprivate: repo.isPrivate,\n\t\turl: repo.htmlUrl,\n\t\t...(repo.language ? { language: repo.language } : {}),\n\t};\n}\n\ndefineTool({\n\tname: 'list_github_repos',\n\tcategory: 'github',\n\tdescription: [\n\t\t'Every GitHub repository this team can deploy, with the `id` create_service takes — and the `owner/name` you can pass instead of it.',\n\t\t'',\n\t\t'When to use: before create_service or create_standalone_dev_environment with a repo source. `github_repo_id` is a HostStack row id, NOT the id GitHub shows — it appears in no clone URL and no GitHub API response, so it cannot be derived and must not be guessed. (Guessing used to be survivable-looking: the create returned 200 and the mistake surfaced one step later as `Invalid git clone URL` from the deploy. It is now refused at create time.)',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - q (optional): case-insensitive substring of the full name (\"owner/name\"). Omit for everything.',\n\t\t'',\n\t\t'Returns: { items: [{ id, fullName, defaultBranch, private, url, language? }], total } — `total` is the count before `q` filtered it.',\n\t\t'',\n\t\t'If the repo you just pushed is missing, call sync_github_repos first: HostStack cannot see a brand-new repository until an installation re-sync runs.',\n\t\t'',\n\t\t'Example: list_github_repos({ q: \"stenshoppen\" }) → { items: [{ id: 143, fullName: \"acme/stenshoppen\", defaultBranch: \"master\", private: true }], total: 45 }',\n\t\t'Then either: create_service({ …, github_repo: \"acme/stenshoppen\" }) — or the id, if you prefer.',\n\t].join('\\n'),\n\tinput: {\n\t\tq: z\n\t\t\t.string()\n\t\t\t.min(1)\n\t\t\t.max(200)\n\t\t\t.optional()\n\t\t\t.describe('Case-insensitive substring of \"owner/name\". Omit to list everything.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst { repos } = await ctx.api.get<{ repos: ConnectedRepo[] }>(\n\t\t\t`/api/github/${teamId}/repos`,\n\t\t);\n\n\t\tconst needle = args.q?.trim().toLowerCase();\n\t\tconst matched = needle\n\t\t\t? repos.filter((r) => r.fullName.toLowerCase().includes(needle))\n\t\t\t: repos;\n\t\tconst items = matched.map(shapeRepo).sort((a, b) => a.fullName.localeCompare(b.fullName));\n\n\t\tif (repos.length === 0) {\n\t\t\treturn respond({\n\t\t\t\tsummary:\n\t\t\t\t\t'No GitHub repositories are connected to this team. Install the HostStack GitHub App from the dashboard (Settings → GitHub), then call sync_github_repos.',\n\t\t\t\tdata: { items, total: 0 },\n\t\t\t});\n\t\t}\n\t\tconst summary = needle\n\t\t\t? items.length === 0\n\t\t\t\t? `No connected repository matches \"${args.q}\" (${repos.length} connected). Push it, then call sync_github_repos.`\n\t\t\t\t: `${items.length} of ${repos.length} connected repositories match \"${args.q}\".`\n\t\t\t: `${items.length} connected repositor${items.length === 1 ? 'y' : 'ies'}.`;\n\t\treturn respond({ summary, data: { items, total: repos.length } });\n\t},\n});\n\ndefineTool({\n\tname: 'sync_github_repos',\n\tcategory: 'github',\n\tdescription: [\n\t\t'Re-sync the repository list from GitHub for every connected GitHub App installation, and return what that now exposes.',\n\t\t'',\n\t\t\"When to use: right after pushing a brand-new repository, HostStack cannot see it until an installation re-sync runs — so create_service with a github_repo_id or github_repo would fail. Call this first. (Same operation as the dashboard's refresh icon on the repo picker.) To read the list WITHOUT re-syncing, call list_github_repos.\",\n\t\t'',\n\t\t'No inputs.',\n\t\t'',\n\t\t'Returns: { installations: number, accounts: [{ account, count }], totalRepos, repos: [{ id, fullName }] } — `repos` carries the ids create_service takes, so a sync is enough to act on; it does not have to be followed by a list.',\n\t\t'',\n\t\t'Example: sync_github_repos({}) → { installations: 1, accounts: [{ account: \"acme\", count: 12 }], totalRepos: 12, repos: [{ id: 143, fullName: \"acme/stenshoppen\" }, …] }',\n\t].join('\\n'),\n\tinput: {},\n\thandler: async (_args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst { installations } = await ctx.api.get<{ installations: Installation[] }>(\n\t\t\t`/api/github/${teamId}/installations`,\n\t\t);\n\t\tif (installations.length === 0) {\n\t\t\treturn respondError(\n\t\t\t\t'No GitHub installations connected. Install the HostStack GitHub App from the dashboard (Settings → GitHub) first.',\n\t\t\t);\n\t\t}\n\n\t\tconst perAccount: { account: string; count: number }[] = [];\n\t\tconst synced: { id: number; fullName: string }[] = [];\n\t\tfor (const inst of installations) {\n\t\t\tconst { repos } = await ctx.api.post<{ repos: SyncedRepo[] }>(\n\t\t\t\t`/api/github/${teamId}/installations/${inst.id}/sync`,\n\t\t\t);\n\t\t\tperAccount.push({ account: inst.accountLogin, count: repos.length });\n\t\t\tfor (const repo of repos) synced.push({ id: repo.id, fullName: repo.fullName });\n\t\t}\n\t\tsynced.sort((a, b) => a.fullName.localeCompare(b.fullName));\n\n\t\treturn respond({\n\t\t\tsummary: `Synced ${synced.length} repositories across ${installations.length} installation(s).`,\n\t\t\tdata: {\n\t\t\t\tinstallations: installations.length,\n\t\t\t\taccounts: perAccount,\n\t\t\t\ttotalRepos: synced.length,\n\t\t\t\trepos: synced,\n\t\t\t},\n\t\t});\n\t},\n});\n","import { z } from 'zod';\n\nimport { defineTool } from '../registry.js';\nimport { respond } from '../lib/respond.js';\nimport { shape } from '../lib/shape.js';\n\ninterface ReportIssueResponse {\n\tticket: { id: number; publicId: string };\n}\n\ndefineTool({\n\tname: 'report_issue',\n\tcategory: 'support',\n\tdescription: [\n\t\t'File a bug or platform fault with the HostStack team. Use this when something on the PLATFORM is broken in a way you cannot fix from where you stand — a deploy that fails identically no matter what you change, a dev box that will not start, an API that returns the wrong thing, an error message that does not match what actually happened.',\n\t\t'',\n\t\t'When to use: you have already looked. This is not a substitute for reading logs (get_deploy_logs, get_service_logs) or for diagnose_deploy — it is what you do once you have a specific fault and no way to act on it. A report that says \"the deploy failed\" is worth less than no report; one that says \"every deploy since 09:03 fails at container create with 404 No such image, but the image pulls fine by hand\" is what gets fixed.',\n\t\t'',\n\t\t\"When NOT to use: an application bug in the user's own code, a failing test, a misconfigured env var, or anything the user asked you to change. Those are your job, not a platform fault. Do not file the same issue twice — if you already reported it this session, say so instead.\",\n\t\t'',\n\t\t'Diagnostic context is attached SERVER-SIDE, so you do not need to paste it: passing `serviceId` attaches the service and its most recent deploy, and the last 40 lines of that deploy log go with it. Pass `deployId` to pin a specific deploy instead of the newest. Describe what you observed and what you expected — the evidence is collected for you.',\n\t\t'',\n\t\t'Inputs: title (required, one line), description (required — what happened, what you expected, what you already ruled out), severity (low|normal|high|urgent, default normal), serviceId (optional), deployId (optional), databaseId (optional).',\n\t\t'',\n\t\t'Rate-limited to 5 reports per minute per team. Filing one notifies the HostStack team and opens a ticket the user can see and reply to.',\n\t\t'',\n\t\t'Returns: { ticket: { id, publicId } } — quote the publicId to the user so they can follow it up.',\n\t\t'',\n\t\t\"Example: report_issue({ title: 'Dev box deploys fail with 404 No such image', description: 'Every deploy of svc 172 since 2026-08-22 09:03 fails at container create with `404 No such image: …/dev-env:latest`. The deploy log reports \\\"Image ready in 1s\\\" for a 5.5 GB image, so the pull is not happening. The image pulls fine by hand from the same registry.', severity: 'high', serviceId: 172 }) → { ticket: { publicId: 'tkt_…' } }\",\n\t].join('\\n'),\n\tinput: {\n\t\ttitle: z.string().min(1).max(300).describe('One-line summary of the fault.'),\n\t\tdescription: z\n\t\t\t.string()\n\t\t\t.min(1)\n\t\t\t.max(10_000)\n\t\t\t.describe('What happened, what you expected instead, and what you already ruled out.'),\n\t\tseverity: z\n\t\t\t.enum(['low', 'normal', 'high', 'urgent'])\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t'Default normal. Use high/urgent only when something is DOWN or losing data — not for \"this is annoying\".',\n\t\t\t),\n\t\tserviceId: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.positive()\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t'The affected service. Attaches the service, its latest deploy, and that deploy log tail automatically.',\n\t\t\t),\n\t\tdeployId: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.positive()\n\t\t\t.optional()\n\t\t\t.describe('Pin a specific deploy instead of the service’s most recent one.'),\n\t\tdatabaseId: z.number().int().positive().optional().describe('The affected database.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst body: Record<string, unknown> = {\n\t\t\ttitle: args.title,\n\t\t\tdescription: args.description,\n\t\t\tseverity: args.severity ?? 'normal',\n\t\t};\n\t\tif (args.serviceId !== undefined) body['serviceId'] = args.serviceId;\n\t\tif (args.deployId !== undefined) body['deployId'] = args.deployId;\n\t\tif (args.databaseId !== undefined) body['databaseId'] = args.databaseId;\n\n\t\tconst response = await ctx.api.post<ReportIssueResponse>(\n\t\t\t`/api/issue-reports/${teamId}`,\n\t\t\tbody,\n\t\t);\n\t\treturn respond({\n\t\t\tsummary: `Issue reported as ${response.ticket.publicId}. Tell the user the reference so they can follow it up — and do not file this one again.`,\n\t\t\tdata: shape(response.ticket),\n\t\t});\n\t},\n});\n","import { defineTool, listToolDefinitions } from '../registry.js';\nimport { respond } from '../lib/respond.js';\nimport { shapeTeam, shapeUser } from '../lib/shape.js';\n// Reuse the build-time-injected package version (the same value the MCP\n// `initialize` handshake advertises — see server-factory's PACKAGE_VERSION,\n// which is itself just MCP_VERSION). Importing from ../version.js keeps this in\n// one place and avoids an @hoststack/shared import (workspace-trap: it would\n// drag the whole monorepo into the published package).\nimport { MCP_VERSION } from '../version.js';\n\ninterface MeResponse {\n\tuser: unknown;\n\tteam?: unknown;\n}\n\n/**\n * The Dev Box tools this MCP build ships. A driving agent can compare this list\n * (returned by `describe_mcp`) against what its client actually surfaced to\n * detect a STALE npx/npm cache silently presenting an old tool set.\n *\n * KEEP IN SYNC with the tool names registered in ./services.ts. We list the\n * names as a literal (rather than filtering the registry by category) so the\n * marker stays meaningful even if the underlying categories are reshuffled —\n * these are the dev-env capabilities a caller most often finds missing.\n */\nconst DEV_ENV_TOOL_NAMES = [\n\t'create_dev_environment',\n\t'create_standalone_dev_environment',\n\t'spin_up_dev_environment',\n\t'list_dev_environments',\n\t'resize_dev_environment',\n\t'delete_dev_environment',\n] as const;\n\ndefineTool({\n\tname: 'get_me',\n\tcategory: 'meta',\n\tdescription: [\n\t\t'Return the user and team identity bound to the current API key. Useful at the start of a conversation to confirm which account the agent is operating on, especially when a user has multiple HostStack environments.',\n\t\t'',\n\t\t'When to use: orient yourself at the start of a session, confirm which team the API key targets, or surface the email/team-name in a reply to the user.',\n\t\t'',\n\t\t'Returns: { user: { id, email, name, ... }, team?: { id, publicId, name, plan, ... } }.',\n\t\t'',\n\t\t'Example: get_me() → { user: { id: 1, email: \"ada@…\", … }, team: { id: 7, name: \"acme\", plan: \"pro\" } }',\n\t].join('\\n'),\n\tinput: {},\n\thandler: async (_args, ctx) => {\n\t\tconst me = await ctx.api.get<MeResponse>('/api/auth/me');\n\t\tconst data: { user: ReturnType<typeof shapeUser>; team?: ReturnType<typeof shapeTeam> } = {\n\t\t\tuser: shapeUser(me.user),\n\t\t};\n\t\tif (me.team) data.team = shapeTeam(me.team);\n\t\tconst teamLabel =\n\t\t\tme.team && typeof me.team === 'object' && 'name' in me.team\n\t\t\t\t? ` on team ${(me.team as { name: string }).name}`\n\t\t\t\t: '';\n\t\tconst userEmail =\n\t\t\tme.user && typeof me.user === 'object' && 'email' in me.user\n\t\t\t\t? (me.user as { email: string }).email\n\t\t\t\t: 'unknown';\n\t\treturn respond({ summary: `Authenticated as ${userEmail}${teamLabel}.`, data });\n\t},\n});\n\ndefineTool({\n\tname: 'describe_mcp',\n\tcategory: 'meta',\n\tdescription: [\n\t\t'Report this MCP build: its package version, total tool count, and the list of Dev Box tools it exposes. Read-only — touches no account data.',\n\t\t'',\n\t\t'When to use: at the start of a session to confirm the live MCP build, OR when a Dev Box tool you expect (create_dev_environment, create_standalone_dev_environment, spin_up_dev_environment, list_dev_environments, resize_dev_environment, delete_dev_environment) seems to be missing. A STALE npx/npm cache can silently present an old tool set with no other signal — call this and check `devEnvToolsPresent`; if it is false, the cached build predates the Dev Box tools and the user should refresh it (e.g. `npx --yes @hoststack.dev/mcp@latest` / clear the npx cache).',\n\t\t'',\n\t\t'Note on terminology: a \"Dev Box\" (a.k.a. Cloud Dev Box) is an agentic cloud box you code inside (create_dev_environment & friends). It is NOT a project \"environment\" (create_environment), which is a deployment target like Production / Staging / Preview. This tool only reports the Dev Box tool set.',\n\t\t'',\n\t\t'Inputs: none.',\n\t\t'',\n\t\t'Returns: { version, toolCount, devEnvTools: string[], devEnvToolsPresent: boolean } — version is the @hoststack.dev/mcp package version, toolCount is every registered tool, devEnvTools is the expected Dev Box tool names, and devEnvToolsPresent is true only if all of them are actually registered in this build.',\n\t\t'',\n\t\t'Example: describe_mcp() → { version: \"0.14.0\", toolCount: 30, devEnvTools: [\"create_dev_environment\", …], devEnvToolsPresent: true }',\n\t].join('\\n'),\n\tinput: {},\n\thandler: async (_args, _ctx) => {\n\t\tconst registered = new Set(listToolDefinitions().map((t) => t.name));\n\t\tconst devEnvToolsPresent = DEV_ENV_TOOL_NAMES.every((name) => registered.has(name));\n\t\tconst data = {\n\t\t\tversion: MCP_VERSION,\n\t\t\ttoolCount: registered.size,\n\t\t\tdevEnvTools: [...DEV_ENV_TOOL_NAMES],\n\t\t\tdevEnvToolsPresent,\n\t\t};\n\t\tconst summary = devEnvToolsPresent\n\t\t\t? `hoststack MCP v${MCP_VERSION} — ${data.toolCount} tools, all ${DEV_ENV_TOOL_NAMES.length} Dev Box tools present.`\n\t\t\t: `hoststack MCP v${MCP_VERSION} — ${data.toolCount} tools, but Dev Box tools are MISSING (stale build — refresh the npx/npm cache).`;\n\t\treturn respond({ summary, data });\n\t},\n});\n","import { z } from 'zod';\n\nimport { defineTool } from '../registry.js';\nimport { respond } from '../lib/respond.js';\nimport { shape, shapeList } from '../lib/shape.js';\n\n/**\n * Source of truth: packages/shared/src/schemas/notification-channel.ts.\n * Copied rather than imported because the MCP package's Zod schemas are\n * what the model sees — pulling these from `@hoststack/shared` at\n * runtime would add a dependency that the published `@hoststack.dev/mcp`\n * shouldn't carry.\n *\n * \"Kept in sync manually\" is what this used to say, and it wasn't: the list\n * sat nine events behind for two weeks while `machine.offline` — the only\n * warning a team gets that their own hardware stopped serving — was rejected\n * at this tool's boundary. It is now checked against shared by\n * test/shared-parity.test.ts, which is a dev-time import and never reaches\n * the bundle. Add an event in shared and this file fails its own test until\n * it follows.\n *\n * This is not documentation. It is the `z.enum` on create/update below, so a\n * value missing here cannot be subscribed to at all — and because\n * `update_notification_channel` REPLACES the list, a channel configured in\n * the dashboard with a newer event could not be read back and written again.\n */\nexport const NOTIFICATION_EVENTS = [\n\t'deploy.started',\n\t'deploy.succeeded',\n\t'deploy.failed',\n\t'deploy.failed_consecutive',\n\t'service.created',\n\t'service.deleted',\n\t'service.suspended',\n\t'service.resumed',\n\t'service.restart_failed',\n\t'service.no_running_container',\n\t'service.health_check_failed',\n\t'service.acme_cert_failed',\n\t'service.resource_alert',\n\t'service.pressure_sustained',\n\t'service.pressure_recovered',\n\t'service.uptime_down',\n\t'service.uptime_recovered',\n\t'watchdog.reported_down',\n\t'watchdog.reported_recovered',\n\t'watchdog.silent',\n\t'error.issue_new',\n\t'error.issue_regressed',\n\t'git.auth_failed',\n\t'cron.execution_failed',\n\t'cron.schedule_missed',\n\t'workflow.failed',\n\t'devenv.agent.needs_input',\n\t'devenv.agent.finished',\n\t'devenv.task.created',\n\t'devenv.task.needs_input',\n\t'devenv.task.finished',\n\t'database.backup_failed',\n\t'database.backup_overdue',\n\t'database.failed',\n\t'database.restore_failed',\n\t'volume.backup_failed',\n\t'volume.backup_overdue',\n\t'dns.registry_status_changed',\n\t'dns.registry_expiring',\n\t'dns.registry_domain_missing',\n\t'dns.registry_record_changed',\n\t'domain.registrant_verification_lapsed',\n\t'service.auto_restarted',\n\t'machine.offline',\n\t'machine.online',\n\t'billing.invoice',\n\t'billing.payment_failed',\n\t'billing.spend_limit',\n] as const;\n\ndefineTool({\n\tname: 'list_notification_channels',\n\tcategory: 'alerts',\n\tdescription: [\n\t\t'List configured notification channels for the team (Slack/Discord webhooks + email recipients). Each channel has a list of events it subscribes to — fire only happens when an event in that list occurs.',\n\t\t'',\n\t\t\"When to use: setting up alerts ('which channels are notifying us on deploy failures?'), auditing why someone got/didn't get paged, or before subscribing a new event.\",\n\t\t'',\n\t\t'Returns: { items: Channel[] } — each channel includes id, type, name, webhookUrl (masked), active, events, createdAt.',\n\t\t'',\n\t\t\"Example: list_notification_channels() → { items: [{ id: 3, type: 'slack', name: 'eng-alerts', events: ['deploy.failed', 'git.auth_failed'], … }] }\",\n\t].join('\\n'),\n\tinput: {},\n\thandler: async (_args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.notifications.listChannels(teamId);\n\t\tconst data = shapeList(response, 'channels', shape);\n\t\tconst summary =\n\t\t\tdata.items.length === 0\n\t\t\t\t? 'No notification channels yet — create one with create_notification_channel.'\n\t\t\t\t: `Found ${data.items.length} notification channel${data.items.length === 1 ? '' : 's'}.`;\n\t\treturn respond({ summary, data });\n\t},\n});\n\ndefineTool({\n\tname: 'create_notification_channel',\n\tcategory: 'alerts',\n\tdescription: [\n\t\t'Create a Slack, Discord, or email notification channel and subscribe it to a list of events.',\n\t\t'',\n\t\t'When to use: setting up alert delivery for a team — paging the on-call when a deploy fails, posting to #incidents when git auth breaks, emailing the owner on ACME renewal failure.',\n\t\t'',\n\t\t'For type=email, `webhook_url` is the recipient email address. For type=slack or type=discord, it is the incoming webhook URL. The URL is stored encrypted server-side and masked in list responses.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - type: \"slack\" | \"discord\" | \"email\".',\n\t\t' - name: human-readable label (1–128 chars).',\n\t\t' - webhook_url: Slack/Discord webhook URL OR email address.',\n\t\t' - events: list of event names to subscribe to. Pass an empty list to create a channel that fires for nothing (manual subscribe later with update_notification_channel).',\n\t\t'',\n\t\t// Rendered from the enum instead of retyped: the hand-written copy of\n\t\t// this sentence went stale the same way the enum did, and a description\n\t\t// that disagrees with the schema teaches the model to send values the\n\t\t// tool then rejects.\n\t\t`Valid events: ${NOTIFICATION_EVENTS.join(', ')}.`,\n\t\t'',\n\t\t'Returns: { channel: Channel }.',\n\t\t'',\n\t\t\"Example: create_notification_channel({ type: 'slack', name: 'eng-alerts', webhook_url: 'https://hooks.slack.com/…', events: ['deploy.failed', 'git.auth_failed', 'service.restart_failed'] })\",\n\t].join('\\n'),\n\tinput: {\n\t\ttype: z.enum(['slack', 'discord', 'email']).describe('Channel type.'),\n\t\tname: z.string().min(1).max(128).describe('Human-readable label.'),\n\t\twebhook_url: z\n\t\t\t.string()\n\t\t\t.max(500)\n\t\t\t.describe('Slack/Discord webhook URL or email address (when type=email).'),\n\t\tevents: z\n\t\t\t.array(z.enum(NOTIFICATION_EVENTS))\n\t\t\t.describe(\n\t\t\t\t'List of events the channel subscribes to. Empty list = subscribe to nothing.',\n\t\t\t),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\t// Cast for the same reason update_notification_channel below casts, plus\n\t\t// one of its own: the SDK's `NotificationChannelEvent` union is a third\n\t\t// hand-copy of this list and is still nine values behind shared, so\n\t\t// events the API accepts do not type-check through it. The values are\n\t\t// Zod-validated against the mirrored enum on the way in, and the cast\n\t\t// stops being load-bearing the moment that union is regenerated.\n\t\tconst response = await ctx.hoststack.notifications.createChannel(teamId, {\n\t\t\ttype: args.type,\n\t\t\tname: args.name,\n\t\t\twebhookUrl: args.webhook_url,\n\t\t\tevents: args.events as Parameters<\n\t\t\t\ttypeof ctx.hoststack.notifications.createChannel\n\t\t\t>[1]['events'],\n\t\t});\n\t\tconst data = { channel: shape(response.channel) };\n\t\treturn respond({\n\t\t\tsummary: `Created ${args.type} channel \"${args.name}\" subscribed to ${args.events.length} event${args.events.length === 1 ? '' : 's'}.`,\n\t\t\tdata,\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'update_notification_channel',\n\tcategory: 'alerts',\n\tdescription: [\n\t\t'Update a notification channel: rename, enable/disable, or change its event subscription list. The webhook URL and type are immutable — create a new channel if those need to change.',\n\t\t'',\n\t\t'When to use: silencing a noisy channel (active=false), adding a new event to an existing list, or relabeling.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - channel_id: numeric id of the channel.',\n\t\t' - name (optional): new label.',\n\t\t' - active (optional): false to silence, true to re-enable.',\n\t\t' - events (optional): replace the full subscription list. Passing [] silences all events without disabling the channel.',\n\t\t'',\n\t\t'Returns: { channel: Channel }.',\n\t\t'',\n\t\t\"Example: update_notification_channel({ channel_id: 3, events: ['deploy.failed', 'service.restart_failed', 'git.auth_failed'] })\",\n\t].join('\\n'),\n\tinput: {\n\t\tchannel_id: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.positive()\n\t\t\t.describe('Numeric channel id from list_notification_channels.'),\n\t\tname: z.string().min(1).max(128).optional().describe('New label.'),\n\t\tactive: z.boolean().optional().describe('false silences without deleting.'),\n\t\tevents: z\n\t\t\t.array(z.enum(NOTIFICATION_EVENTS))\n\t\t\t.optional()\n\t\t\t.describe('Replaces the full subscription list.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst update: { name?: string; active?: boolean; events?: readonly string[] } = {};\n\t\tif (args.name !== undefined) update.name = args.name;\n\t\tif (args.active !== undefined) update.active = args.active;\n\t\tif (args.events !== undefined) update.events = args.events;\n\n\t\tif (Object.keys(update).length === 0) {\n\t\t\treturn respond({ summary: 'No fields to update.', data: {} });\n\t\t}\n\t\t// Cast: SDK's events type is the typed enum array; passing through\n\t\t// from the Zod-validated args is safe at the type level even if\n\t\t// TS can't bridge the readonly tuple to the SDK signature.\n\t\tconst response = await ctx.hoststack.notifications.updateChannel(\n\t\t\tteamId,\n\t\t\targs.channel_id,\n\t\t\tupdate as Parameters<typeof ctx.hoststack.notifications.updateChannel>[2],\n\t\t);\n\t\tconst data = { channel: shape(response.channel) };\n\t\treturn respond({\n\t\t\tsummary: `Updated notification channel #${args.channel_id}.`,\n\t\t\tdata,\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'delete_notification_channel',\n\tcategory: 'alerts',\n\tdescription: [\n\t\t'Permanently delete a notification channel. Use update_notification_channel({ active: false }) to silence without deleting if you might want it back later.',\n\t\t'',\n\t\t'When to use: a channel is no longer needed (decommissioned Slack workspace, team member left, webhook leaked) and you want it gone from the team config entirely.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - channel_id: numeric id of the channel.',\n\t\t'',\n\t\t'Returns: { ok: true }.',\n\t\t'',\n\t\t'Example: delete_notification_channel({ channel_id: 3 }) → { ok: true }',\n\t].join('\\n'),\n\tinput: {\n\t\tchannel_id: z.number().int().positive().describe('Numeric channel id.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tawait ctx.hoststack.notifications.deleteChannel(teamId, args.channel_id);\n\t\treturn respond({\n\t\t\tsummary: `Deleted notification channel #${args.channel_id}.`,\n\t\t\tdata: { ok: true },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'test_notification_channel',\n\tcategory: 'alerts',\n\tdescription: [\n\t\t'Fire a synthetic test event to the channel so the user can confirm the webhook is wired up correctly. The webhook receives an unmistakeable \"test message\" payload.',\n\t\t'',\n\t\t\"When to use: after creating a channel, or when alerts stop arriving — quickest way to tell whether the issue is on HostStack's side or the channel's.\",\n\t\t'',\n\t\t'Inputs:',\n\t\t' - channel_id: numeric id of the channel.',\n\t\t'',\n\t\t'Returns: { success: boolean, error?: string } — false + error string when the webhook returned non-2xx.',\n\t\t'',\n\t\t'Example: test_notification_channel({ channel_id: 3 }) → { success: true }',\n\t].join('\\n'),\n\tinput: {\n\t\tchannel_id: z.number().int().positive().describe('Numeric channel id.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.notifications.testChannel(teamId, args.channel_id);\n\t\tconst data = shape(response);\n\t\tconst okFlag =\n\t\t\tdata && typeof data === 'object' && 'success' in data\n\t\t\t\t? (data as { success: boolean }).success\n\t\t\t\t: false;\n\t\treturn respond({\n\t\t\tsummary: okFlag\n\t\t\t\t? `Test event delivered to channel #${args.channel_id}.`\n\t\t\t\t: `Test event FAILED for channel #${args.channel_id}: ${(data as { error?: string }).error ?? 'unknown error'}.`,\n\t\t\tdata,\n\t\t});\n\t},\n});\n","import { z } from 'zod';\n\nimport { defineTool } from '../registry.js';\nimport { respond } from '../lib/respond.js';\nimport { shapeList, shapeProject } from '../lib/shape.js';\n\n// HostStack region IDs (matches @hoststack/shared REGION_IDS and the SDK's\n// internal RegionId). The SDK narrows region to this union but doesn't export\n// the type name, so we mirror the literals here — structurally identical, so\n// it stays assignable to the SDK's create-project param.\n//\n// AVAILABLE_REGION_IDS mirrors @hoststack/shared's list of regions we actually\n// run workers in — only these may be selected when creating a project, since a\n// project pinned to a region with no fleet can never deploy. Keep in lockstep\n// with the shared constant when a new region goes live.\nconst AVAILABLE_REGION_IDS = ['eu-central-1'] as const;\ntype RegionId = (typeof AVAILABLE_REGION_IDS)[number];\n\ndefineTool({\n\tname: 'list_projects',\n\tcategory: 'projects',\n\tdescription: [\n\t\t'List every project in the active HostStack team.',\n\t\t'',\n\t\t'When to use: the agent needs an overview of what projects exist before drilling into services, deploys, or databases. Use this as the first step when the user mentions a project by name and you need to resolve its ID.',\n\t\t'',\n\t\t'Returns: { items: Project[] } — each project includes id, publicId, name, description, createdAt.',\n\t\t'',\n\t\t'Example: list_projects() → { items: [{ id: 12, publicId: \"prj_…\", name: \"billing-api\", … }] }',\n\t].join('\\n'),\n\tinput: {},\n\thandler: async (_args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.projects.list(teamId);\n\t\tconst data = shapeList(response, 'projects', shapeProject);\n\t\tconst summary =\n\t\t\tdata.items.length === 0\n\t\t\t\t? 'No projects yet — create one in the dashboard or via create_project.'\n\t\t\t\t: `Found ${data.items.length} project${data.items.length === 1 ? '' : 's'}.`;\n\t\treturn respond({ summary, data });\n\t},\n});\n\ndefineTool({\n\tname: 'create_project',\n\tcategory: 'projects',\n\tdescription: [\n\t\t'Create a new project (logical grouping of services + databases).',\n\t\t'',\n\t\t\"When to use: the user wants to set up a new app or environment in HostStack. Projects are a free organisational layer — they don't cost anything until you add services or databases inside them.\",\n\t\t'',\n\t\t'Inputs:',\n\t\t' - name: human-readable project name (1–60 chars).',\n\t\t' - description (optional): short blurb shown in the dashboard.',\n\t\t' - region (optional): \"eu-central-1\" (Falkenstein). Currently the only region with available capacity; defaults to eu-central-1.',\n\t\t'',\n\t\t'Returns: { project: Project } — includes the new id and publicId.',\n\t\t'',\n\t\t'Example: create_project({ name: \"billing-api\", description: \"Stripe webhooks\", region: \"eu-central-1\" }) → { project: { id: 12, publicId: \"prj_…\", … } }',\n\t].join('\\n'),\n\tinput: {\n\t\tname: z.string().min(1).max(60).describe('Project name (1–60 chars).'),\n\t\tdescription: z.string().max(500).optional().describe('Short description (≤500 chars).'),\n\t\tregion: z\n\t\t\t.enum(AVAILABLE_REGION_IDS)\n\t\t\t.optional()\n\t\t\t.describe('Region: eu-central-1 (Falkenstein) — currently the only available region.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst input: { name: string; description?: string; region?: RegionId } = {\n\t\t\tname: args.name,\n\t\t};\n\t\tif (args.description !== undefined) input.description = args.description;\n\t\tif (args.region !== undefined) input.region = args.region;\n\t\tconst response = await ctx.hoststack.projects.create(teamId, input);\n\t\tconst data = { project: shapeProject(response.project) };\n\t\tconst publicId =\n\t\t\tdata.project && 'publicId' in data.project\n\t\t\t\t? (data.project as { publicId: string }).publicId\n\t\t\t\t: 'unknown';\n\t\treturn respond({ summary: `Created project \"${args.name}\" (${publicId}).`, data });\n\t},\n});\n\ndefineTool({\n\tname: 'update_project',\n\tcategory: 'projects',\n\tdescription: [\n\t\t'Rename a project or update its description.',\n\t\t'',\n\t\t'When to use: the user wants to fix a typo in a project name, attach a clearer description, or align the dashboard label with their internal naming.',\n\t\t'',\n\t\t'Inputs (all optional, at least one required):',\n\t\t' - project_id: publicId of the project (required).',\n\t\t' - name: new name (1–60 chars).',\n\t\t' - description: new description (≤500 chars).',\n\t\t'',\n\t\t'Returns: { project: Project } — the updated record.',\n\t\t'',\n\t\t'Example: update_project({ project_id: \"prj_abc\", name: \"billing-prod\" }) → { project: { name: \"billing-prod\", … } }',\n\t].join('\\n'),\n\tinput: {\n\t\tproject_id: z.string().describe('Project publicId.'),\n\t\tname: z.string().min(1).max(60).optional().describe('New name (1–60 chars).'),\n\t\tdescription: z.string().max(500).optional().describe('New description (≤500 chars).'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tif (args.name === undefined && args.description === undefined) {\n\t\t\treturn respond({\n\t\t\t\tsummary: 'No fields to update — pass `name` and/or `description`.',\n\t\t\t\tdata: {},\n\t\t\t});\n\t\t}\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst input: { name?: string; description?: string } = {};\n\t\tif (args.name !== undefined) input.name = args.name;\n\t\tif (args.description !== undefined) input.description = args.description;\n\t\tconst response = await ctx.hoststack.projects.update(teamId, args.project_id, input);\n\t\tconst data = { project: shapeProject(response.project) };\n\t\treturn respond({ summary: `Updated project ${args.project_id}.`, data });\n\t},\n});\n\ndefineTool({\n\tname: 'get_project',\n\tcategory: 'projects',\n\tdescription: [\n\t\t'Fetch a single project by ID. Includes name, description, and timestamps.',\n\t\t'',\n\t\t'When to use: confirming a project exists before creating resources inside it, or pulling the canonical name/description for a reply to the user.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - project_id: publicId of the project (e.g. \"prj_abc123\").',\n\t\t'',\n\t\t'Returns: { project: Project }.',\n\t\t'',\n\t\t'Example: get_project({ project_id: \"prj_abc\" }) → { project: { id: 12, name: \"billing\", … } }',\n\t].join('\\n'),\n\tinput: {\n\t\tproject_id: z.string().describe('Project publicId (e.g. prj_abc123).'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.projects.get(teamId, args.project_id);\n\t\tconst data = { project: shapeProject(response.project) };\n\t\tconst name =\n\t\t\tdata.project && 'name' in data.project\n\t\t\t\t? (data.project as { name: string }).name\n\t\t\t\t: args.project_id;\n\t\treturn respond({ summary: `Project \"${name}\".`, data });\n\t},\n});\n","import { z } from 'zod';\n\nimport { respond } from '../lib/respond.js';\nimport { shape, shapeList } from '../lib/shape.js';\nimport { defineTool } from '../registry.js';\n\n/**\n * The agent task backlog behind a dev box.\n *\n * This category exists because the backlog was reachable from the dashboard and\n * from `errors fix` and nowhere else: an agent that wanted to hand work to\n * ANOTHER box - a fix that belongs upstream, a follow-up in a different repo -\n * had no tool for it and had to hand-roll an HTTP call.\n *\n * The one thing to keep straight: filing a task does not start an agent. It\n * writes a prompt into a queue that a person (or automode, where that is on)\n * starts later. That is what makes it safe to file work for a box that is\n * asleep, and it is why every description below says so.\n */\nconst NOT_A_RUN =\n\t\"Filing a task does NOT start an agent. It writes a prompt into the box's backlog for someone to run; a suspended box keeps it until it wakes.\";\n\ndefineTool({\n\tname: 'list_dev_tasks',\n\tcategory: 'dev-tasks',\n\tdescription: [\n\t\t\"List a project's agent task backlog - the same list the dashboard's Development → Tasks surface shows.\",\n\t\t'',\n\t\t'When to use: to see what is already queued for a box before filing something (the backlog is how you avoid two agents being pointed at one job), or to find the task id for update_dev_task.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - project_id: numeric project id (from list_projects) or its \"prj_…\" publicId.',\n\t\t'',\n\t\t'Returns: { summary, data: { items: DevTask[] } } - each task with publicId, title, status, serviceId (the box, or null for a loose idea), createdAt.',\n\t\t'',\n\t\t'`status` spans two worlds: `idea`/`done` are what a person sets, while `queued`/`running`/`needs_input`/`failed`/`cancelled` describe an agent run and belong to the runner.',\n\t\t'',\n\t\t'Example: list_dev_tasks({ project_id: 26 }) → { items: [{ publicId: \"task_…\", status: \"idea\", title: \"Fix the footprint join\" }] }',\n\t].join('\\n'),\n\tinput: {\n\t\tproject_id: z\n\t\t\t.union([z.number().int().positive(), z.string()])\n\t\t\t.describe('Project — publicId (\"prj_…\") or numeric id.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.devTasks.list(teamId, args.project_id);\n\t\tconst data = shapeList(response, 'tasks', shape);\n\t\tconst open = data.items.filter(\n\t\t\t(t) =>\n\t\t\t\tt &&\n\t\t\t\ttypeof t === 'object' &&\n\t\t\t\t'status' in t &&\n\t\t\t\t!['done', 'cancelled'].includes(String((t as { status: unknown }).status)),\n\t\t).length;\n\t\tconst summary =\n\t\t\tdata.items.length === 0\n\t\t\t\t? 'No tasks in this project.'\n\t\t\t\t: `${data.items.length} task${data.items.length === 1 ? '' : 's'}, ${open} still open.${\n\t\t\t\t\t\tresponse.automodeEnabled\n\t\t\t\t\t\t\t? ''\n\t\t\t\t\t\t\t: ' Automode is off here, so a queued task waits for someone to start it.'\n\t\t\t\t\t}`;\n\t\treturn respond({ summary, data });\n\t},\n});\n\ndefineTool({\n\tname: 'get_dev_task',\n\tcategory: 'dev-tasks',\n\tdescription: [\n\t\t'Get one task, including the full prompt body.',\n\t\t'',\n\t\t'When to use: to read what a task actually asks for before acting on it or marking it done - `list_dev_tasks` returns titles, not prompts.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - task_id: \"task_…\" publicId or numeric id.',\n\t\t'',\n\t\t'Returns: { summary, data: DevTask }.',\n\t\t'',\n\t\t'Example: get_dev_task({ task_id: \"task_hy1i2jdp…\" }) → { data: { title: \"Footprint coverage\", body: \"The BBRUUID join returns 17 of 49 …\", status: \"idea\" } }',\n\t].join('\\n'),\n\tinput: {\n\t\ttask_id: z\n\t\t\t.union([z.number().int().positive(), z.string()])\n\t\t\t.describe('Task — publicId (\"task_…\") or numeric id.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.devTasks.get(teamId, args.task_id);\n\t\tconst data = shape(response.task);\n\t\treturn respond({ summary: `Task ${response.task.publicId}: ${response.task.title}`, data });\n\t},\n});\n\ndefineTool({\n\tname: 'create_dev_task',\n\tcategory: 'dev-tasks',\n\tdescription: [\n\t\t\"File a task in a project's backlog, optionally pinned to a specific dev box.\",\n\t\t'',\n\t\t`When to use: to hand work to a dev box other than the one you are in - a fix that belongs in an upstream service, a follow-up in a different repo, anything the box you are in cannot do itself. ${NOT_A_RUN}`,\n\t\t'',\n\t\t'Inputs:',\n\t\t' - project_id: numeric project id or \"prj_…\" publicId.',\n\t\t' - title: one line, ≤200 chars. This is what the backlog shows.',\n\t\t' - body: the prompt, markdown, ≤20 000 chars. Write it for someone who was not in this conversation: what is broken, where, how it was measured, and what to watch out for.',\n\t\t' - service_id: the dev box to pin it to (\"svc_…\" or numeric). Omit for a loose idea in the project backlog.',\n\t\t'',\n\t\t'Returns: { summary, data: DevTask } - `publicId` is the id to quote back to the user.',\n\t\t'',\n\t\t'Example: create_dev_task({ project_id: 26, service_id: 51, title: \"Footprint coverage\", body: \"The BBRUUID join returns 17 of 49 …\" })',\n\t].join('\\n'),\n\tinput: {\n\t\tproject_id: z\n\t\t\t.union([z.number().int().positive(), z.string()])\n\t\t\t.describe('Project — publicId (\"prj_…\") or numeric id.'),\n\t\ttitle: z.string().min(1).max(200).describe('One-line title, ≤200 chars.'),\n\t\tbody: z\n\t\t\t.string()\n\t\t\t.max(20_000)\n\t\t\t.optional()\n\t\t\t.describe('The prompt handed to the agent. Markdown, ≤20 000 chars.'),\n\t\tservice_id: z\n\t\t\t.union([z.number().int().positive(), z.string()])\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t'Dev box to pin it to — publicId (\"svc_…\") or numeric id. Omit for a loose idea.',\n\t\t\t),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst serviceId =\n\t\t\targs.service_id === undefined\n\t\t\t\t? undefined\n\t\t\t\t: await ctx.hoststack.resolveId(args.service_id, { kind: 'service', teamId });\n\t\tconst projectId = await ctx.hoststack.resolveId(args.project_id, {\n\t\t\tkind: 'project',\n\t\t\tteamId,\n\t\t});\n\t\tconst response = await ctx.hoststack.devTasks.create(teamId, {\n\t\t\tprojectId,\n\t\t\ttitle: args.title,\n\t\t\t...(args.body ? { body: args.body } : {}),\n\t\t\t...(serviceId ? { serviceId } : {}),\n\t\t});\n\t\tconst data = shape(response.task);\n\t\treturn respond({\n\t\t\tsummary: `Filed ${response.task.publicId}: ${response.task.title}. ${NOT_A_RUN}`,\n\t\t\tdata,\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'update_dev_task',\n\tcategory: 'dev-tasks',\n\tdescription: [\n\t\t'Edit a task, or move it between the two statuses a PERSON may set.',\n\t\t'',\n\t\t'When to use: to mark a task `done` once the work has landed (by hand, or in another branch - most work does not finish inside the task runner), to reopen it as an `idea`, to correct a title or prompt, or to pin a loose idea to a box.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - task_id: \"task_…\" publicId or numeric id.',\n\t\t' - status: \"done\" or \"idea\". Only these two; `queued`/`running`/`failed`/`cancelled` belong to the runner and are rejected.',\n\t\t' - title / body / service_id: optional edits.',\n\t\t' - provider: which agent runs it — \"claude\", \"codex\" or \"opencode\". null clears the pin and lets the box decide.',\n\t\t' - permission_mode: how that agent is allowed to act — \"default\" (ask before acting), \"acceptEdits\" (edit files without asking) or \"bypassPermissions\" (no prompts at all). Only set bypassPermissions when the person asked for it: it is the box-wide yolo setting scoped to one task.',\n\t\t'',\n\t\t'Only mark a task done when it is actually resolved - the backlog is what someone reads to decide what still needs doing.',\n\t\t'',\n\t\t'Returns: { summary, data: DevTask }.',\n\t\t'',\n\t\t'Example: update_dev_task({ task_id: \"task_hy1i2jdp…\", status: \"done\" }) → { data: { status: \"done\" } }',\n\t].join('\\n'),\n\tinput: {\n\t\ttask_id: z\n\t\t\t.union([z.number().int().positive(), z.string()])\n\t\t\t.describe('Task — publicId (\"task_…\") or numeric id.'),\n\t\tstatus: z\n\t\t\t.enum(['idea', 'done'])\n\t\t\t.optional()\n\t\t\t.describe('The only two a person may set. The runner owns the rest of the lifecycle.'),\n\t\ttitle: z.string().min(1).max(200).optional().describe('New title.'),\n\t\tbody: z.string().max(20_000).optional().describe('New prompt body.'),\n\t\tservice_id: z\n\t\t\t.union([z.number().int().positive(), z.string()])\n\t\t\t.nullable()\n\t\t\t.optional()\n\t\t\t.describe('Pin to a dev box, or null to unpin it back to a loose idea.'),\n\t\tprovider: z\n\t\t\t.enum(['claude', 'codex', 'opencode'])\n\t\t\t.nullable()\n\t\t\t.optional()\n\t\t\t.describe('Which agent runs this task, or null to leave it to the box.'),\n\t\tpermission_mode: z\n\t\t\t.enum(['acceptEdits', 'bypassPermissions', 'default'])\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t'How the agent may act. bypassPermissions asks for nothing — set it only on request.',\n\t\t\t),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst serviceId =\n\t\t\targs.service_id === undefined || args.service_id === null\n\t\t\t\t? args.service_id\n\t\t\t\t: await ctx.hoststack.resolveId(args.service_id, { kind: 'service', teamId });\n\t\tconst response = await ctx.hoststack.devTasks.update(teamId, args.task_id, {\n\t\t\t...(args.status ? { status: args.status } : {}),\n\t\t\t...(args.title ? { title: args.title } : {}),\n\t\t\t...(args.body !== undefined ? { body: args.body } : {}),\n\t\t\t...(serviceId !== undefined ? { serviceId } : {}),\n\t\t\t// null is meaningful on provider (unpin), so compare to undefined.\n\t\t\t...(args.provider !== undefined ? { provider: args.provider } : {}),\n\t\t\t...(args.permission_mode !== undefined ? { permissionMode: args.permission_mode } : {}),\n\t\t});\n\t\tconst data = shape(response.task);\n\t\treturn respond({\n\t\t\tsummary: `${response.task.publicId} is now ${response.task.status}: ${response.task.title}`,\n\t\t\tdata,\n\t\t});\n\t},\n});\n","import { z } from 'zod';\n\nimport { defineTool } from '../registry.js';\nimport { respond } from '../lib/respond.js';\nimport { shape, shapeList } from '../lib/shape.js';\n\nconst RESOURCE_LINK_TYPES = [\n\t'database',\n\t'object_storage',\n\t'queue',\n\t'search',\n\t'email_domain',\n] as const;\n\n/**\n * The env vars a link injects, by resource class. Documented in the tool\n * descriptions so an agent can predict what the container will see WITHOUT\n * fetching credentials — the whole point of the link system.\n */\nconst INJECTION_CONTRACT = [\n\t'What a link injects at deploy time:',\n\t' - Alias-prefixed vars for every field of the resource (alias \"APP_DB\" → APP_DB_HOST, APP_DB_PORT, APP_DB_USER, APP_DB_PASSWORD, APP_DB_URL, …). This is how one service consumes several resources of the same type without collisions.',\n\t' - PLUS the conventional name for the first linked resource of each engine class: `DATABASE_URL` (postgres/mysql/mariadb), `REDIS_URL` (redis), `MONGO_URL` (mongodb). So an app reading `process.env.DATABASE_URL` works with no code changes.',\n\t' - The conventional names are set with `??=` — an env var you set yourself via set_env_var ALWAYS wins over the injected one.',\n].join('\\n');\n\ndefineTool({\n\tname: 'list_managed_resources',\n\tcategory: 'resource-links',\n\tdescription: [\n\t\t'List EVERY managed resource the team owns in one call — databases, object storage buckets, queues, search indexes and email domains — from the unified `managed_resources` read-model.',\n\t\t'',\n\t\t'When to use: to find the NUMERIC `id` that link_resource_to_service needs, especially for resource types with no dedicated list tool of their own (object storage, queues, search, email domains — list_databases only covers databases). Also a fast inventory answer for \"what do we actually have running\".',\n\t\t'',\n\t\t'Team-wide, not project-scoped. Use list_databases when you specifically want databases in one project.',\n\t\t'',\n\t\t'Returns: { items: ManagedResource[] } — id (numeric, for linking), publicId, type, name, status, region, createdAt.',\n\t\t'',\n\t\t'Example: list_managed_resources() → { items: [{ id: 42, type: \"database\", name: \"app-db\", status: \"available\" }, { id: 8, type: \"object_storage\", name: \"uploads\", … }] }',\n\t].join('\\n'),\n\tinput: {},\n\thandler: async (_args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.serviceResourceLinks.listManagedResources(teamId);\n\t\tconst data = shapeList(response, 'resources', shape);\n\t\tconst byType = new Map<string, number>();\n\t\tfor (const item of data.items) {\n\t\t\tconst type =\n\t\t\t\titem && typeof item === 'object' && 'type' in item\n\t\t\t\t\t? String((item as { type: unknown }).type)\n\t\t\t\t\t: 'unknown';\n\t\t\tbyType.set(type, (byType.get(type) ?? 0) + 1);\n\t\t}\n\t\tconst breakdown = [...byType.entries()].map(([t, n]) => `${n} ${t}`).join(', ');\n\t\tconst summary =\n\t\t\tdata.items.length === 0\n\t\t\t\t? 'No managed resources on this team yet.'\n\t\t\t\t: `${data.items.length} managed resource${data.items.length === 1 ? '' : 's'}: ${breakdown}.`;\n\t\treturn respond({ summary, data });\n\t},\n});\n\ndefineTool({\n\tname: 'list_service_resources',\n\tcategory: 'resource-links',\n\tdescription: [\n\t\t'List the managed resources (databases, object storage, queues, search, email domains) linked to a service — i.e. which resources get injected into its container as env vars on deploy.',\n\t\t'',\n\t\t'When to use: before adding a link (to avoid a duplicate or an alias collision), when debugging \"why is DATABASE_URL empty in my app\" (the answer is usually: no link exists, or the service was never redeployed after linking), or to find the linkId needed by unlink_resource_from_service.',\n\t\t'',\n\t\tINJECTION_CONTRACT,\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId (\"svc_…\") or numeric id of the service.',\n\t\t'',\n\t\t'Returns: { items: ServiceResourceLink[] } — id (the linkId), resourceType, resourceId (numeric id of the linked resource), alias, createdAt.',\n\t\t'',\n\t\t'Example: list_service_resources({ service_id: \"svc_abc\" }) → { items: [{ id: 7, resourceType: \"database\", resourceId: 42, alias: \"APP_DB\" }] }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z\n\t\t\t.union([z.number().int().positive(), z.string()])\n\t\t\t.describe('Service — publicId (\"svc_…\") or numeric id.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.serviceResourceLinks.list(teamId, args.service_id);\n\t\tconst data = shapeList(response, 'links', shape);\n\t\tconst summary =\n\t\t\tdata.items.length === 0\n\t\t\t\t? `No resources linked to service ${args.service_id}. Nothing is being injected — link a database with link_resource_to_service.`\n\t\t\t\t: `${data.items.length} resource${data.items.length === 1 ? '' : 's'} linked to service ${args.service_id}.`;\n\t\treturn respond({ summary, data });\n\t},\n});\n\ndefineTool({\n\tname: 'link_resource_to_service',\n\tcategory: 'resource-links',\n\tdescription: [\n\t\t'Link a managed resource to a service so its connection details are injected into the container as environment variables. This is the step that connects a database to an app — creating the database alone does nothing for the app.',\n\t\t'',\n\t\t'When to use: immediately after create_database (or when pointing an existing service at an existing resource). This is step 2 of 3 in the managed-database flow: create_database → link_resource_to_service → trigger_deploy.',\n\t\t'',\n\t\t'*** The link takes effect on the NEXT DEPLOY. *** After calling this, call trigger_deploy({ service_id }) or the running container will not see the new vars. A service that \"cannot connect to the database\" right after linking almost always just needs that redeploy.',\n\t\t'',\n\t\tINJECTION_CONTRACT,\n\t\t'',\n\t\t\"Because the platform injects the credentials directly into the container, you do NOT need to read the password, build a connection string by hand, or store it with set_env_var. Don't.\",\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId (\"svc_…\") or numeric id of the consuming service.',\n\t\t` - resource_type: ${RESOURCE_LINK_TYPES.join(' | ')}.`,\n\t\t' - resource_id: the NUMERIC id of the resource — e.g. the `id` field from create_database / list_databases, NOT the \"db_…\" publicId.',\n\t\t' - alias: uppercase env-var prefix (A–Z, 0–9, underscore; must start with a letter; ≤48 chars), e.g. \"APP_DB\", \"CACHE\", \"CATALOG\". Must be unique within the service.',\n\t\t'',\n\t\t'Returns: { link: ServiceResourceLink } — including `id`, the linkId used to unlink later.',\n\t\t'',\n\t\t'Fails with 409 if this exact resource is already linked to the service, or if the alias is already taken on it.',\n\t\t'',\n\t\t'Example: link_resource_to_service({ service_id: \"svc_abc\", resource_type: \"database\", resource_id: 42, alias: \"APP_DB\" }) → { link: { id: 7, alias: \"APP_DB\" } }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z\n\t\t\t.union([z.number().int().positive(), z.string()])\n\t\t\t.describe('Consuming service — publicId (\"svc_…\") or numeric id.'),\n\t\tresource_type: z.enum(RESOURCE_LINK_TYPES).describe('Kind of resource being linked.'),\n\t\tresource_id: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.positive()\n\t\t\t.describe('NUMERIC id of the resource (e.g. database.id) — not the publicId.'),\n\t\talias: z\n\t\t\t.string()\n\t\t\t.min(1)\n\t\t\t.max(48)\n\t\t\t.regex(\n\t\t\t\t/^[A-Z][A-Z0-9_]*$/,\n\t\t\t\t'Alias must be uppercase letters, digits and underscores, starting with a letter.',\n\t\t\t)\n\t\t\t.describe('Uppercase env-var prefix, e.g. \"APP_DB\". Unique within the service.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.serviceResourceLinks.create(teamId, args.service_id, {\n\t\t\tresourceType: args.resource_type,\n\t\t\tresourceId: args.resource_id,\n\t\t\talias: args.alias,\n\t\t});\n\t\tconst data = { link: shape(response.link) };\n\t\treturn respond({\n\t\t\tsummary: `Linked ${args.resource_type} ${args.resource_id} to service ${args.service_id} as \"${args.alias}\". Call trigger_deploy({ service_id: \"${args.service_id}\" }) to inject it — the running container will not see the vars until then.`,\n\t\t\tdata,\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'unlink_resource_from_service',\n\tcategory: 'resource-links',\n\tdescription: [\n\t\t'Remove a resource link from a service. The resource itself is NOT deleted — only the binding.',\n\t\t'',\n\t\t'When to use: repointing a service at a different database, or detaching a resource a service no longer needs. The injected env vars disappear on the next deploy, so a service that still reads DATABASE_URL will break then, not immediately. Use delete_database if the intent is to destroy the data.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId (\"svc_…\") or numeric id of the service.',\n\t\t' - link_id: numeric linkId from list_service_resources (the `id` field on the link, not the resource id).',\n\t\t'',\n\t\t'Returns: { ok: true }.',\n\t\t'',\n\t\t'Example: unlink_resource_from_service({ service_id: \"svc_abc\", link_id: 7 }) → { ok: true }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z\n\t\t\t.union([z.number().int().positive(), z.string()])\n\t\t\t.describe('Service — publicId (\"svc_…\") or numeric id.'),\n\t\tlink_id: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.positive()\n\t\t\t.describe(\"Numeric linkId from list_service_resources (the link's own `id`).\"),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tawait ctx.hoststack.serviceResourceLinks.delete(teamId, args.service_id, args.link_id);\n\t\treturn respond({\n\t\t\tsummary: `Unlinked resource link ${args.link_id} from service ${args.service_id}. The resource still exists; its env vars vanish on the next deploy.`,\n\t\t});\n\t},\n});\n","import type { CreateDevEnvironmentInput } from '@hoststack.dev/sdk';\nimport { z } from 'zod';\n\nimport { defineTool } from '../registry.js';\nimport { MCP_APP_TEMPLATES } from '../lib/app-templates.js';\nimport { respond, respondError } from '../lib/respond.js';\nimport { shape, shapeList, shapeService } from '../lib/shape.js';\nimport { machineInput, resolveMachineId } from './machines.js';\n\n// Keep in sync with the real sources of truth:\n// packages/shared/src/constants/dev-env.ts DEV_ENV_IMAGE\n// apps/api/src/services/dev-environment.service.ts WORKSPACE_VOLUME\n// packages/shared/src/constants/pricing.ts DEV_ENV_MIN_SIZE,\n// HETZNER_BLOCK_VOLUME_*\n// NOT packages/shared/src/templates.ts for the DEV_ENV_* constants below: the\n// `dev-environment` preset was removed from that file on purpose and it now\n// carries the opposite instruction (the canonical creator is /development/new),\n// with a test asserting the preset never returns. Following the old pointer led\n// you to a file that says the thing does not exist. templates.ts IS the source\n// for the app quickstart catalog — mirrored in lib/app-templates.ts, which\n// `list_templates` returns.\n// The MCP package only depends on the SDK (it publishes to npm and must NOT\n// import the private @hoststack/shared package, which would drag the whole\n// monorepo into the bundle), so these constants are duplicated here\n// intentionally rather than imported. Drift is caught by review, not the type\n// system — update both sides together.\nconst DEV_ENV_IMAGE = 'registry.hoststack.dev/hoststack/dev-env:latest';\nconst DEV_ENV_VOLUME = { name: 'workspace', mountPath: '/workspace', sizeGb: 10 };\n\n// What a box can already do, stated on EVERY tool that makes one.\n//\n// This exists because of a real failure: an agent told to \"add a database\" in a\n// dev box tried to `docker run postgres` (there is no daemon) or to install its\n// own, because nothing it could read said the box already had one. Capability\n// that is undiscoverable is capability that does not exist — so the answer is\n// not to add features, it is to say what is there at the moment the box is\n// created. Keep in sync with apps/dev-env-image/agent-guide.md, which is the\n// same information delivered to the agent once it is inside.\nconst DEV_BOX_INSIDE = [\n\t'INSIDE the box (no Docker daemon — do not try `docker run`, and runtime `apt-get` is blocked too; it is all preinstalled):',\n\t' - Databases/engines via `dev-services up`: Postgres 17 (:5432, user \"dev\", trust auth), Redis (:6379), Meilisearch (:7700). MariaDB/MySQL (:3306, user \"dev\", no password, client `mariadb`) and MongoDB (:27017, client `mongosh`) are OPT-IN — `dev-services up mysql` / `dev-services up mongodb` — so a box does not pay RAM for engines it never uses. `dev-services createdb <name>` makes a Postgres role + database in one step. Data lives on /workspace, so it survives restarts.',\n\t' - Language runtimes: node, bun, python and php 8.3 are built in. go, java, ruby, rust, elixir and dotnet install on demand with `dev-runtime add <name>` (persisted to /workspace). Together that covers every runtime the platform itself deploys — a WordPress/Laravel/Rails stack runs in the box. elixir needs `dev-runtime add erlang` first: it is a BEAM distribution with no OTP of its own, and Ubuntu ships one too old to use, so mise builds a current one (takes a few minutes).',\n\t' - `hoststack-status` prints what is running, the box dev URL and agent login state at any time.',\n].join('\\n');\n\nconst SERVICE_TYPES = [\n\t'web_service',\n\t'private_service',\n\t'worker',\n\t'cron_job',\n\t'static_site',\n] as const;\n// The real service catalog, ascending — mirrors the server's\n// SERVICE_SIZE_ORDER. `pico↓xlarge` are shared-CPU; `pro_*` are dedicated-CPU.\n// Note: `starter` is a DATABASE/addon size, not a service size — do not add it.\nconst SERVICE_PLANS = [\n\t'pico',\n\t'nano',\n\t'micro',\n\t'small',\n\t'standard',\n\t'large',\n\t'xlarge',\n\t'pro_standard',\n\t'pro_large',\n] as const;\n\n// The OOM-safe floor for dev boxes — mirrors DEV_ENV_MIN_SIZE in\n// packages/shared/src/constants/pricing.ts. A coding agent + build OOMs on\n// anything below `standard` (2 GB). The server floors a sub-standard plan\n// too; we clamp here so the MCP tool never even requests an undersized box.\nconst DEV_ENV_MIN_SIZE = 'standard';\n\n/**\n * Clamp a requested service size up to the dev-env floor. Mirrors the\n * server-side `coerceDevEnvSize`: an unknown/omitted/sub-`standard` value\n * becomes `standard`; a known size at/above the floor is left untouched.\n * Duplicated (not imported) per the workspace constraint above.\n */\nfunction coerceDevEnvSize(value: string | undefined): (typeof SERVICE_PLANS)[number] {\n\tconst minIdx = SERVICE_PLANS.indexOf(DEV_ENV_MIN_SIZE);\n\tconst idx = value ? SERVICE_PLANS.indexOf(value as (typeof SERVICE_PLANS)[number]) : -1;\n\treturn idx >= minIdx ? (value as (typeof SERVICE_PLANS)[number]) : DEV_ENV_MIN_SIZE;\n}\n\ndefineTool({\n\tname: 'list_services',\n\tcategory: 'services',\n\tdescription: [\n\t\t'List services (web, worker, cron, private, static) in the active HostStack team.',\n\t\t'',\n\t\t'When to use: the agent needs to find a service by name, check what is deployed, or pick a target for a follow-up tool (logs, deploys, env vars). This is the canonical way to resolve a publicId from a human-friendly name.',\n\t\t'',\n\t\t'Note: agentic Dev Boxes (cloud dev environments) are EXCLUDED by default. Pass dev_environment:true to include them, or use list_dev_environments for the dedicated, annotated dev-box listing.',\n\t\t'',\n\t\t'Inputs (all optional):',\n\t\t' - project_id: narrow to one project (numeric id or publicId \"prj_…\").',\n\t\t' - environment_id: narrow to one environment (numeric id or publicId \"env_…\").',\n\t\t' - status: \"active\" | \"deploying\" | \"suspended\" | \"failed\" | \"not_deployed\".',\n\t\t' - type: \"web_service\" | \"private_service\" | \"worker\" | \"cron_job\" | \"static_site\".',\n\t\t' - dev_environment: include Dev Boxes in the results (excluded by default).',\n\t\t'',\n\t\t'Returns: { items: Service[] } — each service includes id, publicId, name, type, status, projectId, branch, runtime, createdAt, and, for a source-built service, repoProvider/repoFullName/repoUrl (what it actually builds — the stored id alone names no repository).',\n\t\t'',\n\t\t'Example: list_services({ status: \"failed\" }) → only services that need attention.',\n\t].join('\\n'),\n\tinput: {\n\t\tproject_id: z\n\t\t\t.union([z.number().int().positive(), z.string()])\n\t\t\t.optional()\n\t\t\t.describe('Project filter — numeric id or publicId.'),\n\t\tenvironment_id: z\n\t\t\t.union([z.number().int().positive(), z.string()])\n\t\t\t.optional()\n\t\t\t.describe('Environment filter — numeric id or publicId.'),\n\t\tstatus: z\n\t\t\t.enum(['active', 'deploying', 'suspended', 'failed', 'not_deployed'])\n\t\t\t.optional()\n\t\t\t.describe('Filter by current runtime status.'),\n\t\ttype: z\n\t\t\t.enum(['web_service', 'private_service', 'worker', 'cron_job', 'static_site'])\n\t\t\t.optional()\n\t\t\t.describe('Filter by service type.'),\n\t\tdev_environment: z\n\t\t\t.boolean()\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t'Include agentic Dev Boxes in the results (excluded by default; see list_dev_environments).',\n\t\t\t),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst filters: Parameters<typeof ctx.hoststack.services.list>[1] = {};\n\t\tif (args.dev_environment) filters.devEnvironment = true;\n\t\tif (args.project_id !== undefined) {\n\t\t\t// SDK accepts publicId or numeric id; let resolveId handle it.\n\t\t\tconst resolved = await ctx.hoststack.resolveId(args.project_id, {\n\t\t\t\tkind: 'project',\n\t\t\t\tteamId,\n\t\t\t});\n\t\t\tfilters.projectId = resolved;\n\t\t}\n\t\tif (args.environment_id !== undefined) {\n\t\t\tconst resolved = await ctx.hoststack.resolveId(args.environment_id, {\n\t\t\t\tkind: 'environment',\n\t\t\t\tteamId,\n\t\t\t});\n\t\t\tfilters.environmentId = resolved;\n\t\t}\n\t\tif (args.status) filters.status = args.status;\n\t\tif (args.type) filters.type = args.type;\n\n\t\tconst response = await ctx.hoststack.services.list(teamId, filters);\n\t\tconst data = shapeList(response, 'services', shapeService);\n\t\tconst filterDesc = [\n\t\t\targs.project_id !== undefined ? `project=${args.project_id}` : null,\n\t\t\targs.environment_id !== undefined ? `env=${args.environment_id}` : null,\n\t\t\targs.status ? `status=${args.status}` : null,\n\t\t\targs.type ? `type=${args.type}` : null,\n\t\t\targs.dev_environment ? 'devBoxes=included' : null,\n\t\t]\n\t\t\t.filter(Boolean)\n\t\t\t.join(', ');\n\t\tconst summary =\n\t\t\tdata.items.length === 0\n\t\t\t\t? filterDesc\n\t\t\t\t\t? `No services match the given filters (${filterDesc}).`\n\t\t\t\t\t: 'No services yet — create one with create_service (or create_dev_environment for an AI dev box), or via the dashboard.'\n\t\t\t\t: `Found ${data.items.length} service${data.items.length === 1 ? '' : 's'}${filterDesc ? ` matching ${filterDesc}` : ''}.`;\n\t\treturn respond({ summary, data });\n\t},\n});\n\ndefineTool({\n\tname: 'create_service',\n\tcategory: 'services',\n\tdescription: [\n\t\t'Create a new service in a project. Source is either a connected git repo (github_repo \"owner/name\", or github_repo_id) OR a pre-built docker_image — never both.',\n\t\t'',\n\t\t'When to use: the user wants to deploy something new. For a one-command AI dev environment specifically, prefer create_dev_environment (it also attaches the /workspace volume and sets the MCP keys).',\n\t\t'',\n\t\t'*** For a packaged app (WordPress, Ghost, n8n, Uptime Kuma, Vaultwarden), start at list_templates and pass template_id. *** Those templates are prebuilt images that only boot with the volume, scratch dirs, uid and companion database the platform attaches from the template id — the same image passed as a bare docker_image crash-loops on the read-only rootfs, and the fix is a redeploy away rather than an edit away.',\n\t\t'',\n\t\t'*** NOT for databases. *** If the user wants Postgres, Redis, MySQL, MariaDB or MongoDB, call create_database — do NOT create a service with docker_image \"postgres:16\" / \"redis:7\" / \"mongo\" / \"mysql\". A database deployed as a service is unmanaged: no backups, no version upgrades, no HA, no credential rotation, no metrics, no persistent volume, and nothing injects its URL into your app. `docker_image` here is for YOUR application images (or sidecars), not for datastores the platform already manages.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - project_id: numeric id or publicId (\"prj_…\") of the target project.',\n\t\t' - name: service name (1–100 chars).',\n\t\t' - type: \"web_service\" | \"private_service\" | \"worker\" | \"cron_job\" | \"static_site\".',\n\t\t' - docker_image (optional): pre-built image ref to deploy instead of building from source.',\n\t\t' - github_repo (optional): connect a connected GitHub repo by name — \"owner/name\". Preferred over github_repo_id: it is what you actually know, and a wrong name is refused here instead of failing the deploy.',\n\t\t\" - github_repo_id (optional): the same repo by its HostStack numeric id (from list_github_repos). NOT GitHub's id. Mutually exclusive with github_repo.\",\n\t\t' - git_repo_url (optional): a PUBLIC https git URL, for a repo that is not connected through GitHub. Private repos do not work here — they need a provider connection, because this field carries no credentials and embedding them in the URL is rejected. Mutually exclusive with github_repo_id and docker_image.',\n\t\t' - branch (optional): git branch (default \"main\").',\n\t\t' - install_command / build_command / start_command (optional): build/run shell commands. start_command is required for web/private services without a docker_image.',\n\t\t' - cron_schedule (optional): cron expression — required for cron_job.',\n\t\t' - publish_path (optional): static-site output dir (e.g. \"dist\").',\n\t\t' - runtime (optional): \"node\" | \"bun\" | \"python\" | … (auto-detected from a repo when omitted).',\n\t\t' - port (optional): the port the container listens on (1–65535). A source-built service should omit it — the platform injects $PORT and expects the app to bind that. Set it for a docker_image whose listen port is fixed by the image (WordPress 80, Ghost 2368, n8n 5678): the platform publishes and health-checks this port and never re-reads what the process actually bound, so an image listening elsewhere fails its first deploy on a container that is perfectly healthy.',\n\t\t' - template_id (optional): create from a quickstart template — an id from list_templates and nothing else. Everything the template brings (volumes, scratch dirs, uid, generated secrets, companion managed database) is resolved server-side and attached before the first deploy fires; there is no way to send those in the body. For an image template, pass its docker_image and port alongside, exactly as list_templates returns them.',\n\t\t' - plan (optional): service size (default \"micro\").',\n\t\t' - environment_id (optional): bind to a specific environment; defaults to the project Production env.',\n\t\t' - auto_deploy (optional, default true): trigger the first deploy immediately when a source is present.',\n\t\t\" - machine (optional): run it on one of the team's OWN enrolled machines (name or id, see list_machines) instead of HostStack compute. Pinned at creation and never moved afterwards, and the service is up only while that machine is.\",\n\t\t'',\n\t\t'Returns: { service: Service, deployId: number | null }.',\n\t\t'',\n\t\t'Example: create_service({ project_id: \"prj_abc\", name: \"api\", type: \"web_service\", github_repo: \"acme/api\" }) → { service: { publicId: \"svc_…\", repoUrl: \"https://github.com/acme/api\" }, deployId: 1234 }',\n\t\t'Repo not listed by list_github_repos? Call sync_github_repos — a repository pushed since the last sync is invisible until then.',\n\t\t'Example (one-click app): create_service({ project_id: \"prj_abc\", name: \"blog\", type: \"web_service\", template_id: \"wordpress\", docker_image: \"wordpress:php8.3-apache\", port: 80 }) — the wp-content volume, the www-data uid, the Apache scratch dirs and the managed MySQL come from the template id.',\n\t].join('\\n'),\n\tinput: {\n\t\tproject_id: z\n\t\t\t.union([z.number().int().positive(), z.string()])\n\t\t\t.describe('Target project — numeric id or publicId.'),\n\t\tname: z.string().min(1).max(100).describe('Service name (1–100 chars).'),\n\t\ttype: z.enum(SERVICE_TYPES).describe('Service type.'),\n\t\tdocker_image: z\n\t\t\t.string()\n\t\t\t.max(500)\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t'Pre-built APPLICATION image ref. Mutually exclusive with github_repo_id. Not for databases — use create_database for postgres/redis/mysql/mariadb/mongodb.',\n\t\t\t),\n\t\tgithub_repo: z\n\t\t\t.string()\n\t\t\t.min(1)\n\t\t\t.max(255)\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t'Connected GitHub repo as \"owner/name\" (see list_github_repos). Resolved server-side. Mutually exclusive with github_repo_id and docker_image.',\n\t\t\t),\n\t\tgithub_repo_id: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.positive()\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t\"HostStack row id of a connected GitHub repo (list_github_repos) — not GitHub's own id. Prefer github_repo. Mutually exclusive with docker_image.\",\n\t\t\t),\n\t\tgit_repo_url: z\n\t\t\t.string()\n\t\t\t.max(2048)\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t'Public https git URL. No embedded credentials — private repos need a provider connection. Mutually exclusive with github_repo_id and docker_image.',\n\t\t\t),\n\t\tbranch: z.string().max(200).optional().describe('Git branch (default \"main\").'),\n\t\tinstall_command: z.string().max(1000).optional().describe('Install shell command.'),\n\t\tbuild_command: z.string().max(1000).optional().describe('Build shell command.'),\n\t\tstart_command: z\n\t\t\t.string()\n\t\t\t.max(1000)\n\t\t\t.optional()\n\t\t\t.describe('Start shell command (required for web/private services without an image).'),\n\t\tcron_schedule: z\n\t\t\t.string()\n\t\t\t.max(100)\n\t\t\t.optional()\n\t\t\t.describe('Cron expression — required for cron_job.'),\n\t\tpublish_path: z.string().max(500).optional().describe('Static-site output dir.'),\n\t\truntime: z.string().max(50).optional().describe('Runtime hint (node/bun/python/…).'),\n\t\tport: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(1)\n\t\t\t.max(65535)\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t'Listen port, for a prebuilt image whose port is fixed by the image. Omit for a source-built service — it binds the injected $PORT.',\n\t\t\t),\n\t\ttemplate_id: z\n\t\t\t.string()\n\t\t\t.max(64)\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t'Quickstart template id from list_templates. Its volumes, scratch dirs, uid, secrets and companion database are resolved server-side; for an image template also pass its docker_image and port.',\n\t\t\t),\n\t\tplan: z.enum(SERVICE_PLANS).optional().describe('Service size (default \"micro\").'),\n\t\tenvironment_id: z\n\t\t\t.union([z.number().int().positive(), z.string()])\n\t\t\t.optional()\n\t\t\t.describe('Bind to a specific environment; defaults to Production.'),\n\t\tauto_deploy: z\n\t\t\t.boolean()\n\t\t\t.optional()\n\t\t\t.describe('Trigger the first deploy immediately (default true).'),\n\t\tmachine: machineInput,\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst projectId = await ctx.hoststack.resolveId(args.project_id, {\n\t\t\tkind: 'project',\n\t\t\tteamId,\n\t\t});\n\n\t\tconst input: Parameters<typeof ctx.hoststack.services.create>[1] = {\n\t\t\tname: args.name,\n\t\t\ttype: args.type,\n\t\t\tprojectId,\n\t\t};\n\t\tif (args.docker_image !== undefined) input.dockerImage = args.docker_image;\n\t\tif (args.github_repo !== undefined) input.githubRepo = args.github_repo;\n\t\tif (args.github_repo_id !== undefined) input.githubRepoId = args.github_repo_id;\n\t\tif (args.git_repo_url !== undefined) input.gitRepoUrl = args.git_repo_url;\n\t\tif (args.branch !== undefined) input.branch = args.branch;\n\t\tif (args.install_command !== undefined) input.installCommand = args.install_command;\n\t\tif (args.build_command !== undefined) input.buildCommand = args.build_command;\n\t\tif (args.start_command !== undefined) input.startCommand = args.start_command;\n\t\tif (args.cron_schedule !== undefined) input.cronSchedule = args.cron_schedule;\n\t\tif (args.publish_path !== undefined) input.publishPath = args.publish_path;\n\t\tif (args.runtime !== undefined) input.runtime = args.runtime;\n\t\tif (args.port !== undefined) input.port = args.port;\n\t\tif (args.template_id !== undefined) input.templateId = args.template_id;\n\t\tif (args.plan !== undefined) input.plan = args.plan;\n\t\tif (args.auto_deploy !== undefined) input.autoDeploy = args.auto_deploy;\n\t\tif (args.environment_id !== undefined) {\n\t\t\tinput.environmentId = await ctx.hoststack.resolveId(args.environment_id, {\n\t\t\t\tkind: 'environment',\n\t\t\t\tteamId,\n\t\t\t});\n\t\t}\n\t\tif (args.machine !== undefined) {\n\t\t\tinput.machineId = await resolveMachineId(ctx, teamId, args.machine);\n\t\t}\n\n\t\tconst response = await ctx.hoststack.services.create(teamId, input);\n\t\tconst data = {\n\t\t\tservice: shapeService(response.service),\n\t\t\tdeployId: (response as { deployId?: number | null }).deployId ?? null,\n\t\t};\n\t\tconst publicId =\n\t\t\tdata.service && 'publicId' in data.service\n\t\t\t\t? (data.service as { publicId: string }).publicId\n\t\t\t\t: 'unknown';\n\t\treturn respond({ summary: `Created service \"${args.name}\" (${publicId}).`, data });\n\t},\n});\n\ndefineTool({\n\tname: 'create_dev_environment',\n\tcategory: 'services',\n\tdescription: [\n\t\t'Spin up an AI Dev Box in one call: a cloud \"Dev Box\" (an agentic container from the dev-env image — Claude Code + Codex + OpenCode + MCPs preinstalled) inside a project, with a persistent /workspace volume and the MCP API keys set, then fire the first deploy.',\n\t\t'',\n\t\t'This makes a cloud Dev Box, NOT a project deploy environment. A project \"environment\" (create_environment → Production / Staging / Preview) is a deployment target; this is a workspace you code in. If you actually want a -dev deploy target, use create_environment instead.',\n\t\t'',\n\t\t'When to use: the user wants a cloud terminal / Dev Box they can drive coding agents in (from a desk or a phone). This mirrors the dashboard\\'s \"AI Dev Environment\" wizard preset — create (deploy deferred) → set keys → attach /workspace → deploy, so the first container boots with its volume and keys already in place.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - project_id: numeric id or publicId (\"prj_…\") of the target project.',\n\t\t' - name (optional): service name (default \"dev-environment\").',\n\t\t' - plan (optional): box size. Defaults to \"standard\" (2 GB) — the OOM-safe floor (DEV_ENV_MIN_SIZE); a coding agent + build OOMs below it. A smaller plan is clamped up to \"standard\".',\n\t\t' - disk_gb (optional): /workspace volume size in GB (default 10, min 10, max 10240).',\n\t\t' - hoststack_api_key (optional): sets HOSTSTACK_API_KEY so the hoststack MCP works inside the container.',\n\t\t' - poststack_api_key (optional): sets POSTSTACK_API_KEY so the poststack MCP works inside the container.',\n\t\t' - repo_url (optional): clone this git URL into /workspace on first boot (with optional branch).',\n\t\t'',\n\t\tDEV_BOX_INSIDE,\n\t\t'',\n\t\t'Returns: { service: Service, volumeAttached: boolean, deployId: number | null }. Open the Terminal tab on the service (dashboard or phone) once it is running; log in once with `claude /login` inside the container.',\n\t\t'',\n\t\t'Example: create_dev_environment({ project_id: \"prj_abc\", name: \"scratch\", hoststack_api_key: \"hs_live_…\" })',\n\t].join('\\n'),\n\tinput: {\n\t\tproject_id: z\n\t\t\t.union([z.number().int().positive(), z.string()])\n\t\t\t.describe('Target project — numeric id or publicId.'),\n\t\tname: z\n\t\t\t.string()\n\t\t\t.min(1)\n\t\t\t.max(100)\n\t\t\t.optional()\n\t\t\t.describe('Service name (default \"dev-environment\").'),\n\t\tplan: z\n\t\t\t.enum(SERVICE_PLANS)\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t'Box size (default \"standard\" — 2 GB, the OOM-safe floor; a smaller plan is clamped up to \"standard\").',\n\t\t\t),\n\t\tdisk_gb: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t// 10/10240 are the API's real bounds (HETZNER_BLOCK_VOLUME_MIN_GB /\n\t\t\t// _MAX_GB in packages/shared/src/constants/pricing.ts). This used to\n\t\t\t// say 1–100, which failed in both directions: `disk_gb: 5` passed\n\t\t\t// this gate, created a billable service, then 400'd on the volume\n\t\t\t// attach — and the handler treats that as fatal and tears the whole\n\t\t\t// box down again. Rejecting it here costs the caller one error\n\t\t\t// message instead of a create-and-destroy round trip.\n\t\t\t.min(10)\n\t\t\t.max(10240)\n\t\t\t.optional()\n\t\t\t.describe('/workspace volume size in GB (default 10, min 10, max 10240).'),\n\t\thoststack_api_key: z\n\t\t\t.string()\n\t\t\t.optional()\n\t\t\t.describe('Value for HOSTSTACK_API_KEY (enables the hoststack MCP in-container).'),\n\t\tpoststack_api_key: z\n\t\t\t.string()\n\t\t\t.optional()\n\t\t\t.describe('Value for POSTSTACK_API_KEY (enables the poststack MCP in-container).'),\n\t\trepo_url: z\n\t\t\t.string()\n\t\t\t.max(500)\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t'Clone this git URL into /workspace on first boot (HTTPS, or SSH once a key is set).',\n\t\t\t),\n\t\tbranch: z.string().max(200).optional().describe('Branch to clone (with repo_url).'),\n\t\tmachine: machineInput,\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst projectId = await ctx.hoststack.resolveId(args.project_id, {\n\t\t\tkind: 'project',\n\t\t\tteamId,\n\t\t});\n\t\tconst name = args.name ?? 'dev-environment';\n\t\tconst sizeGb = args.disk_gb ?? DEV_ENV_VOLUME.sizeGb;\n\t\t// Defence in depth: clamp the requested plan up to the OOM-safe dev-env\n\t\t// floor BEFORE asking the SDK to create the box. The server floors it\n\t\t// too, but this guarantees the tool never even requests a sub-`standard`\n\t\t// box that would OOM a coding agent on first boot.\n\t\tconst plan = coerceDevEnvSize(args.plan);\n\n\t\t// Defer the first deploy (autoDeploy:false) so we can set the MCP keys\n\t\t// and attach /workspace BEFORE the container boots — same ordering as\n\t\t// the dashboard wizard's use-new-service-form.\n\t\tconst createInput: Parameters<typeof ctx.hoststack.services.create>[1] = {\n\t\t\tname,\n\t\t\ttype: 'private_service',\n\t\t\tprojectId,\n\t\t\tdockerImage: DEV_ENV_IMAGE,\n\t\t\tautoDeploy: false,\n\t\t\tplan,\n\t\t};\n\t\t// Resolved BEFORE the service exists: an unknown machine name should cost\n\t\t// the caller an error message, not a created-then-torn-down box.\n\t\tif (args.machine !== undefined) {\n\t\t\tcreateInput.machineId = await resolveMachineId(ctx, teamId, args.machine);\n\t\t}\n\t\tconst created = await ctx.hoststack.services.create(teamId, createInput);\n\t\tconst service = created.service;\n\n\t\t// From here on the service EXISTS and is billable. Any later failure\n\t\t// must tear it back down so we never orphan a half-provisioned,\n\t\t// non-persistent box — see rollback() below. (The server's own\n\t\t// rollbackPartialDevBox only covers the dedicated dev-env create path,\n\t\t// not this generic create-then-attach sequence.)\n\t\tconst rollback = async (): Promise<void> => {\n\t\t\ttry {\n\t\t\t\t// tearDownDevEnvironment cascades the box + volume + cloned DB;\n\t\t\t\t// it only accepts dev-env services, which this is (dev-env image).\n\t\t\t\tawait ctx.hoststack.services.tearDownDevEnvironment(teamId, service.id);\n\t\t\t} catch {\n\t\t\t\ttry {\n\t\t\t\t\tawait ctx.hoststack.services.delete(teamId, service.id);\n\t\t\t\t} catch {\n\t\t\t\t\t// Best-effort: surface the original failure to the caller\n\t\t\t\t\t// even if cleanup itself fails (orphan will need manual\n\t\t\t\t\t// removal via delete_dev_environment / the dashboard).\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\n\t\ttry {\n\t\t\tconst envVars: { key: string; value: string; isSecret: boolean }[] = [];\n\t\t\tif (args.hoststack_api_key)\n\t\t\t\tenvVars.push({\n\t\t\t\t\tkey: 'HOSTSTACK_API_KEY',\n\t\t\t\t\tvalue: args.hoststack_api_key,\n\t\t\t\t\tisSecret: true,\n\t\t\t\t});\n\t\t\tif (args.poststack_api_key)\n\t\t\t\tenvVars.push({\n\t\t\t\t\tkey: 'POSTSTACK_API_KEY',\n\t\t\t\t\tvalue: args.poststack_api_key,\n\t\t\t\t\tisSecret: true,\n\t\t\t\t});\n\t\t\t// Clone an arbitrary repo at boot (the image entrypoint reads these).\n\t\t\tif (args.repo_url) {\n\t\t\t\tenvVars.push({\n\t\t\t\t\tkey: 'HOSTSTACK_DEVENV_REPO_URL',\n\t\t\t\t\tvalue: args.repo_url,\n\t\t\t\t\tisSecret: false,\n\t\t\t\t});\n\t\t\t\tif (args.branch)\n\t\t\t\t\tenvVars.push({\n\t\t\t\t\t\tkey: 'HOSTSTACK_DEVENV_BRANCH',\n\t\t\t\t\t\tvalue: args.branch,\n\t\t\t\t\t\tisSecret: false,\n\t\t\t\t\t});\n\t\t\t}\n\t\t\tif (envVars.length > 0) {\n\t\t\t\tawait ctx.hoststack.envVars.bulkSet(teamId, service.id, { vars: envVars });\n\t\t\t}\n\n\t\t\t// A Dev Box without its /workspace volume silently loses all\n\t\t\t// out-of-image work on the next recreate. ABORT rather than deploy\n\t\t\t// a non-persistent box: a missing volume is a hard failure here.\n\t\t\ttry {\n\t\t\t\tawait ctx.hoststack.volumes.create(teamId, service.id, {\n\t\t\t\t\tname: DEV_ENV_VOLUME.name,\n\t\t\t\t\tmountPath: DEV_ENV_VOLUME.mountPath,\n\t\t\t\t\tsizeGb,\n\t\t\t\t});\n\t\t\t} catch (error: unknown) {\n\t\t\t\tconst reason = error instanceof Error ? error.message : String(error);\n\t\t\t\tawait rollback();\n\t\t\t\treturn respondError(\n\t\t\t\t\t`Failed to attach the /workspace volume for Dev Box \"${name}\" — aborted and rolled back rather than deploy a box that would lose all work on recreate. Volume error: ${reason}`,\n\t\t\t\t\t{ volumeError: reason, rolledBack: true },\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst deploy = await ctx.hoststack.deploys.trigger(teamId, service.id);\n\t\t\tconst deployId = deploy.deploy?.id ?? null;\n\n\t\t\treturn respond({\n\t\t\t\tsummary: `Created AI Dev Box \"${name}\" (${service.publicId}) with a /workspace volume — deploying. Open it in the dashboard's Dev Boxes section (or the service's Terminal tab) once running.`,\n\t\t\t\tdata: { service: shapeService(service), volumeAttached: true, deployId },\n\t\t\t});\n\t\t} catch (error: unknown) {\n\t\t\t// Any post-create failure (env-var set, deploy trigger, …) tears the\n\t\t\t// billable box back down so nothing is orphaned.\n\t\t\tconst reason = error instanceof Error ? error.message : String(error);\n\t\t\tawait rollback();\n\t\t\treturn respondError(\n\t\t\t\t`Failed to provision Dev Box \"${name}\" — rolled back the partially-created box. Error: ${reason}`,\n\t\t\t\t{ rolledBack: true },\n\t\t\t);\n\t\t}\n\t},\n});\n\ndefineTool({\n\tname: 'spin_up_dev_environment',\n\tcategory: 'services',\n\tdescription: [\n\t\t'Spin up an agentic Dev Box FROM an existing service so you can reproduce a bug, fix it, view it, and ship it. Creates a cloud Dev Box (Claude/Codex/OpenCode + MCPs) that RUNS a clone of the app: the repo is auto-cloned into /workspace, the service env-vars are copied, and its linked database is cloned (so the box never touches the prod DB). The box gets an unguessable public dev URL and seamless `git push`.',\n\t\t'',\n\t\t'This makes a cloud Dev Box, NOT a project deploy environment — for a -dev/-staging deploy target use create_environment instead.',\n\t\t'',\n\t\t'When to use: \"spin up a dev environment for <service>\", \"I found a bug on <service>, give me a box to fix it\". Distinct from create_dev_environment, which makes a BARE box not tied to any app.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: the source service to debug — numeric id or publicId (\"svc_…\").',\n\t\t' - include_database_clone (optional): clone the linked database so the app runs on a copy of real data (default true).',\n\t\t' - name (optional): dev box name (default \"<source>-dev\").',\n\t\t'',\n\t\tDEV_BOX_INSIDE,\n\t\t'',\n\t\t'Returns: { service, devUrl, deployId }. Once it is live: open the Terminal tab, run `claude`, start the dev server (it must bind $PORT), and view the app at https://<devUrl>. Tear it all down later with delete_dev_environment.',\n\t\t'',\n\t\t'Example: spin_up_dev_environment({ service_id: \"svc_api\" }) → a dev box running a clone of the api service (repo + env-vars + cloned DB) with a public dev URL.',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z\n\t\t\t.union([z.number().int().positive(), z.string()])\n\t\t\t.describe('Source service to debug — numeric id or publicId.'),\n\t\tinclude_database_clone: z\n\t\t\t.boolean()\n\t\t\t.optional()\n\t\t\t.describe('Clone the linked database so the app runs on copied data (default true).'),\n\t\tname: z\n\t\t\t.string()\n\t\t\t.min(1)\n\t\t\t.max(100)\n\t\t\t.optional()\n\t\t\t.describe('Dev box name (default \"<source>-dev\").'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst serviceId = await ctx.hoststack.resolveId(args.service_id, {\n\t\t\tkind: 'service',\n\t\t\tteamId,\n\t\t});\n\t\tconst opts: { includeDatabaseClone?: boolean; name?: string } = {};\n\t\tif (args.include_database_clone !== undefined)\n\t\t\topts.includeDatabaseClone = args.include_database_clone;\n\t\tif (args.name !== undefined) opts.name = args.name;\n\t\tconst result = await ctx.hoststack.services.spinUpDevEnvironment(teamId, serviceId, opts);\n\t\treturn respond({\n\t\t\tsummary: `Spun up dev environment \"${result.service.name}\" (${result.service.publicId}) — deploying. Once live: open the Terminal tab, run \\`claude\\`, start the dev server on $PORT, and view it at https://${result.devUrl}. \\`git push\\` works from inside.`,\n\t\t\tdata: {\n\t\t\t\tservice: shapeService(result.service),\n\t\t\t\tdevUrl: result.devUrl,\n\t\t\t\tdeployId: result.deployId,\n\t\t\t},\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'delete_dev_environment',\n\tcategory: 'services',\n\tdescription: [\n\t\t'Tear down an agentic Dev Box in one call: removes the cloud Dev Box AND cascade-deletes its cloned database, its /workspace volume, and the auto-created `development` environment if it is now empty.',\n\t\t'',\n\t\t'This deletes a cloud Dev Box, NOT a project deploy environment — to remove a -dev/-staging deploy target use delete_environment instead.',\n\t\t'',\n\t\t'When to use: \"delete / tear down the dev environment\", once you have shipped the fix and no longer need the box.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: the dev box to tear down — numeric id or publicId.',\n\t\t'',\n\t\t'Returns: { ok: true }. Irreversible — the cloned database and workspace volume are deleted.',\n\t\t'',\n\t\t'Example: delete_dev_environment({ service_id: \"svc_api_dev\" }) → removes the dev box, its cloned database, and the /workspace volume.',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z\n\t\t\t.union([z.number().int().positive(), z.string()])\n\t\t\t.describe('The dev box to tear down — numeric id or publicId.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst serviceId = await ctx.hoststack.resolveId(args.service_id, {\n\t\t\tkind: 'service',\n\t\t\tteamId,\n\t\t});\n\t\tawait ctx.hoststack.services.tearDownDevEnvironment(teamId, serviceId);\n\t\treturn respond({\n\t\t\tsummary:\n\t\t\t\t'Dev environment torn down — box, cloned database, /workspace volume, and empty development environment removed.',\n\t\t\tdata: { ok: true },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'resize_dev_environment',\n\tcategory: 'services',\n\tdescription: [\n\t\t'Resize a cloud Dev Box (or any service) to a different size tier — the supported way to give it more memory/CPU/disk headroom (e.g. when ESLint/tsc OOMs).',\n\t\t'',\n\t\t'This resizes a cloud Dev Box, NOT a project deploy environment.',\n\t\t'',\n\t\t'When to use: a Dev Box OOM-killed (see exitReason/recommendedSize from list_dev_environments), or you just want more headroom. This changes the SIZE TIER.',\n\t\t'',\n\t\t\"How it applies: the new tier's memory + CPU take effect LIVE on the running container (no recreate, no dropped shell sessions); a larger disk takes effect on the next recreate (suspend→resume). Dev boxes are floored to the OOM-safe minimum size server-side.\",\n\t\t'',\n\t\t'The tier is not always the container\\'s limit, so check `effectiveMemoryMb` from list_dev_environments rather than assuming: a dedicated (Pro) tier resolves ~1.5 GB below nominal for agent headroom, and a per-service memory override REPLACES the tier figure rather than merely clamping to it. An UPSIZE now retires an override that would hold the box below its new tier; a re-assert of the SAME tier does not, so on a box already at the top tier a resize cannot lift an override — clear it with a config PATCH of memoryMb to 512 (the legacy floor, which means \"no override\"), or use the Remove-cap button on the box\\'s Settings → General tab. `memoryPinnedBelowTier: true` is how you tell that case apart from a box that is genuinely maxed out.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: the box to resize — numeric id or publicId.',\n\t\t' - size: target tier — one of the service catalog sizes (e.g. \"standard\", \"large\", \"xlarge\").',\n\t\t'',\n\t\t'Returns: { service } with the new plan.',\n\t\t'',\n\t\t'Example: resize_dev_environment({ service_id: \"svc_skyskraber_dev\", size: \"large\" }) → bumps the box to the large tier, applied live.',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z\n\t\t\t.union([z.number().int().positive(), z.string()])\n\t\t\t.describe('The box to resize — numeric id or publicId.'),\n\t\tsize: z\n\t\t\t.enum(SERVICE_PLANS)\n\t\t\t.describe(\n\t\t\t\t'Target size tier (service catalog size, e.g. \"standard\", \"large\", \"xlarge\").',\n\t\t\t),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst serviceId = await ctx.hoststack.resolveId(args.service_id, {\n\t\t\tkind: 'service',\n\t\t\tteamId,\n\t\t});\n\t\tconst { service } = await ctx.hoststack.services.resize(teamId, serviceId, args.size);\n\t\treturn respond({\n\t\t\tsummary: `Resized to ${service.plan} — memory/CPU applied live; disk grows on next recreate.`,\n\t\t\tdata: { service: shapeService(service) },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'list_dev_environments',\n\tcategory: 'services',\n\tdescription: [\n\t\t'List the team\\'s cloud Dev Boxes (the dashboard \"Dev Boxes\" / Development section), each annotated with the companion services attached to it.',\n\t\t'',\n\t\t\"These are cloud Dev Boxes, NOT project deploy environments — to list a project's deploy targets (Production / Staging / Preview) use list_environments instead.\",\n\t\t'',\n\t\t'When to use: \"show my dev environments\", before opening/tearing one down, to find a box\\'s id.',\n\t\t'',\n\t\t'Returns: { items: [{ ...service, devUrl, databases, exitReason, recommendedSize, effectiveMemoryMb, memoryPinnedBelowTier }] } where `databases` lists the companion engines wired into the box (e.g. [\"postgres\",\"redis\"]). `exitReason` is \"oom_killed\" / \"crashed\" / null for the box\\'s last container exit; when it is \"oom_killed\", `recommendedSize` is the next tier up to rescale to (use resize_dev_environment).',\n\t\t'',\n\t\t'`effectiveMemoryMb` is the container\\'s REAL ceiling — what `/sys/fs/cgroup/memory.max` reads inside the box — and is the number to size any in-box work against. Do NOT derive it from `plan`: a dedicated (Pro) tier sits ~1.5 GB below nominal, and a per-service override replaces the tier figure outright. When `memoryPinnedBelowTier` is true an override, not the tier, is the cap, so `recommendedSize: null` means \"nothing bigger to sell you\" rather than \"nothing you can do\" — see resize_dev_environment for how to lift it.',\n\t\t'',\n\t\t'Example: list_dev_environments() → every dev box for the active team.',\n\t].join('\\n'),\n\tinput: {},\n\thandler: async (_args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst { environments } = await ctx.hoststack.services.listDevEnvironments(teamId);\n\t\tconst oomCount = environments.filter((e) => e.exitReason === 'oom_killed').length;\n\t\treturn respond({\n\t\t\tsummary:\n\t\t\t\t`${environments.length} dev environment${environments.length === 1 ? '' : 's'}.` +\n\t\t\t\t(oomCount > 0\n\t\t\t\t\t? ` ${oomCount} recently OOM-killed — consider resize_dev_environment.`\n\t\t\t\t\t: ''),\n\t\t\tdata: {\n\t\t\t\titems: environments.map((env) => ({\n\t\t\t\t\t...shapeService(env),\n\t\t\t\t\tdevUrl: env.devUrl ?? null,\n\t\t\t\t\tdatabases: env.databases ?? [],\n\t\t\t\t\texitReason: env.exitReason ?? null,\n\t\t\t\t\trecommendedSize: env.recommendedSize ?? null,\n\t\t\t\t\t// The number an agent should size its work against. Absent it,\n\t\t\t\t\t// the only honest way to learn a box's ceiling was to shell in\n\t\t\t\t\t// and read the cgroup — so every box rediscovered its own.\n\t\t\t\t\teffectiveMemoryMb: env.effectiveMemoryMb ?? null,\n\t\t\t\t\tmemoryPinnedBelowTier: env.memoryPinnedBelowTier ?? false,\n\t\t\t\t})),\n\t\t\t},\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'list_templates',\n\tcategory: 'services',\n\tdescription: [\n\t\t'List the app quickstart templates the dashboard New-Service wizard offers, plus the separate Dev Box preset. Two kinds live in one list: a stack template (Next.js, Django, SvelteKit, Spring Boot, …) is a pre-filled install/build/start command set you pair with a Git repo, and a one-click app (WordPress, Ghost, n8n, Uptime Kuma, Vaultwarden) is a prebuilt image that needs no repo at all.',\n\t\t'',\n\t\t'When to use: whenever asked to deploy a known stack (\"deploy my Next.js app\", \"put this Django project on HostStack\"), before hand-writing a start command; whenever asked for a packaged app (\"deploy WordPress\", \"I want a Ghost blog\"), before reaching for a bare docker_image; and before creating a Dev Box, to confirm the image, /workspace size, plan floor, and companion engines instead of duplicating those constants.',\n\t\t'',\n\t\t'Returns: { templates: [{ id, name, description, type, runtime?, installCommand?, buildCommand?, startCommand?, publishPath?, cronSchedule?, dockerImage?, port?, requiresTemplateId? }], devBox: { id, name, image, volume: { name, mountPath, sizeGb }, minPlan, createWith, companions, inBox } }. A template WITHOUT `dockerImage` is source-built — pass its commands to create_service with a `github_repo_id`. A template WITH `dockerImage` is flagged `requiresTemplateId` and must be created as create_service({ template_id, docker_image, port, … }): the volume, scratch dirs, uid, generated secrets and companion managed database it needs are attached server-side from the id, and the same image sent without it crash-loops on the read-only rootfs. The Dev Box is NOT in `templates`: it is created with create_standalone_dev_environment / create_dev_environment, never create_service. `companions` are SEPARATE managed databases you can attach; `inBox` is what the box already runs on its own (engines via `dev-services`, language runtimes via `dev-runtime`) — check it before concluding a box cannot run something.',\n\t\t'',\n\t\t'Example: list_templates() → { templates: [{ id: \"nextjs-ssr\", name: \"Next.js\", type: \"web_service\", runtime: \"node\", installCommand: \"npm install\", buildCommand: \"npm run build\", startCommand: \"npm run start\" }, { id: \"wordpress\", name: \"WordPress\", type: \"web_service\", dockerImage: \"wordpress:php8.3-apache\", port: 80, requiresTemplateId: true }, …], devBox: { id: \"dev-environment\", image: \"registry.hoststack.dev/hoststack/dev-env:latest\", volume: { mountPath: \"/workspace\", sizeGb: 10 }, minPlan: \"standard\", companions: [\"postgres\",\"redis\",\"meilisearch\"] } }',\n\t].join('\\n'),\n\tinput: {},\n\thandler: async () => {\n\t\t// The Dev Box preset is assembled from THIS package's own constants\n\t\t// (DEV_ENV_IMAGE / DEV_ENV_VOLUME / DEV_ENV_MIN_SIZE), and the app\n\t\t// catalog from lib/app-templates.ts. Both are copies of\n\t\t// packages/shared, NOT imports, per the workspace constraint (the MCP\n\t\t// package must not depend on the private @hoststack/shared package).\n\t\t//\n\t\t// It is returned under its own key rather than in `templates`: it is not\n\t\t// creatable with create_service (hard plan floor, /workspace volume,\n\t\t// companion DBs), and folding it into the same list is exactly what made\n\t\t// an agent try to build one through the wrong tool.\n\t\tconst devBox = {\n\t\t\tid: 'dev-environment',\n\t\t\tname: 'AI Dev Environment',\n\t\t\timage: DEV_ENV_IMAGE,\n\t\t\tvolume: {\n\t\t\t\tname: DEV_ENV_VOLUME.name,\n\t\t\t\tmountPath: DEV_ENV_VOLUME.mountPath,\n\t\t\t\tsizeGb: DEV_ENV_VOLUME.sizeGb,\n\t\t\t},\n\t\t\t/** OOM-safe plan floor — a coding agent + build needs at least this. */\n\t\t\tminPlan: DEV_ENV_MIN_SIZE,\n\t\t\t/** Create it with these, never create_service. */\n\t\t\tcreateWith: ['create_standalone_dev_environment', 'create_dev_environment'],\n\t\t\t/** Companion engines you can attach (fresh + empty), like local `make db-up`. */\n\t\t\tcompanions: ['postgres', 'redis', 'meilisearch'],\n\t\t\t/**\n\t\t\t * What the box can already run without attaching anything. Reported\n\t\t\t * so an agent picking a template can see that \"it needs MySQL\" or\n\t\t\t * \"it's a PHP app\" is already satisfied, instead of concluding the\n\t\t\t * box is unsuitable and reaching for Docker (there is no daemon).\n\t\t\t */\n\t\t\tinBox: {\n\t\t\t\tengines: {\n\t\t\t\t\tdefault: ['postgres', 'redis', 'meilisearch'],\n\t\t\t\t\toptIn: ['mysql', 'mongodb'],\n\t\t\t\t\tstart: 'dev-services up [mysql|mongodb]',\n\t\t\t\t},\n\t\t\t\truntimes: {\n\t\t\t\t\tbuiltin: ['node', 'bun', 'python', 'php'],\n\t\t\t\t\t// erlang is listed because elixir cannot run without it —\n\t\t\t\t\t// see DEV_BOX_INSIDE. Ordering matters for a caller acting\n\t\t\t\t\t// on this: add erlang, then elixir.\n\t\t\t\t\tonDemand: ['go', 'java', 'ruby', 'rust', 'erlang', 'elixir', 'dotnet'],\n\t\t\t\t\tinstall: 'dev-runtime add <name>',\n\t\t\t\t},\n\t\t\t},\n\t\t};\n\t\t// The image templates carry the flag per row rather than leaving it to the\n\t\t// prose above. Everything else in this list is \"copy these fields into\n\t\t// create_service\", and an entry that looks the same but silently needs a\n\t\t// `template_id` alongside is the one difference a caller skimming the\n\t\t// JSON would miss — and would only find out about from a crash-looping\n\t\t// container two deploys later.\n\t\tconst templates = MCP_APP_TEMPLATES.map((t) =>\n\t\t\tt.dockerImage ? { ...t, requiresTemplateId: true } : t,\n\t\t);\n\t\t// Counted, not enumerated: respond() hard-throws over SUMMARY_MAX_LEN\n\t\t// (500), and a summary that lists every id would start failing at\n\t\t// runtime the moment the catalog grows. The ids are in `data` anyway.\n\t\tconst byType = new Map<string, number>();\n\t\tfor (const t of templates) byType.set(t.type, (byType.get(t.type) ?? 0) + 1);\n\t\tconst breakdown = [...byType].map(([type, n]) => `${n} ${type}`).join(', ');\n\t\treturn respond({\n\t\t\tsummary:\n\t\t\t\t`${templates.length} app templates (${breakdown}) — pick one by id, then either pass its ` +\n\t\t\t\t'commands to create_service with a github_repo_id, or, if it has a dockerImage, ' +\n\t\t\t\t'pass template_id + docker_image + port. Plus the Dev Box preset ' +\n\t\t\t\t`(image ${DEV_ENV_IMAGE}, /workspace ${DEV_ENV_VOLUME.sizeGb} GB, floor \"${DEV_ENV_MIN_SIZE}\"), ` +\n\t\t\t\t'which is created with create_standalone_dev_environment, not create_service.',\n\t\t\tdata: { templates, devBox },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'create_standalone_dev_environment',\n\tcategory: 'services',\n\tdescription: [\n\t\t\"Create a STANDALONE Dev Box in one call: a cloud box (Claude Code + Codex + OpenCode + MCPs) on a persistent /workspace, from a connected GitHub repo, an arbitrary clone URL, or blank — with optional companion Postgres / Redis / Meilisearch wired into its env (mirrors a local `make db-up`). The box lives in the team's hidden Development home, NOT under a project.\",\n\t\t'',\n\t\t'This makes a cloud Dev Box, NOT a project deploy environment (create_environment → Production / Staging / Preview). It is a workspace you code in, not a deployment target.',\n\t\t'',\n\t\tDEV_BOX_INSIDE,\n\t\t'',\n\t\t\"Those in-box engines are the no-Docker replacement for a local `make db-up`, and are the right answer for development inside the box. The `companions` option is a DIFFERENT thing: it attaches SEPARATE managed databases to the box's environment, for when the box needs a real, persistent, backed-up datastore rather than a scratch one.\",\n\t\t'',\n\t\t'When to use: \"create a dev environment for <repo>\", \"spin me up a cloud dev box with a Postgres\". Distinct from spin_up_dev_environment (which clones an EXISTING service) and create_dev_environment (a bare box in a chosen project).',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - name (optional): the Dev Box name. Omit it and the box is named after its source (the repo name, or \"dev-box\" when blank), with a numeric suffix if that name is taken — so \"give me a dev box\" needs no invented name.',\n\t\t' - source_kind: \"github_repo\" (clone a connected repo — needs github_repo_id), \"url\" (clone any http(s) git URL — needs clone_url), or \"blank\" (empty box).',\n\t\t' - github_repo_id (for github_repo): HostStack row id of a connected GitHub repo — call list_github_repos for it. It is NOT the id GitHub shows, and it cannot be guessed.',\n\t\t' - clone_url (for url): an http(s) git clone URL.',\n\t\t' - branch (optional): branch to clone.',\n\t\t' - databases (optional): companion services to attach — any of \"postgres\", \"redis\", \"meilisearch\".',\n\t\t\" - machine (optional): run the box on one of the team's OWN enrolled machines (name or id, see list_machines) instead of HostStack compute. /workspace then lives on that machine's disk, so the box is reachable only while it is switched on, and any companion databases are created there too.\",\n\t\t' - plan (optional): box size. Defaults to \"standard\" (2 GB) — the OOM-safe floor (DEV_ENV_MIN_SIZE); a smaller plan is floored up to \"standard\" server-side.',\n\t\t\" - agent_accounts (optional): bind specific saved agent logins by account id per provider. OMIT to auto-inherit the box owner's default logins (claude/codex/opencode), so `claude` is already authenticated on first boot — no manual login.\",\n\t\t'',\n\t\t'Returns: { service, devUrl, deployId } — deploying. Once live: open the Terminal tab, run `claude`, start the dev server on $PORT, view at https://<devUrl>. Tear down with delete_dev_environment.',\n\t\t'',\n\t\t'Example: list_github_repos({ q: \"app\" }) → id 42, then create_standalone_dev_environment({ name: \"app-dev\", source_kind: \"github_repo\", github_repo_id: 42, databases: [\"postgres\",\"redis\"] })',\n\t].join('\\n'),\n\tinput: {\n\t\tname: z\n\t\t\t.string()\n\t\t\t.min(1)\n\t\t\t.max(100)\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t'Dev Box name. Omit to have it named after the source (the repo name, or \"dev-box\" for a blank one), de-duplicated against existing boxes.',\n\t\t\t),\n\t\tsource_kind: z.enum(['github_repo', 'url', 'blank']).describe('Where the code comes from.'),\n\t\tgithub_repo_id: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.positive()\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t'Connected GitHub repo id from list_github_repos (required when source_kind=\"github_repo\").',\n\t\t\t),\n\t\tclone_url: z\n\t\t\t.string()\n\t\t\t.url()\n\t\t\t.optional()\n\t\t\t.describe('http(s) git clone URL (required when source_kind=\"url\").'),\n\t\tbranch: z.string().min(1).max(255).optional().describe('Branch to clone.'),\n\t\tdatabases: z\n\t\t\t.array(z.enum(['postgres', 'redis', 'meilisearch']))\n\t\t\t.optional()\n\t\t\t.describe('Companion services to attach (fresh + empty).'),\n\t\tplan: z\n\t\t\t.enum(SERVICE_PLANS)\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t'Box size (default \"standard\" — 2 GB; a smaller plan is floored to \"standard\").',\n\t\t\t),\n\t\tagent_accounts: z\n\t\t\t.array(\n\t\t\t\tz.object({\n\t\t\t\t\tprovider: z.enum(['claude', 'codex', 'opencode']),\n\t\t\t\t\taccount_id: z.number().int().positive(),\n\t\t\t\t}),\n\t\t\t)\n\t\t\t.max(3)\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t\"Bind saved agent logins by account id per provider. Omit to inherit the box owner's default logins automatically.\",\n\t\t\t),\n\t\tmachine: machineInput,\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tlet source: CreateDevEnvironmentInput['source'];\n\t\tif (args.source_kind === 'github_repo') {\n\t\t\tif (!args.github_repo_id) {\n\t\t\t\treturn respond({\n\t\t\t\t\tsummary: 'github_repo_id is required when source_kind is \"github_repo\".',\n\t\t\t\t\tdata: { ok: false },\n\t\t\t\t});\n\t\t\t}\n\t\t\tsource = {\n\t\t\t\tkind: 'github_repo',\n\t\t\t\tgithubRepoId: args.github_repo_id,\n\t\t\t\t...(args.branch ? { branch: args.branch } : {}),\n\t\t\t};\n\t\t} else if (args.source_kind === 'url') {\n\t\t\tif (!args.clone_url) {\n\t\t\t\treturn respond({\n\t\t\t\t\tsummary: 'clone_url is required when source_kind is \"url\".',\n\t\t\t\t\tdata: { ok: false },\n\t\t\t\t});\n\t\t\t}\n\t\t\tsource = {\n\t\t\t\tkind: 'url',\n\t\t\t\tcloneUrl: args.clone_url,\n\t\t\t\t...(args.branch ? { branch: args.branch } : {}),\n\t\t\t};\n\t\t} else {\n\t\t\tsource = { kind: 'blank' };\n\t\t}\n\t\tconst machineId =\n\t\t\targs.machine !== undefined\n\t\t\t\t? await resolveMachineId(ctx, teamId, args.machine)\n\t\t\t\t: undefined;\n\t\tconst input: CreateDevEnvironmentInput = {\n\t\t\t...(args.name ? { name: args.name } : {}),\n\t\t\tsource,\n\t\t\t...(machineId !== undefined ? { machineId } : {}),\n\t\t\t...(args.databases ? { databases: args.databases } : {}),\n\t\t\t...(args.plan ? { plan: args.plan } : {}),\n\t\t\t...(args.agent_accounts && args.agent_accounts.length > 0\n\t\t\t\t? {\n\t\t\t\t\t\tagentAccounts: args.agent_accounts.map((a) => ({\n\t\t\t\t\t\t\tprovider: a.provider,\n\t\t\t\t\t\t\taccountId: a.account_id,\n\t\t\t\t\t\t})),\n\t\t\t\t\t}\n\t\t\t\t: {}),\n\t\t};\n\t\tconst result = await ctx.hoststack.services.createDevEnvironment(teamId, input);\n\t\treturn respond({\n\t\t\tsummary: `Created dev environment \"${result.service.name}\" (${result.service.publicId}) — deploying. Once live: open the Terminal tab, run \\`claude\\`, start the dev server on $PORT, and view it at https://${result.devUrl}.`,\n\t\t\tdata: {\n\t\t\t\tservice: shapeService(result.service),\n\t\t\t\tdevUrl: result.devUrl,\n\t\t\t\tdeployId: result.deployId,\n\t\t\t},\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'get_service',\n\tcategory: 'services',\n\tdescription: [\n\t\t'Fetch a single service by ID with its current status AND its service_config row (resources, health-check tuning, scaling, restart policy).',\n\t\t'',\n\t\t'When to use: drilling into a service after list_services, checking deploy/runtime status, grabbing the repo+branch before triggering a deploy, or inspecting the health-check / autoscale knobs before tweaking them via update_service_config.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service (e.g. \"svc_abc123\").',\n\t\t'',\n\t\t'Returns: { service: Service, config: ServiceConfig } — service has type/status/runtime/branch/autoDeploy/region/plan/timestamps plus repoProvider/repoFullName/repoUrl for a source-built service (absent when it deploys a prebuilt image); config has memoryMb, cpuShares, diskSizeGb, port, protocol, healthCheckEnabled, healthCheckInterval, healthCheckTimeout, healthCheckGracePeriodSec, allowSearchIndexing, restartPolicy, deployStrategy (\"rolling\" | \"recreate\"), preDeployCommand, min/maxInstances, scale thresholds.',\n\t\t'',\n\t\t'Example: get_service({ service_id: \"svc_abc\" }) → { service: { type: \"web\", status: \"running\", … }, config: { healthCheckGracePeriodSec: 120, … } }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId (e.g. svc_abc123).'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\t// Bundle service + config so a single call answers both \"how is it\n\t\t// running?\" and \"how is it tuned?\". Previously callers had to follow\n\t\t// up with a separate read for any of the health-check / scaling /\n\t\t// resource knobs, which lived only on the service_config row.\n\t\tconst [serviceResponse, configResponse] = await Promise.all([\n\t\t\tctx.hoststack.services.get(teamId, args.service_id),\n\t\t\tctx.hoststack.services.getConfig(teamId, args.service_id),\n\t\t]);\n\t\tconst data = {\n\t\t\tservice: shapeService(serviceResponse.service),\n\t\t\tconfig: shape(configResponse.config),\n\t\t};\n\t\tconst status =\n\t\t\tdata.service && 'status' in data.service\n\t\t\t\t? (data.service as { status: string }).status\n\t\t\t\t: 'unknown';\n\t\treturn respond({ summary: `Service ${args.service_id} is ${status}.`, data });\n\t},\n});\n\ndefineTool({\n\tname: 'get_service_metrics',\n\tcategory: 'services',\n\tdescription: [\n\t\t'Fetch the latest CPU, memory, network, and request-rate metrics for a service.',\n\t\t'',\n\t\t'When to use: a service is reportedly slow, you suspect resource pressure, or you want a quick health snapshot before scaling. Returns a single point-in-time sample. For a time series — \"is memory growing?\", \"did CPU spike during that deploy?\" — use get_service_metrics_history instead.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t'',\n\t\t'Returns: { metrics: { cpu, memory, network, requests } } — values are normalised utilisation/throughput numbers.',\n\t\t'',\n\t\t'Example: get_service_metrics({ service_id: \"svc_abc\" }) → { metrics: { cpu: 0.42, memory: 0.71, … } }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.services.getMetrics(teamId, args.service_id);\n\t\tconst data = { metrics: shape(response.metrics) };\n\t\treturn respond({ summary: `Metrics snapshot for service ${args.service_id}.`, data });\n\t},\n});\n\ndefineTool({\n\tname: 'get_service_metrics_history',\n\tcategory: 'services',\n\tdescription: [\n\t\t'Fetch a metrics time series (CPU, memory, network, disk) for a service.',\n\t\t'',\n\t\t'When to use: answering \"is memory growing over the last hour?\", \"did CPU spike during deploy X?\", or \"what does normal look like for this service?\". Pairs well with get_deploy to align spikes against deploy times.',\n\t\t'',\n\t\t'Inputs (all optional — omit both for the trailing hour):',\n\t\t' - service_id: publicId of the service.',\n\t\t' - from: ISO-8601 lower bound OR relative offset like \"-15m\" / \"-2h\" / \"-7d\".',\n\t\t' - to: ISO-8601 upper bound (or relative offset). Defaults to now.',\n\t\t'',\n\t\t'Resolution: ≤7d → raw samples (one per ~30s), >7d → hourly pre-aggregates, >30d → daily. At most 300 points are returned, so a window wider than ~2.5h is thinned to fit.',\n\t\t'',\n\t\t'`cpuPercent` is the PEAK of whatever the point covers, and 100 means one host CORE — not 100% of the service. A `micro` (500 millicores) is capped by its cgroup at ~50, so ~50 IS that plan pegged. To compare against a `service.pressure_sustained` alert, which reports a share of the plan allowance, multiply by 1000/cpuMillicores (the alert ships that denominator in its metadata).',\n\t\t'',\n\t\t'Memory is the reading at that point, NOT a peak — so a memory number here can sit below an alert that averaged the hour.',\n\t\t'',\n\t\t'Returns: { history: Array<{ timestamp, cpuPercent, memoryUsedMb, memoryLimitMb, networkRxBytes, networkTxBytes, diskUsedMb }> }.',\n\t\t'',\n\t\t'Example: get_service_metrics_history({ service_id: \"svc_abc\", from: \"-1h\" }) → ~120 points for the last hour.',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t\tfrom: z\n\t\t\t.string()\n\t\t\t.optional()\n\t\t\t.describe('ISO-8601 lower bound or relative offset (e.g. \"-1h\", \"-2d\").'),\n\t\tto: z.string().optional().describe('ISO-8601 upper bound; defaults to now.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\t// Translate the same `-Nm/h/d` relative shorthand we accept in logs\n\t\t// so callers don't have to learn a second convention. The metrics\n\t\t// history endpoint expects ISO timestamps server-side; we resolve\n\t\t// relative offsets here for symmetry with get_service_logs.\n\t\tconst opts: { from?: string; to?: string } = {};\n\t\tconst resolveRel = (raw: string): string => {\n\t\t\tconst rel = /^-(\\d+)(s|m|h|d)$/.exec(raw.trim());\n\t\t\tif (!rel) return raw;\n\t\t\tconst amount = Number.parseInt(rel[1] as string, 10);\n\t\t\tconst unit = rel[2] as 's' | 'm' | 'h' | 'd';\n\t\t\tconst ms =\n\t\t\t\tunit === 's'\n\t\t\t\t\t? amount * 1000\n\t\t\t\t\t: unit === 'm'\n\t\t\t\t\t\t? amount * 60_000\n\t\t\t\t\t\t: unit === 'h'\n\t\t\t\t\t\t\t? amount * 3_600_000\n\t\t\t\t\t\t\t: amount * 86_400_000;\n\t\t\treturn new Date(Date.now() - ms).toISOString();\n\t\t};\n\t\tif (args.from) opts.from = resolveRel(args.from);\n\t\tif (args.to) opts.to = resolveRel(args.to);\n\n\t\tconst response = await ctx.hoststack.services.getMetricsHistory(\n\t\t\tteamId,\n\t\t\targs.service_id,\n\t\t\topts,\n\t\t);\n\t\tconst points = Array.isArray(response.history) ? response.history.length : 0;\n\t\treturn respond({\n\t\t\tsummary: `Returned ${points} metric point${points === 1 ? '' : 's'} for service ${args.service_id}.`,\n\t\t\tdata: { history: response.history },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'update_service',\n\tcategory: 'services',\n\tdescription: [\n\t\t'Rename a service. Only the human-readable name changes — the publicId is permanent.',\n\t\t'',\n\t\t'When to use: the user wants to relabel a service in the dashboard. For deeper config edits (build command, branch, scale, plan), use update_service_config.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t' - name: new name (1–60 chars).',\n\t\t'',\n\t\t'Returns: { service: Service }.',\n\t\t'',\n\t\t'Example: update_service({ service_id: \"svc_abc\", name: \"api-prod\" }) → { service: { name: \"api-prod\", … } }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t\tname: z.string().min(1).max(60).describe('New service name (1–60 chars).'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.services.update(teamId, args.service_id, {\n\t\t\tname: args.name,\n\t\t});\n\t\tconst data = { service: shapeService(response.service) };\n\t\treturn respond({ summary: `Renamed service ${args.service_id} to \"${args.name}\".`, data });\n\t},\n});\n\ndefineTool({\n\tname: 'update_service_config',\n\tcategory: 'services',\n\tdescription: [\n\t\t'Update build/runtime configuration for a service. All fields optional — pass only what you want to change.',\n\t\t'',\n\t\t'When to use: the user wants to tweak how a service builds, runs, scales, or health-checks. Build/runtime fields (branch, install/build/start command, root, dockerfile) take effect on the next deploy — call trigger_deploy after if you need them applied immediately. Instance_count, resource and health-check changes rescale or rewire without a redeploy.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t' - install_command, build_command, start_command (optional): shell commands. Pass null to clear.',\n\t\t' - branch (optional): git branch to track.',\n\t\t' - root_directory (optional): build context root inside the repo.',\n\t\t' - dockerfile_path (optional): path to Dockerfile relative to root_directory. Pass null to clear.',\n\t\t' - auto_deploy (optional): boolean — auto-deploy on git push.',\n\t\t' - health_check_path (optional): HTTP path the platform GETs to verify liveness (e.g. \"/health\"). Pass null for TCP-only check.',\n\t\t' - health_check_enabled (optional): boolean — toggle health checking on/off.',\n\t\t' - allow_search_indexing (optional): boolean — let search engines index the free *.hoststack.dev platform URL. Off by default; the platform URL is served with X-Robots-Tag: noindex so a site does not rank on a hostname it does not own. Custom domains are always indexable and unaffected. Applies on the next deploy.',\n\t\t' - health_check_interval (optional): integer 5–300 seconds — how often the check runs.',\n\t\t' - health_check_timeout (optional): integer 1–60 seconds — single-attempt timeout.',\n\t\t' - health_check_grace_period_sec (optional): integer 1–1800 seconds — startup tolerance before failures count. RAISE THIS (e.g. 180) when the agent reports \"Health check timed out\" on a cold-boot app (Bun + Vite SSR typically need 90–180s).',\n\t\t' - memory_mb (optional): integer 128–16384 — container memory cap.',\n\t\t' - cpu_shares (optional): integer 128–4096 — relative CPU weight.',\n\t\t' - disk_size_gb (optional): integer 1–100 — ephemeral disk cap.',\n\t\t' - port (optional): integer 1–65535 — container port the platform forwards traffic to.',\n\t\t' - protocol (optional): \"http\" | \"tcp\".',\n\t\t' - restart_policy (optional): \"always\" | \"on-failure\" | \"no\".',\n\t\t' - deploy_strategy (optional): \"rolling\" (default) | \"recreate\". SET THIS TO \"recreate\" for any single-container service that holds an exclusive lock on a mounted volume — a database running inside the container, an embedded queue, anything with a lockfile or a fixed host port on shared storage. Under the default rolling strategy those services CANNOT deploy at all: the new container can\\'t open the datadir the outgoing container still holds, so it exits; because it never goes healthy the old one is never stopped; because the old one is never stopped the lock is never released. The deploy deadlocks for the whole grace period and then reports \"Health check timed out\". \"recreate\" stops the old container first, at the cost of a brief outage.',\n\t\t' - pre_deploy_command (optional): shell command run before the new release accepts traffic (typical use: migrations).',\n\t\t' - instance_count (optional): integer 1–50 — pin both min and max instances to this value.',\n\t\t' - min_instances, max_instances (optional): integers — autoscale bounds. Use instead of instance_count when you want a range.',\n\t\t' - scale_cpu_threshold, scale_memory_threshold (optional): integer 10–100 — autoscale trigger percentage.',\n\t\t' - scale_requests_per_sec_threshold (optional): integer 1–10000 — autoscale on request rate instead of (or alongside) CPU and memory. Pass null to stop scaling on request rate.',\n\t\t' - build_timeout_seconds (optional): integer 60–1800 — how long a build may run before it is killed. Raise it for a service whose build legitimately takes longer than the default; pass null to go back to the default.',\n\t\t\" - plan (optional): move the service to a different size tier. This is the supported way to grow past the current tier's ceiling — memory_mb and cpu_shares are CLAMPED to the tier, so raising them alone does nothing once you are at the top of it. Memory and CPU apply to the running container where possible; disk grows on the next recreate. Changing tier changes what the service costs.\",\n\t\t' - docker_image (optional): repoint an image-backed service at a different image. Takes effect on the next deploy — this writes the reference, it does not pull. Pass null to clear it.',\n\t\t' - cron_schedule (optional): the schedule for a cron_job service. Pass null to clear it.',\n\t\t' - publish_path (optional): the directory a static_site publishes (e.g. \"dist\"). Pass null to clear it.',\n\t\t' - runtime (optional): runtime hint (\"node\" | \"bun\" | \"python\" | …), normally auto-detected from the repo. Set it when the detection is wrong.',\n\t\t' - log_filter_rules (optional): list of { pattern, action } rules applied at read time — to get_service_logs (tail AND count) and to the dashboard\\'s live stream alike. Pattern matches the message by case-insensitive substring, literally (a % or _ is not a wildcard); action is \"drop\" (filter out) or \"downgrade\" (flip stderr → stdout so it stops looking like an error). Stored lines are never altered, so removing a rule re-exposes everything it was hiding. Pass [] to clear all rules. Capped at 50 rules.',\n\t\t'',\n\t\t'Returns: { service?: Service, config?: ServiceConfig } — whichever rows were touched.',\n\t\t'',\n\t\t'Example: update_service_config({ service_id: \"svc_abc\", health_check_grace_period_sec: 180 }) → { config: { healthCheckGracePeriodSec: 180, … } }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t\tinstall_command: z\n\t\t\t.string()\n\t\t\t.nullable()\n\t\t\t.optional()\n\t\t\t.describe('Install shell command. Null clears.'),\n\t\tbuild_command: z\n\t\t\t.string()\n\t\t\t.nullable()\n\t\t\t.optional()\n\t\t\t.describe('Build shell command. Null clears.'),\n\t\tstart_command: z\n\t\t\t.string()\n\t\t\t.nullable()\n\t\t\t.optional()\n\t\t\t.describe('Start shell command. Null clears.'),\n\t\tbranch: z.string().optional().describe('Git branch to track.'),\n\t\troot_directory: z.string().optional().describe('Build context root.'),\n\t\tdockerfile_path: z\n\t\t\t.string()\n\t\t\t.nullable()\n\t\t\t.optional()\n\t\t\t.describe('Path to Dockerfile relative to root. Null clears.'),\n\t\tauto_deploy: z.boolean().optional().describe('Auto-deploy on push.'),\n\t\thealth_check_path: z\n\t\t\t.string()\n\t\t\t.nullable()\n\t\t\t.optional()\n\t\t\t.describe('HTTP health-check path (e.g. \"/health\"). Null = TCP-only check.'),\n\t\thealth_check_enabled: z.boolean().optional().describe('Toggle health checking on/off.'),\n\t\tallow_search_indexing: z\n\t\t\t.boolean()\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t'Let search engines index the free *.hoststack.dev platform URL (off by default). Custom domains are always indexable. Applies on the next deploy.',\n\t\t\t),\n\t\thealth_check_interval: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(5)\n\t\t\t.max(300)\n\t\t\t.optional()\n\t\t\t.describe('How often the check runs, in seconds (5–300).'),\n\t\thealth_check_timeout: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(1)\n\t\t\t.max(60)\n\t\t\t.optional()\n\t\t\t.describe('Single-attempt timeout in seconds (1–60).'),\n\t\thealth_check_grace_period_sec: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(1)\n\t\t\t.max(1800)\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t'Startup grace period in seconds (1–1800). Raise this if the app needs more time to boot before health checks start counting failures.',\n\t\t\t),\n\t\tmemory_mb: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(128)\n\t\t\t.max(16384)\n\t\t\t.optional()\n\t\t\t.describe('Container memory cap in MB (128–16384).'),\n\t\tcpu_shares: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(128)\n\t\t\t.max(4096)\n\t\t\t.optional()\n\t\t\t.describe('Relative CPU weight (128–4096).'),\n\t\tdisk_size_gb: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(1)\n\t\t\t.max(100)\n\t\t\t.optional()\n\t\t\t.describe('Ephemeral disk size in GB (1–100).'),\n\t\tport: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(1)\n\t\t\t.max(65535)\n\t\t\t.optional()\n\t\t\t.describe('Container port the platform forwards traffic to.'),\n\t\tprotocol: z.enum(['http', 'tcp']).optional().describe('Traffic protocol.'),\n\t\trestart_policy: z\n\t\t\t.enum(['always', 'on-failure', 'no'])\n\t\t\t.optional()\n\t\t\t.describe('Docker restart policy.'),\n\t\tdeploy_strategy: z\n\t\t\t.enum(['rolling', 'recreate'])\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t'How a deploy replaces the container. \"rolling\" (default) = start new, wait for healthy, switch traffic, stop old (zero downtime). \"recreate\" = stop old first, then start new (brief outage) — required for a container holding an exclusive lock on a mounted volume, which cannot deploy at all under rolling.',\n\t\t\t),\n\t\tpre_deploy_command: z\n\t\t\t.string()\n\t\t\t.optional()\n\t\t\t.describe('Shell command run before the new release accepts traffic.'),\n\t\tinstance_count: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.positive()\n\t\t\t.max(50)\n\t\t\t.optional()\n\t\t\t.describe('Pin min and max instances to this value (1–50).'),\n\t\tmin_instances: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(0)\n\t\t\t.max(50)\n\t\t\t.optional()\n\t\t\t.describe('Autoscale lower bound. Use with max_instances for a range.'),\n\t\tmax_instances: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(1)\n\t\t\t.max(50)\n\t\t\t.optional()\n\t\t\t.describe('Autoscale upper bound. Use with min_instances for a range.'),\n\t\tscale_cpu_threshold: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(10)\n\t\t\t.max(100)\n\t\t\t.optional()\n\t\t\t.describe('Autoscale CPU trigger percentage (10–100).'),\n\t\tscale_memory_threshold: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(10)\n\t\t\t.max(100)\n\t\t\t.optional()\n\t\t\t.describe('Autoscale memory trigger percentage (10–100).'),\n\t\tscale_requests_per_sec_threshold: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(1)\n\t\t\t.max(10000)\n\t\t\t.nullable()\n\t\t\t.optional()\n\t\t\t.describe('Autoscale request-rate trigger (1–10000 req/s). null stops using it.'),\n\t\tbuild_timeout_seconds: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(60)\n\t\t\t.max(1800)\n\t\t\t.nullable()\n\t\t\t.optional()\n\t\t\t.describe('Kill a build that runs longer than this (60–1800s). null = default.'),\n\t\tplan: z\n\t\t\t.enum(SERVICE_PLANS)\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t'Move to a different size tier. memory_mb and cpu_shares are clamped to the tier, so this is what raises the ceiling. Changes cost.',\n\t\t\t),\n\t\tdocker_image: z\n\t\t\t.string()\n\t\t\t.max(500)\n\t\t\t.nullable()\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t'Repoint an image-backed service. Applies on the next deploy. null clears it.',\n\t\t\t),\n\t\tcron_schedule: z\n\t\t\t.string()\n\t\t\t.max(100)\n\t\t\t.nullable()\n\t\t\t.optional()\n\t\t\t.describe('Cron expression for a cron_job service. null clears it.'),\n\t\tpublish_path: z\n\t\t\t.string()\n\t\t\t.max(500)\n\t\t\t.nullable()\n\t\t\t.optional()\n\t\t\t.describe('Static-site output directory, e.g. \"dist\". null clears it.'),\n\t\truntime: z\n\t\t\t.string()\n\t\t\t.max(50)\n\t\t\t.optional()\n\t\t\t.describe('Runtime hint (node/bun/python/…). Normally auto-detected.'),\n\t\tlog_filter_rules: z\n\t\t\t.array(\n\t\t\t\tz.object({\n\t\t\t\t\tpattern: z.string().min(1).max(200),\n\t\t\t\t\taction: z.enum(['drop', 'downgrade']),\n\t\t\t\t}),\n\t\t\t)\n\t\t\t.max(50)\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t'Runtime-log filter rules. Empty array [] clears all rules. Each pattern is case-insensitive substring match against the message.',\n\t\t\t),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\n\t\t// services-row fields (PATCH /services/:tid/:sid). Note: nullable\n\t\t// fields take `null` to clear, distinct from `undefined` which means\n\t\t// \"leave unchanged\" — passing `null` through to the underlying API\n\t\t// is what flips the column to NULL on the row.\n\t\tconst serviceUpdate: Record<string, unknown> = {};\n\t\tif (args.install_command !== undefined)\n\t\t\tserviceUpdate['installCommand'] = args.install_command;\n\t\tif (args.build_command !== undefined) serviceUpdate['buildCommand'] = args.build_command;\n\t\tif (args.start_command !== undefined) serviceUpdate['startCommand'] = args.start_command;\n\t\tif (args.dockerfile_path !== undefined)\n\t\t\tserviceUpdate['dockerfilePath'] = args.dockerfile_path;\n\t\tif (args.branch !== undefined) serviceUpdate['branch'] = args.branch;\n\t\tif (args.root_directory !== undefined) serviceUpdate['rootDirectory'] = args.root_directory;\n\t\tif (args.auto_deploy !== undefined) serviceUpdate['autoDeploy'] = args.auto_deploy;\n\t\tif (args.health_check_path !== undefined)\n\t\t\tserviceUpdate['healthCheckPath'] = args.health_check_path;\n\t\tif (args.plan !== undefined) serviceUpdate['plan'] = args.plan;\n\t\tif (args.cron_schedule !== undefined) serviceUpdate['cronSchedule'] = args.cron_schedule;\n\t\tif (args.publish_path !== undefined) serviceUpdate['publishPath'] = args.publish_path;\n\t\tif (args.runtime !== undefined) serviceUpdate['runtime'] = args.runtime;\n\n\t\t// service_config-row fields (PATCH /services/:tid/:sid/config). The\n\t\t// schema expects min_instances/max_instances; \"pin to N\" means setting\n\t\t// both to the same value.\n\t\tconst configUpdate: Record<string, unknown> = {};\n\t\tif (args.health_check_enabled !== undefined)\n\t\t\tconfigUpdate['healthCheckEnabled'] = args.health_check_enabled;\n\t\tif (args.allow_search_indexing !== undefined)\n\t\t\tconfigUpdate['allowSearchIndexing'] = args.allow_search_indexing;\n\t\tif (args.health_check_interval !== undefined)\n\t\t\tconfigUpdate['healthCheckInterval'] = args.health_check_interval;\n\t\tif (args.health_check_timeout !== undefined)\n\t\t\tconfigUpdate['healthCheckTimeout'] = args.health_check_timeout;\n\t\tif (args.health_check_grace_period_sec !== undefined)\n\t\t\tconfigUpdate['healthCheckGracePeriodSec'] = args.health_check_grace_period_sec;\n\t\tif (args.memory_mb !== undefined) configUpdate['memoryMb'] = args.memory_mb;\n\t\tif (args.cpu_shares !== undefined) configUpdate['cpuShares'] = args.cpu_shares;\n\t\tif (args.disk_size_gb !== undefined) configUpdate['diskSizeGb'] = args.disk_size_gb;\n\t\tif (args.port !== undefined) configUpdate['port'] = args.port;\n\t\tif (args.protocol !== undefined) configUpdate['protocol'] = args.protocol;\n\t\tif (args.restart_policy !== undefined) configUpdate['restartPolicy'] = args.restart_policy;\n\t\tif (args.deploy_strategy !== undefined)\n\t\t\tconfigUpdate['deployStrategy'] = args.deploy_strategy;\n\t\tif (args.pre_deploy_command !== undefined)\n\t\t\tconfigUpdate['preDeployCommand'] = args.pre_deploy_command;\n\t\tif (args.instance_count !== undefined) {\n\t\t\tconfigUpdate['minInstances'] = args.instance_count;\n\t\t\tconfigUpdate['maxInstances'] = args.instance_count;\n\t\t}\n\t\tif (args.min_instances !== undefined) configUpdate['minInstances'] = args.min_instances;\n\t\tif (args.max_instances !== undefined) configUpdate['maxInstances'] = args.max_instances;\n\t\tif (args.scale_cpu_threshold !== undefined)\n\t\t\tconfigUpdate['scaleCpuThreshold'] = args.scale_cpu_threshold;\n\t\tif (args.scale_memory_threshold !== undefined)\n\t\t\tconfigUpdate['scaleMemoryThreshold'] = args.scale_memory_threshold;\n\t\t// null is a meaningful value on both of these — it clears the setting\n\t\t// rather than leaving it alone — so test against undefined, not falsiness.\n\t\tif (args.scale_requests_per_sec_threshold !== undefined)\n\t\t\tconfigUpdate['scaleRequestsPerSecThreshold'] = args.scale_requests_per_sec_threshold;\n\t\tif (args.build_timeout_seconds !== undefined)\n\t\t\tconfigUpdate['buildTimeoutSeconds'] = args.build_timeout_seconds;\n\t\t// docker_image lives on the CONFIG row, not the service row, unlike the\n\t\t// other five added alongside it.\n\t\tif (args.docker_image !== undefined) configUpdate['dockerImage'] = args.docker_image;\n\t\tif (args.log_filter_rules !== undefined) {\n\t\t\t// Empty array is a meaningful value (clears all rules);\n\t\t\t// undefined leaves rules unchanged. Don't short-circuit.\n\t\t\tconfigUpdate['logFilterRules'] = args.log_filter_rules;\n\t\t}\n\n\t\tif (Object.keys(serviceUpdate).length === 0 && Object.keys(configUpdate).length === 0) {\n\t\t\treturn respond({ summary: 'No fields to update.', data: {} });\n\t\t}\n\n\t\tconst data: Record<string, unknown> = {};\n\t\tconst touchedFields: string[] = [];\n\t\tif (Object.keys(serviceUpdate).length > 0) {\n\t\t\tconst serviceResponse = await ctx.hoststack.services.update(\n\t\t\t\tteamId,\n\t\t\t\targs.service_id,\n\t\t\t\tserviceUpdate,\n\t\t\t);\n\t\t\tdata['service'] = shape(serviceResponse.service);\n\t\t\ttouchedFields.push(...Object.keys(serviceUpdate));\n\t\t}\n\t\tif (Object.keys(configUpdate).length > 0) {\n\t\t\tconst configResponse = await ctx.hoststack.services.updateConfig(\n\t\t\t\tteamId,\n\t\t\t\targs.service_id,\n\t\t\t\tconfigUpdate,\n\t\t\t);\n\t\t\tdata['config'] = shape(configResponse.config);\n\t\t\ttouchedFields.push(...Object.keys(configUpdate));\n\t\t}\n\n\t\treturn respond({\n\t\t\tsummary: `Updated ${touchedFields.join(', ')} on service ${args.service_id}.`,\n\t\t\tdata,\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'suspend_service',\n\tcategory: 'services',\n\tdescription: [\n\t\t'Suspend a service: stop all running instances and pause auto-deploys. The service stays configured and can be resumed later with resume_service.',\n\t\t'',\n\t\t'When to use: the user wants to temporarily stop traffic and billing without deleting the service (for example: dev environment overnight, debugging).',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t'',\n\t\t'Returns: { ok: true }.',\n\t\t'',\n\t\t'Example: suspend_service({ service_id: \"svc_dev\" }) → { ok: true }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tawait ctx.hoststack.services.suspend(teamId, args.service_id);\n\t\treturn respond({ summary: `Suspended service ${args.service_id}.`, data: { ok: true } });\n\t},\n});\n\ndefineTool({\n\tname: 'resume_service',\n\tcategory: 'services',\n\tdescription: [\n\t\t'Resume a previously suspended service: start instances back up and re-enable auto-deploys.',\n\t\t'',\n\t\t'When to use: the user wants to bring a suspended service back online. Pair with suspend_service.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t'',\n\t\t'Returns: { ok: true }.',\n\t\t'',\n\t\t'Example: resume_service({ service_id: \"svc_dev\" }) → { ok: true }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tawait ctx.hoststack.services.resume(teamId, args.service_id);\n\t\treturn respond({ summary: `Resumed service ${args.service_id}.`, data: { ok: true } });\n\t},\n});\n\ndefineTool({\n\tname: 'delete_service',\n\tcategory: 'services',\n\tdescription: [\n\t\t'Permanently delete a service: stops and removes its containers, releases its hostname and routes, and cascade-deletes its attached volumes, env vars, domains and deploy history. Irreversible.',\n\t\t'',\n\t\t'When to use: the user explicitly asks to delete or remove a service, or you are cleaning up a service that was created by mistake or is no longer needed. Without this, a broken or abandoned service could only be removed from the dashboard — so a service that failed to come up had to be left in place and worked around by creating another one.',\n\t\t'',\n\t\t'ALWAYS confirm with the user before calling this on anything that has served traffic. To stop a service temporarily without losing it, use suspend_service instead. To tear down an agentic Dev Box (which also cascades its cloned database), use delete_dev_environment.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service to delete.',\n\t\t'',\n\t\t'Returns: { ok: true }.',\n\t\t'',\n\t\t'Example: delete_service({ service_id: \"svc_abandoned\" }) → { ok: true }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tawait ctx.hoststack.services.delete(teamId, args.service_id);\n\t\treturn respond({\n\t\t\tsummary: `Deleted service ${args.service_id} and its attached resources. This cannot be undone.`,\n\t\t\tdata: { ok: true },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'get_service_logs',\n\tcategory: 'logs',\n\tdescription: [\n\t\t\"Fetch a snapshot of the running container's recent runtime logs (stdout/stderr). For *deploy* logs (build output), use get_deploy_logs instead.\",\n\t\t'',\n\t\t'When to use: a service is misbehaving in production and you need to read what it is currently logging. The tool returns a snapshot — there is no streaming over MCP. Re-call to get newer entries.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t' - lines (optional): tail size (default 200, max 1000).',\n\t\t' - since/until (optional): ISO-8601 timestamp OR a relative offset like \"-5m\", \"-1h\", \"-2d\".',\n\t\t' - stream (optional): \"stdout\" | \"stderr\". Omit to combine.',\n\t\t' - level (optional): real log level — trace/debug/info/warn/error/fatal. For structured JSON logs (pino, bunyan, OpenTelemetry severity) this filters on the parsed inner level field. For plain text logs it falls back to a stream-alias hint (info/debug → stdout, warn/error/fatal → stderr).',\n\t\t' - search (optional): case-insensitive substring grep, ≤100 chars.',\n\t\t' - count_only (optional): when true, returns { count } only — much cheaper for \"how many error lines in last 5m\" polling. It counts exactly what the same call would return, so `count` and the entry count agree whenever count <= lines.',\n\t\t'',\n\t\t\"If the service has log_filter_rules with action='drop' (see update_service_config), the lines they match are excluded from BOTH the tail and the count — so a count lower than the raw log volume is the rules working, not lines going missing. `stream` selects on the stream shown in the response, which a 'downgrade' rule may have flipped from stderr to stdout.\",\n\t\t'',\n\t\t'Returns: { logs: LogEntry[] | string } when count_only is false. Each entry has { timestamp, level?, stream, message }. `level` is the parsed inner level when the message is a structured JSON envelope (pino numeric or string), and undefined for plain-text logs. `stream` is always one of stdout/stderr. Or { count: number } when count_only is true.',\n\t\t'',\n\t\t'Example: get_service_logs({ service_id: \"svc_abc\", search: \"OOM\", since: \"-15m\" }) → { logs: [...] }.',\n\t\t'',\n\t\t'More examples:',\n\t\t' - Last 50 stderr lines from the past hour: get_service_logs({ service_id: \"svc_abc\", lines: 50, stream: \"stderr\", since: \"-1h\" })',\n\t\t' - Just count error lines without fetching them: get_service_logs({ service_id: \"svc_abc\", level: \"error\", since: \"-5m\", count_only: true }) → { count: 47 }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t\tlines: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.positive()\n\t\t\t.max(1000)\n\t\t\t.optional()\n\t\t\t.describe('Tail size; default 200, hard cap 1000.'),\n\t\tsince: z\n\t\t\t.string()\n\t\t\t.optional()\n\t\t\t.describe('ISO-8601 timestamp or relative offset (e.g. \"-5m\", \"-1h\").'),\n\t\tuntil: z.string().optional().describe('ISO-8601 timestamp or relative offset upper bound.'),\n\t\tstream: z.enum(['stdout', 'stderr']).optional().describe('Restrict to one stream.'),\n\t\tlevel: z\n\t\t\t.enum(['stdout', 'stderr', 'trace', 'debug', 'info', 'warn', 'error', 'fatal'])\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t'Filter by structured JSON log level (pino/bunyan/severity). Falls back to a stream-alias hint for plain-text logs (info/debug→stdout, warn/error/fatal→stderr).',\n\t\t\t),\n\t\tsearch: z.string().max(100).optional().describe('Case-insensitive substring filter.'),\n\t\tcount_only: z\n\t\t\t.boolean()\n\t\t\t.optional()\n\t\t\t.describe('When true, return only { count } — skips the log payload.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst opts: {\n\t\t\tlines?: number;\n\t\t\tsince?: string;\n\t\t\tuntil?: string;\n\t\t\tstream?: 'stdout' | 'stderr';\n\t\t\tlevel?: 'stdout' | 'stderr' | 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal';\n\t\t\tsearch?: string;\n\t\t\tcountOnly?: boolean;\n\t\t} = {\n\t\t\tlines: args.lines ?? 200,\n\t\t};\n\t\tif (args.since) opts.since = args.since;\n\t\tif (args.until) opts.until = args.until;\n\t\tif (args.stream) opts.stream = args.stream;\n\t\tif (args.level) opts.level = args.level;\n\t\tif (args.search) opts.search = args.search;\n\t\tif (args.count_only) opts.countOnly = args.count_only;\n\n\t\tconst response = await ctx.hoststack.services.getRuntimeLogs(teamId, args.service_id, opts);\n\t\tif ('count' in response) {\n\t\t\treturn respond({\n\t\t\t\tsummary: `${response.count} matching log line${response.count === 1 ? '' : 's'} for service ${args.service_id}.`,\n\t\t\t\tdata: { count: response.count },\n\t\t\t});\n\t\t}\n\t\tconst count = Array.isArray(response.logs)\n\t\t\t? response.logs.length\n\t\t\t: typeof response.logs === 'string'\n\t\t\t\t? response.logs.split('\\n').length\n\t\t\t\t: 0;\n\t\treturn respond({\n\t\t\tsummary: `Fetched ${count} log line${count === 1 ? '' : 's'} for service ${args.service_id}.`,\n\t\t\tdata: { logs: response.logs },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'get_service_logs_bulk',\n\tcategory: 'logs',\n\tdescription: [\n\t\t'Fetch runtime logs for multiple services in parallel and return one response keyed by service_id. Single round-trip from the MCP client perspective.',\n\t\t'',\n\t\t'When to use: investigating something that spans a project — \"what does every service in the auth-fleet say about the OOM at 14:00?\", \"did anything log an error in the last 5 minutes across these 4 services?\". Pair with list_services (filter by project/status) to get the service_ids list first.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_ids: list of service publicIds (svc_…). Hard cap 10 to bound work — for larger fleets, partition the call.',\n\t\t' - lines_per_service (optional): tail size per service (default 100, max 500). Smaller default than get_service_logs since you are fanning out.',\n\t\t' - since/until (optional): same shape as get_service_logs (ISO-8601 or \"-5m\", \"-1h\").',\n\t\t' - stream (optional): \"stdout\" | \"stderr\".',\n\t\t' - level (optional): structured-log level filter (trace/debug/info/warn/error/fatal).',\n\t\t' - search (optional): case-insensitive substring filter applied to every service.',\n\t\t' - count_only (optional): when true, return { results: { [service_id]: { count } } } — much cheaper for \"how many error lines per service in the last 5m\" surveys.',\n\t\t'',\n\t\t'Returns: { results: { [service_id]: { logs?, count?, error? } } } — per-service result, with `error` populated when one service failed (the rest still succeed).',\n\t\t'',\n\t\t'Example: get_service_logs_bulk({ service_ids: [\"svc_api\", \"svc_worker\"], level: \"error\", since: \"-15m\", count_only: true }) → { results: { svc_api: { count: 0 }, svc_worker: { count: 12 } } }.',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_ids: z\n\t\t\t.array(z.string())\n\t\t\t.min(1)\n\t\t\t.max(10)\n\t\t\t.describe('Service publicIds (1–10). Hard cap 10 to bound parallel work.'),\n\t\tlines_per_service: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.positive()\n\t\t\t.max(500)\n\t\t\t.optional()\n\t\t\t.describe('Tail size per service; default 100, hard cap 500.'),\n\t\tsince: z\n\t\t\t.string()\n\t\t\t.optional()\n\t\t\t.describe('ISO-8601 timestamp or relative offset (e.g. \"-5m\", \"-1h\").'),\n\t\tuntil: z.string().optional().describe('ISO-8601 timestamp or relative offset upper bound.'),\n\t\tstream: z.enum(['stdout', 'stderr']).optional().describe('Restrict to one stream.'),\n\t\tlevel: z\n\t\t\t.enum(['stdout', 'stderr', 'trace', 'debug', 'info', 'warn', 'error', 'fatal'])\n\t\t\t.optional()\n\t\t\t.describe('Structured log level filter (same as get_service_logs).'),\n\t\tsearch: z.string().max(100).optional().describe('Case-insensitive substring filter.'),\n\t\tcount_only: z\n\t\t\t.boolean()\n\t\t\t.optional()\n\t\t\t.describe('When true, return only counts per service — skips the log payload.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst linesPerService = args.lines_per_service ?? 100;\n\n\t\t// Build a single options object reused across all fan-outs so we\n\t\t// don't reconstruct identical params in a loop.\n\t\tconst baseOpts: {\n\t\t\tlines: number;\n\t\t\tsince?: string;\n\t\t\tuntil?: string;\n\t\t\tstream?: 'stdout' | 'stderr';\n\t\t\tlevel?: 'stdout' | 'stderr' | 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal';\n\t\t\tsearch?: string;\n\t\t\tcountOnly?: boolean;\n\t\t} = { lines: linesPerService };\n\t\tif (args.since) baseOpts.since = args.since;\n\t\tif (args.until) baseOpts.until = args.until;\n\t\tif (args.stream) baseOpts.stream = args.stream;\n\t\tif (args.level) baseOpts.level = args.level;\n\t\tif (args.search) baseOpts.search = args.search;\n\t\tif (args.count_only) baseOpts.countOnly = args.count_only;\n\n\t\t// Promise.allSettled so one failing service (e.g. deleted between\n\t\t// list_services and this call) doesn't void the rest.\n\t\tconst settled = await Promise.allSettled(\n\t\t\targs.service_ids.map(async (sid) => {\n\t\t\t\tconst response = await ctx.hoststack.services.getRuntimeLogs(teamId, sid, baseOpts);\n\t\t\t\treturn { sid, response };\n\t\t\t}),\n\t\t);\n\n\t\tconst results: Record<string, unknown> = {};\n\t\tlet okCount = 0;\n\t\tlet errorCount = 0;\n\t\tfor (let i = 0; i < settled.length; i += 1) {\n\t\t\tconst outcome = settled[i] as PromiseSettledResult<{\n\t\t\t\tsid: string;\n\t\t\t\tresponse: { logs?: unknown; count?: number };\n\t\t\t}>;\n\t\t\tconst sid = args.service_ids[i] as string;\n\t\t\tif (outcome.status === 'fulfilled') {\n\t\t\t\tconst { response } = outcome.value;\n\t\t\t\tif ('count' in response) {\n\t\t\t\t\tresults[sid] = { count: response.count };\n\t\t\t\t} else {\n\t\t\t\t\tresults[sid] = { logs: response.logs };\n\t\t\t\t}\n\t\t\t\tokCount += 1;\n\t\t\t} else {\n\t\t\t\tconst reason = outcome.reason as { message?: string } | string;\n\t\t\t\tresults[sid] = {\n\t\t\t\t\terror:\n\t\t\t\t\t\ttypeof reason === 'string' ? reason : (reason?.message ?? 'Unknown error'),\n\t\t\t\t};\n\t\t\t\terrorCount += 1;\n\t\t\t}\n\t\t}\n\n\t\tconst summary =\n\t\t\terrorCount === 0\n\t\t\t\t? `Fetched logs from ${okCount} service${okCount === 1 ? '' : 's'}.`\n\t\t\t\t: `Fetched logs from ${okCount} of ${args.service_ids.length} service${args.service_ids.length === 1 ? '' : 's'} (${errorCount} failed — see per-service .error field).`;\n\t\treturn respond({ summary, data: { results } });\n\t},\n});\n","/**\n * Quickstart templates surfaced by the `list_templates` tool.\n *\n * MIRROR of packages/shared/src/templates.ts (`APP_TEMPLATES`). It is copied,\n * not imported, for the same reason DEV_ENV_IMAGE is copied in\n * tools/services.ts: this package publishes to npm and depends only on\n * @hoststack.dev/sdk, so importing the private @hoststack/shared workspace\n * package would drag the monorepo into the published bundle. Update both\n * sides together — drift is now caught by test/shared-parity.test.ts, which\n * imports shared at DEV time only (a test file is never an entry point, so\n * nothing from shared reaches the bundle) and fails when the ids, their order\n * or any mirrored field disagree. \"Caught by review\" is what this said while\n * five templates went missing for a week, so it no longer says it.\n *\n * What is below is exactly what the dashboard's New Service wizard fills in,\n * so an agent that passes it to `create_service` gets a service configured\n * identically to a hand-created one.\n *\n * The rule for which fields come across is \"what a caller has to SEND\", not\n * \"what the template is\". `category` is not mirrored: it only drives the\n * wizard's tab grouping. `type` is, because it is a required `create_service`\n * argument. `dockerImage` and `port` are, because the API takes those two off\n * the wire even for a template — the wizard sends them alongside `templateId`\n * and so must an agent. Everything else an image template declares — its\n * volumes, scratch dirs, `runAsUser`, generated secrets, companion managed\n * database, health-check grace period — is resolved server-side from\n * `templateId` and cannot be sent at all, so copying it here would only\n * describe a payload no caller is allowed to write.\n *\n * The catalog used to carry three OTHER image templates — `docker-nginx`,\n * `docker-meilisearch`, `docker-minio` — and all three were removed after\n * being run under the real service sandbox flags: they exit on the read-only\n * rootfs before ever binding a port. See rule 5 in\n * packages/shared/src/templates.ts for the captured output. Do not re-add\n * them here.\n *\n * The five image templates that DO boot spent a week out of this mirror on\n * purpose, and the condition they were waiting on is worth keeping in mind\n * before anything here is loosened: `create_service` had no `template_id`\n * argument, so an agent shown `wordpress` could only reach it as\n * `docker_image: 'wordpress:php8.3-apache'` — an image with no volume,\n * running as root under a read-only rootfs, i.e. exactly the crash-loop rule\n * 7 was written to prevent, now looking officially supported. They are listed\n * here only because that argument exists; an entry with a `dockerImage` and\n * no `template_id` on the other end is a dead end, not a shortcut.\n */\n\nexport interface McpAppTemplate {\n\tid: string;\n\tname: string;\n\tdescription: string;\n\t/** Maps to the `type` argument of create_service. */\n\ttype: 'web_service' | 'worker' | 'cron_job' | 'static_site' | 'private_service';\n\t/**\n\t * Maps to `runtime` — the builder image the agent picks. Source-built\n\t * templates only; an image template has nothing to build.\n\t */\n\truntime?: string;\n\tinstallCommand?: string;\n\tbuildCommand?: string;\n\tstartCommand?: string;\n\tpublishPath?: string;\n\tcronSchedule?: string;\n\t/**\n\t * Prebuilt image, on the templates that ship one. Its presence is what makes\n\t * an entry a one-click app rather than a command set: pass it to\n\t * `create_service` TOGETHER with `template_id`, never on its own.\n\t */\n\tdockerImage?: string;\n\t/**\n\t * Maps to `port` — what the platform publishes and health-checks. Only ever\n\t * set on a `dockerImage` template, whose listen port is a property of the\n\t * image and cannot be argued with; a source-built app binds the injected\n\t * `$PORT` instead. Dropping it on the way to `create_service` gets a healthy\n\t * container probed on 3000 while the process listens somewhere else.\n\t */\n\tport?: number;\n}\n\nexport const MCP_APP_TEMPLATES: McpAppTemplate[] = [\n\t{\n\t\tid: 'node-express',\n\t\tname: 'Node.js Express',\n\t\tdescription: 'HTTP server with Express.js',\n\t\ttype: 'web_service',\n\t\truntime: 'node',\n\t\tinstallCommand: 'npm install',\n\t\tstartCommand: 'node index.js',\n\t},\n\t{\n\t\tid: 'python-fastapi',\n\t\tname: 'Python FastAPI',\n\t\tdescription: 'Modern Python API framework',\n\t\ttype: 'web_service',\n\t\truntime: 'python',\n\t\tinstallCommand: 'pip install -r requirements.txt',\n\t\tstartCommand: 'uvicorn main:app --host 0.0.0.0 --port $PORT',\n\t},\n\t{\n\t\tid: 'go-api',\n\t\tname: 'Go HTTP Server',\n\t\tdescription: 'Lightweight Go web service',\n\t\ttype: 'web_service',\n\t\truntime: 'go',\n\t\tinstallCommand: 'go mod download',\n\t\tbuildCommand: 'go build -o server .',\n\t\tstartCommand: './server',\n\t},\n\t{\n\t\tid: 'bun-hono',\n\t\tname: 'Bun + Hono',\n\t\tdescription: 'Fast TypeScript API with Bun runtime',\n\t\ttype: 'web_service',\n\t\truntime: 'bun',\n\t\tinstallCommand: 'bun install',\n\t\tstartCommand: 'bun run src/index.ts',\n\t},\n\t{\n\t\tid: 'nextjs-ssr',\n\t\tname: 'Next.js',\n\t\tdescription: 'Server-rendered Next.js started with next start',\n\t\ttype: 'web_service',\n\t\truntime: 'node',\n\t\tinstallCommand: 'npm install',\n\t\tbuildCommand: 'npm run build',\n\t\tstartCommand: 'npm run start',\n\t},\n\t{\n\t\tid: 'react-router-ssr',\n\t\tname: 'React Router / Remix',\n\t\tdescription: 'Server build served by react-router-serve or remix-serve',\n\t\ttype: 'web_service',\n\t\truntime: 'node',\n\t\tinstallCommand: 'npm install',\n\t\tbuildCommand: 'npm run build',\n\t\tstartCommand: 'npm run start',\n\t},\n\t{\n\t\tid: 'sveltekit-node',\n\t\tname: 'SvelteKit',\n\t\tdescription: 'SvelteKit server build produced by adapter-node',\n\t\ttype: 'web_service',\n\t\truntime: 'node',\n\t\tinstallCommand: 'npm install',\n\t\tbuildCommand: 'npm run build',\n\t\tstartCommand: 'node build',\n\t},\n\t{\n\t\tid: 'nuxt',\n\t\tname: 'Nuxt',\n\t\tdescription: 'Nuxt server build running on the Nitro output',\n\t\ttype: 'web_service',\n\t\truntime: 'node',\n\t\tinstallCommand: 'npm install',\n\t\tbuildCommand: 'npm run build',\n\t\tstartCommand: 'node .output/server/index.mjs',\n\t},\n\t{\n\t\tid: 'django',\n\t\tname: 'Django',\n\t\tdescription: 'Django project served by Gunicorn',\n\t\ttype: 'web_service',\n\t\truntime: 'python',\n\t\tinstallCommand: 'pip install -r requirements.txt',\n\t\tbuildCommand: 'python manage.py collectstatic --noinput || true',\n\t\tstartCommand:\n\t\t\t'gunicorn --bind 0.0.0.0:$PORT --workers 2 $(ls */wsgi.py | head -n1 | cut -d/ -f1).wsgi:application',\n\t},\n\t{\n\t\tid: 'flask',\n\t\tname: 'Flask',\n\t\tdescription: 'Flask app served by Gunicorn',\n\t\ttype: 'web_service',\n\t\truntime: 'python',\n\t\tinstallCommand: 'pip install -r requirements.txt',\n\t\tstartCommand: 'gunicorn --bind 0.0.0.0:$PORT --workers 2 app:app',\n\t},\n\t{\n\t\tid: 'spring-boot',\n\t\tname: 'Spring Boot',\n\t\tdescription: 'Spring Boot fat JAR built with Maven or Gradle',\n\t\ttype: 'web_service',\n\t\truntime: 'java',\n\t\t// No install/build on purpose: the agent inspects the repo and picks\n\t\t// Maven vs Gradle with the matching toolchain image. Sending either one\n\t\t// here would break the other half of the split.\n\t\tstartCommand: 'java -jar /app/app.jar --server.port=$PORT',\n\t},\n\t{\n\t\tid: 'static-react',\n\t\tname: 'React SPA',\n\t\tdescription: 'Single-page React application',\n\t\ttype: 'static_site',\n\t\truntime: 'node',\n\t\tinstallCommand: 'npm install',\n\t\tbuildCommand: 'npm run build',\n\t\tpublishPath: './dist',\n\t},\n\t{\n\t\tid: 'static-nextjs',\n\t\tname: 'Next.js Static',\n\t\tdescription: 'Next.js with static export',\n\t\ttype: 'static_site',\n\t\truntime: 'node',\n\t\tinstallCommand: 'npm install',\n\t\tbuildCommand: 'npm run build',\n\t\tpublishPath: './out',\n\t},\n\t{\n\t\tid: 'astro',\n\t\tname: 'Astro',\n\t\tdescription: \"Astro site built to Astro's default static output\",\n\t\ttype: 'static_site',\n\t\truntime: 'node',\n\t\tinstallCommand: 'npm install',\n\t\tbuildCommand: 'npm run build',\n\t\tpublishPath: './dist',\n\t},\n\t{\n\t\tid: 'worker-bullmq',\n\t\tname: 'BullMQ Worker',\n\t\tdescription: 'Background job processor',\n\t\ttype: 'worker',\n\t\truntime: 'node',\n\t\tinstallCommand: 'npm install',\n\t\tstartCommand: 'node worker.js',\n\t},\n\t{\n\t\tid: 'uptime-kuma',\n\t\tname: 'Uptime Kuma',\n\t\tdescription: 'Self-hosted uptime monitoring and status pages, with its own SQLite store',\n\t\ttype: 'web_service',\n\t\tdockerImage: 'louislam/uptime-kuma:1',\n\t\tport: 3000,\n\t},\n\t{\n\t\tid: 'vaultwarden',\n\t\tname: 'Vaultwarden',\n\t\tdescription: 'Self-hosted Bitwarden-compatible password manager (unofficial server)',\n\t\ttype: 'web_service',\n\t\tdockerImage: 'vaultwarden/server:1.32.7-alpine',\n\t\tport: 3000,\n\t},\n\t{\n\t\tid: 'n8n',\n\t\tname: 'n8n',\n\t\tdescription: 'Self-hosted workflow automation — visual editor, 400+ integrations',\n\t\ttype: 'web_service',\n\t\tdockerImage: 'n8nio/n8n:1',\n\t\tport: 5678,\n\t},\n\t{\n\t\tid: 'wordpress',\n\t\tname: 'WordPress',\n\t\tdescription: 'PHP 8.3 + Apache, with a managed MySQL database (billed separately)',\n\t\ttype: 'web_service',\n\t\tdockerImage: 'wordpress:php8.3-apache',\n\t\tport: 80,\n\t},\n\t{\n\t\tid: 'ghost',\n\t\tname: 'Ghost',\n\t\tdescription: 'Blogs and newsletters, with a managed MySQL database (billed separately)',\n\t\ttype: 'web_service',\n\t\tdockerImage: 'ghost:5-alpine',\n\t\tport: 2368,\n\t},\n\t{\n\t\tid: 'cron-cleanup',\n\t\tname: 'Cleanup Cron',\n\t\tdescription: 'Daily maintenance task',\n\t\ttype: 'cron_job',\n\t\truntime: 'node',\n\t\tinstallCommand: 'npm install',\n\t\tstartCommand: 'node cleanup.js',\n\t\tcronSchedule: '0 3 * * *',\n\t},\n];\n","import { z } from 'zod';\n\nimport { respond } from '../lib/respond.js';\nimport { shape } from '../lib/shape.js';\nimport { defineTool } from '../registry.js';\n\ninterface CheckResponse {\n\tcheck: Record<string, unknown> | null;\n}\n\nconst STATUS_MEANING = [\n\t'Status vocabulary: `up` answering as expected · `down` failed the threshold and an alert is open · `unknown` not probed yet · `unresolvable` the service has no active domain to request · `paused` the service is not meant to be answering (suspended, mid-deploy, never deployed) OR it sleeps when idle and probing it would keep it awake.',\n].join('\\n');\n\ndefineTool({\n\tname: 'get_uptime_check',\n\tcategory: 'uptime',\n\tdescription: [\n\t\t\"Read a service's uptime check — HostStack requesting the service's public URL on a schedule and alerting when it stops answering.\",\n\t\t'',\n\t\t'When to use: \"is this service actually reachable\", or before changing a check to see what it currently does. This is NOT the deploy-time health check: that one watches the container from inside the host and stops mattering once a deploy is live. This one runs from the control plane against the public URL, so it also catches DNS, TLS, edge and routing failures — and a service that accepts the connection and then answers nothing.',\n\t\t'',\n\t\tSTATUS_MEANING,\n\t\t'',\n\t\t'Inputs: serviceId (required).',\n\t\t'',\n\t\t'Returns: { check } or { check: null } when none is configured. The check carries path, method, expectedStatus, intervalSeconds, failureThreshold, status, consecutiveFailures, lastCheckedAt, lastStatusCode, lastLatencyMs, lastError, lastChangedAt (the \"down since\" timestamp), and lastTargetUrl.',\n\t\t'',\n\t\t\"WHICH HOSTNAME IS PROBED: `lastTargetUrl` is the URL the last probe actually dialled — read it here rather than inferring it. A check names a PATH and the platform picks the host: the service's primary domain, or the OLDEST domain when no primary is nominated, which on a renamed host is usually the retired alias. So a service answering on two names that behave differently (canonical serves, alias 301s) can have a check that asserts the wrong status code without anything being wrong with the service. If lastTargetUrl is not the name you meant to watch, nominate the right one with update_domain({ domain_id, isPrimary: true }) — set_uptime_check takes a path and never a host.\",\n\t\t'',\n\t\t\"Example: get_uptime_check({ serviceId: 48 }) → { check: { path: '/healthz', status: 'down', consecutiveFailures: 5, lastError: 'No response within 10000ms', lastChangedAt: '2026-08-21T04:12:00Z', lastTargetUrl: 'https://grundfast.dk/healthz' } }.\",\n\t].join('\\n'),\n\tinput: { serviceId: z.number().int().positive() },\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.api.get<CheckResponse>(\n\t\t\t`/api/services/${teamId}/${args.serviceId}/uptime-check`,\n\t\t);\n\t\tif (response.check === null) {\n\t\t\treturn respond({\n\t\t\t\tsummary: 'No uptime check on this service — nothing is watching its public URL.',\n\t\t\t\tdata: { check: null },\n\t\t\t});\n\t\t}\n\t\tconst check = shape(response.check) as Record<string, unknown>;\n\t\treturn respond({\n\t\t\tsummary: `Uptime check is ${String(check['status'])}${check['lastError'] ? ` — ${String(check['lastError'])}` : ''}.`,\n\t\t\tdata: { check },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'set_uptime_check',\n\tcategory: 'uptime',\n\tdescription: [\n\t\t\"Create or update a service's uptime check.\",\n\t\t'',\n\t\t'When to use: turning monitoring on for a service that has just gone live, or adjusting a check that is too noisy (raise failureThreshold) or too slow to notice (lower intervalSeconds).',\n\t\t'',\n\t\t'Only service types with a public URL can be checked (web services and static sites). A worker, cron job or private service is refused with 400 — a check on one could only ever report \"no domain to check\", which reads as a broken feature rather than an inapplicable one.',\n\t\t'',\n\t\t'For a site HostStack does NOT host, there is no serviceId to pass: use set_site_uptime_check with its analytics siteId instead. That path needs the domain proven first (verify_site_domain), because the host it probes comes from the site row rather than from a domain the platform already vouches for.',\n\t\t'',\n\t\t'IMPORTANT: changing the shape of a check RESETS its accumulated state (status, consecutive failures, open alert). A check whose path or expected status just changed has not observed the new check failing, so carrying failures forward would alert about a condition that was never measured. A write that resolves to the shape already stored changes nothing and leaves that state alone — but the inputs are defaults, not a partial edit, so omitting intervalSeconds on a check set to 30 resolves to 60 and IS a change. Read a check with get_uptime_check; never re-save one in order to look at it.',\n\t\t'',\n\t\t\"`path` is a path, not a URL: the host comes from the service's primary domain at probe time, so moving the service to a new domain moves the check with it. On a service with SEVERAL domains that is a choice, and this tool does not make it — nominate the hostname to watch with update_domain({ domain_id, isPrimary: true }), or the platform falls back to the OLDEST domain, which after a rename is usually the alias that redirects. Check which one is live with get_uptime_check's lastTargetUrl before trusting an expectedStatus. When ONE named hostname has to be asserted whatever the platform picks — a canonical host and a 301 alias cannot share an expectedStatus — give that hostname its own analytics site and check it with set_site_uptime_check.\",\n\t\t'',\n\t\t\"Method is GET or HEAD only. A probe fires unattended every interval forever, so it has to be safe to repeat — a check that could POST would be a scheduled writer against the team's own API.\",\n\t\t'',\n\t\t'Redirects are NOT followed: a 301 is an answer, and following one can walk the probe onto a marketing site and report a dead service as healthy. If a redirect is expected, set expectedStatus to it.',\n\t\t'',\n\t\t'Inputs (all optional except serviceId): enabled, path (default \"/\"), method (GET|HEAD), expectedStatus (default 200), timeoutMs (1000–60000, default 10000 — this is what catches a hung server), intervalSeconds (30–3600, default 60), failureThreshold (1–10, default 3 — one failed request is usually a restart, not an outage).',\n\t\t'',\n\t\t'Returns: { check } with the saved check.',\n\t\t'',\n\t\t\"Example: set_uptime_check({ serviceId: 48, path: '/healthz', intervalSeconds: 60, failureThreshold: 3 }) → { check: { status: 'unknown', … } }.\",\n\t].join('\\n'),\n\tinput: {\n\t\tserviceId: z.number().int().positive(),\n\t\tenabled: z.boolean().optional(),\n\t\tpath: z.string().max(500).optional().describe('Must start with /. Default \"/\".'),\n\t\tmethod: z.enum(['GET', 'HEAD']).optional(),\n\t\texpectedStatus: z.number().int().min(100).max(599).optional(),\n\t\ttimeoutMs: z.number().int().min(1000).max(60_000).optional(),\n\t\tintervalSeconds: z.number().int().min(30).max(3600).optional(),\n\t\tfailureThreshold: z.number().int().min(1).max(10).optional(),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst { serviceId, ...body } = args;\n\t\tconst response = await ctx.api.put<{ check: Record<string, unknown> }>(\n\t\t\t`/api/services/${teamId}/${serviceId}/uptime-check`,\n\t\t\tbody,\n\t\t);\n\t\tconst check = shape(response.check) as Record<string, unknown>;\n\t\t// A null lastCheckedAt is what BOTH a never-probed check and a just-reset\n\t\t// one look like, so say which: non-null means the write changed nothing and\n\t\t// the accumulated state survived.\n\t\tconst probed = check['lastCheckedAt'] !== null && check['lastCheckedAt'] !== undefined;\n\t\treturn respond({\n\t\t\tsummary: probed\n\t\t\t\t? `Uptime check saved — the shape was already stored, so nothing was reset: still ${String(check['status'])}, last probed ${String(check['lastCheckedAt'])}.`\n\t\t\t\t: 'Uptime check saved, and not probed yet — \"unknown\" with no lastCheckedAt is the state of a check that has not run once, not a broken one. It reports within a minute or two: read it with get_uptime_check, never by saving it again.',\n\t\t\tdata: { check },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'delete_uptime_check',\n\tcategory: 'uptime',\n\tdescription: [\n\t\t'Stop checking a service. Any open \"not answering\" alert is left as it stands rather than being silently resolved — removing the monitor is not evidence the service came back.',\n\t\t'',\n\t\t'When to use: the service is being retired, or monitoring has moved somewhere else. To pause a check without losing its configuration, call set_uptime_check with enabled=false instead.',\n\t\t'',\n\t\t'Inputs: serviceId (required).',\n\t\t'',\n\t\t'Returns: { success: true }.',\n\t\t'',\n\t\t'Example: delete_uptime_check({ serviceId: 48 }) → { success: true }.',\n\t].join('\\n'),\n\tinput: { serviceId: z.number().int().positive() },\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tawait ctx.api.delete(`/api/services/${teamId}/${args.serviceId}/uptime-check`);\n\t\treturn respond({ summary: 'Uptime check removed. Nothing is watching this service now.' });\n\t},\n});\n","import { z } from 'zod';\n\nimport { defineTool } from '../registry.js';\nimport { respond } from '../lib/respond.js';\nimport { shape, shapeList, shapeVolume } from '../lib/shape.js';\n\n/**\n * Platform minimum for a persistent disk, in GB.\n *\n * Inlined rather than imported: this package publishes to npm standalone and\n * must not depend on the `@hoststack/shared` workspace package. Kept in sync\n * with `HETZNER_BLOCK_VOLUME_MIN_GB` in\n * packages/shared/src/constants/pricing.ts — the API rejects anything below\n * it, so a lower bound here only converts a clean rejection into a failed\n * deploy.\n */\nconst MIN_VOLUME_SIZE_GB = 10;\n\ndefineTool({\n\tname: 'list_volumes',\n\tcategory: 'volumes',\n\tdescription: [\n\t\t'List persistent disks attached to a service. Volumes mount writable storage into the container at the path you choose, surviving redeploys and restarts.',\n\t\t'',\n\t\t'When to use: auditing what disks are attached, checking sizes before adding more, or confirming a volume took effect after creation. Note: storage is metered for billing — use this to spot oversized disks.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t'',\n\t\t'Returns: { items: Volume[] } — each entry has id, publicId, name, mountPath, sizeGb, status (pending|active|deleting), backupEnabled, createdAt, updatedAt.',\n\t\t'',\n\t\t'`backupEnabled` says backups are being TAKEN, not that any exist. Call list_volume_backups to see which archives are actually restorable — a volume can be enabled and have nothing behind it (nothing has completed yet, or the host has no upload grant and is writing tars onto the very disk it is backing up, which is not a copy of anything). Turn it on with update_volume({ backup_enabled: true }).',\n\t\t'',\n\t\t'Example: list_volumes({ service_id: \"svc_abc\" }) → { items: [{ name: \"data\", mountPath: \"/var/data\", sizeGb: 10, status: \"active\", backupEnabled: false }] }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId (e.g. svc_abc123).'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.volumes.list(teamId, args.service_id);\n\t\tconst data = shapeList(response, 'volumes', shapeVolume);\n\t\tconst summary =\n\t\t\tdata.items.length === 0\n\t\t\t\t? `No volumes attached to service ${args.service_id}.`\n\t\t\t\t: `Found ${data.items.length} volume${data.items.length === 1 ? '' : 's'} on service ${args.service_id}.`;\n\t\treturn respond({ summary, data });\n\t},\n});\n\ndefineTool({\n\tname: 'create_volume',\n\tcategory: 'volumes',\n\tdescription: [\n\t\t'Attach a new persistent disk to a service. The disk gets provisioned on the host and bind-mounted into the container at `mount_path` on the next deploy. Use this when an app needs writable persistent storage — uploads, caches, SQLite databases, anything that must survive container restarts.',\n\t\t'',\n\t\t'When to use: the user mentions storing files, uploads, or any data that needs to outlive a container restart. For purely ephemeral scratch space, point the app at /tmp instead (already tmpfs-mounted, free, no provisioning needed) — see HOSTSTACK_TMP_DIR.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service to attach to.',\n\t\t' - name: lowercase alphanumeric + hyphens, ≤64 chars (used as the docker volume identifier — change with care once data is written).',\n\t\t' - mount_path: in-container absolute path (e.g. \"/var/data\").',\n\t\t' - size_gb: optional, 10–100, default 10. 10 GB is the platform minimum (the underlying block storage cannot provision anything smaller) — a smaller value is rejected here rather than failing the next deploy. Metered for billing.',\n\t\t'',\n\t\t'Returns: { volume: Volume } — the created record. A durable volume comes back `status: \"pending\"`; the disk is created and attached on the next deploy, which flips it to \"active\". A deploy is required before the mount exists.',\n\t\t'',\n\t\t'Example: create_volume({ service_id: \"svc_abc\", name: \"data\", mount_path: \"/var/data\", size_gb: 10 }) → { volume: { name: \"data\", mountPath: \"/var/data\", sizeGb: 10, status: \"pending\" } }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t\tname: z\n\t\t\t.string()\n\t\t\t.min(1)\n\t\t\t.max(64)\n\t\t\t.regex(/^[a-z0-9-]+$/)\n\t\t\t.describe('Volume name (lowercase alphanumeric + hyphens).'),\n\t\tmount_path: z\n\t\t\t.string()\n\t\t\t.startsWith('/')\n\t\t\t.max(500)\n\t\t\t.describe('In-container mount path (absolute).'),\n\t\t// 10 GB is the real floor: the block-storage backend rejects anything\n\t\t// smaller. Advertising 1 GB here (and defaulting to it) meant taking the\n\t\t// defaults produced a volume that provisioned with `Hetzner API error:\n\t\t// 422` on the NEXT deploy, with nothing tying the failure back to the\n\t\t// size. Reject it at the call instead.\n\t\tsize_gb: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(MIN_VOLUME_SIZE_GB)\n\t\t\t.max(100)\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t`Disk size in GB (minimum ${MIN_VOLUME_SIZE_GB}, default ${MIN_VOLUME_SIZE_GB}, max 100 via MCP).`,\n\t\t\t),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst input: { name: string; mountPath: string; sizeGb?: number } = {\n\t\t\tname: args.name,\n\t\t\tmountPath: args.mount_path,\n\t\t};\n\t\tif (args.size_gb !== undefined) input.sizeGb = args.size_gb;\n\t\tconst response = await ctx.hoststack.volumes.create(teamId, args.service_id, input);\n\t\tconst data = { volume: shape(response.volume) };\n\t\treturn respond({\n\t\t\tsummary: `Attached volume \"${args.name}\" (${response.volume.sizeGb}GB) at ${args.mount_path} on service ${args.service_id}. Status is \"${response.volume.status}\" — deploy the service to provision and mount it.`,\n\t\t\tdata,\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'update_volume',\n\tcategory: 'volumes',\n\tdescription: [\n\t\t\"Update a volume's mountPath or sizeGb. Resizes take effect on the next deploy; shrinking is rejected at the storage layer (filesystems can't safely shrink under live data).\",\n\t\t'',\n\t\t'When to use: the user wants to grow the disk for an existing volume, or move where it mounts inside the container.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t' - volume_id: publicId of the volume to update.',\n\t\t' - mount_path (optional): new in-container mount path.',\n\t\t' - size_gb (optional): new size in GB (must be ≥ current).',\n\t\t' - backup_enabled (optional): whether this volume is backed up nightly. Turning it off stops future backups; it does not delete the ones already taken. Verify with list_volume_backups — enabling is not the same as having a backup.',\n\t\t'',\n\t\t'IMPORTANT about what a volume backup is: a block-level tar of a LIVE filesystem, i.e. CRASH CONSISTENT, not application consistent. Nothing is quiesced. For a container running its own database (WordPress + MariaDB in one box, Postgres on a disk, SQLite under write), the archive captures whatever was on disk mid-write — the same state the database would face after a power cut. Usually recoverable, occasionally not. Recommend this as the disaster fallback and a scheduled dump as the actual backup; do not present it as a substitute for one.',\n\t\t'',\n\t\t'Returns: { volume: Volume } — the updated record.',\n\t\t'',\n\t\t'Example: update_volume({ service_id: \"svc_abc\", volume_id: \"vol_xyz\", size_gb: 20 }) → { volume: { sizeGb: 20, … } }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t\tvolume_id: z.string().describe('Volume publicId (e.g. vol_…).'),\n\t\tmount_path: z.string().startsWith('/').max(500).optional().describe('New mount path.'),\n\t\tsize_gb: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.min(MIN_VOLUME_SIZE_GB)\n\t\t\t.max(100)\n\t\t\t.optional()\n\t\t\t.describe(`New size in GB (minimum ${MIN_VOLUME_SIZE_GB}, grow-only).`),\n\t\tbackup_enabled: z\n\t\t\t.boolean()\n\t\t\t.optional()\n\t\t\t.describe('Whether to keep backing this volume up. Past backups are kept either way.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst input: { mountPath?: string; sizeGb?: number; backupEnabled?: boolean } = {};\n\t\tif (args.mount_path !== undefined) input.mountPath = args.mount_path;\n\t\tif (args.size_gb !== undefined) input.sizeGb = args.size_gb;\n\t\tif (args.backup_enabled !== undefined) input.backupEnabled = args.backup_enabled;\n\t\tif (Object.keys(input).length === 0) {\n\t\t\treturn respond({ summary: 'No fields to update.', data: {} });\n\t\t}\n\t\tconst response = await ctx.hoststack.volumes.update(\n\t\t\tteamId,\n\t\t\targs.service_id,\n\t\t\targs.volume_id,\n\t\t\tinput,\n\t\t);\n\t\tconst data = { volume: shape(response.volume) };\n\t\tconst fields = Object.keys(input).join(', ');\n\t\treturn respond({ summary: `Updated ${fields} on volume ${args.volume_id}.`, data });\n\t},\n});\n\ndefineTool({\n\tname: 'list_volume_backups',\n\tcategory: 'volumes',\n\tdescription: [\n\t\t'List the archives a volume can be restored from, newest first.',\n\t\t'',\n\t\t'WHY THIS MATTERS: `backupEnabled` on a volume says backups are being TAKEN. It does not say any exist. A volume can be enabled and have nothing behind it — nothing has completed a cycle yet, or the host has no upload grant and is writing its tar onto the very disk it is backing up (that file is not a copy of anything and is deliberately NOT listed here). This tool is how you tell \"protected\" from \"believed to be protected\". Check it whenever a volume holds the only copy of something.',\n\t\t'',\n\t\t'When to use: before a risky migration or deploy, when someone asks what the recovery position is, or to pick an archive to hand to restore_volume.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t' - volume_id: publicId of the volume (vol_…).',\n\t\t'',\n\t\t'Returns: { items: RestorePoint[] } — id (pass to restore_volume), archiveName, sizeBytes (may be null on older agents), createdAt, s3Url. Only the most recent few are kept: the host prunes older archives out of the bucket on its own schedule.',\n\t\t'',\n\t\t'Example: list_volume_backups({ service_id: \"svc_abc\", volume_id: \"vol_xyz\" }) → { items: [{ id: 41, archiveName: \"backup-20260911-0300.tar.gz\", sizeBytes: 184532992, createdAt: \"2026-09-11T03:00:11Z\" }] }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t\tvolume_id: z.string().describe('Volume publicId (e.g. vol_…).'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tconst response = await ctx.hoststack.volumes.listRestorePoints(\n\t\t\tteamId,\n\t\t\targs.service_id,\n\t\t\targs.volume_id,\n\t\t);\n\t\tconst data = shapeList(response, 'restorePoints', shape);\n\t\tconst newest = response.restorePoints[0];\n\t\tconst summary =\n\t\t\tdata.items.length === 0\n\t\t\t\t? `Volume ${args.volume_id} has NO restore points. If backupEnabled is true, either no backup has completed yet or the host cannot upload off-site — either way there is currently nothing to restore from.`\n\t\t\t\t: `Volume ${args.volume_id} has ${data.items.length} restore point${data.items.length === 1 ? '' : 's'}; newest ${newest?.archiveName} from ${newest?.createdAt}.`;\n\t\treturn respond({ summary, data });\n\t},\n});\n\ndefineTool({\n\tname: 'restore_volume',\n\tcategory: 'volumes',\n\tdescription: [\n\t\t\"Unpack one of a volume's archives back over its contents.\",\n\t\t'',\n\t\t'DESTRUCTIVE AND NOT REVERSIBLE. Everything on the disk now is replaced by what was on it when the archive was taken. Nothing snapshots the pre-restore state first, so a restore to the wrong point loses the data you had. CONFIRM WITH THE USER — naming the archive and its timestamp — before calling this. Never call it to \"check whether a backup works\".',\n\t\t'',\n\t\t'What the archive is: a block-level tar of a filesystem that was LIVE when it was taken — crash consistent, not application consistent. A database inside it will come back the way it would after a power cut, and may need its own recovery on first start. Stop the service before restoring if you can.',\n\t\t'',\n\t\t'When to use: the user explicitly asks to roll a volume back to an earlier state, or to recover after data loss.',\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t' - volume_id: publicId of the volume.',\n\t\t' - backup_id: the numeric id of a restore point from list_volume_backups. Call that FIRST and show the user the choice — never guess an id.',\n\t\t'',\n\t\t'Returns: { ok: true } once the host has accepted the restore. The unpack itself runs on the host; re-check the service afterwards.',\n\t\t'',\n\t\t'Example: restore_volume({ service_id: \"svc_abc\", volume_id: \"vol_xyz\", backup_id: 41 })',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t\tvolume_id: z.string().describe('Volume publicId.'),\n\t\tbackup_id: z\n\t\t\t.number()\n\t\t\t.int()\n\t\t\t.positive()\n\t\t\t.describe('Restore-point id from list_volume_backups. Confirm with the user first.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tawait ctx.hoststack.volumes.restore(teamId, args.service_id, args.volume_id, {\n\t\t\tbackupId: args.backup_id,\n\t\t});\n\t\treturn respond({\n\t\t\tsummary: `Restore of volume ${args.volume_id} from backup ${args.backup_id} accepted — the host is unpacking the archive over the volume's contents. Check the service once it finishes; a database on this volume may run its own crash recovery on first start.`,\n\t\t\tdata: { ok: true },\n\t\t});\n\t},\n});\n\ndefineTool({\n\tname: 'delete_volume',\n\tcategory: 'volumes',\n\tdescription: [\n\t\t'Detach and deprovision a volume. The underlying disk is destroyed — back up any data first. Async: marks the row deleting, the host agent finalises removal once it acks. A periodic reaper retries if the agent was offline at delete time.',\n\t\t'',\n\t\t\"When to use: the user explicitly says to remove a volume, or you're cleaning up after retiring a service. Confirm before calling on production data.\",\n\t\t'',\n\t\t'Inputs:',\n\t\t' - service_id: publicId of the service.',\n\t\t' - volume_id: publicId of the volume.',\n\t\t'',\n\t\t'Returns: { ok: true }.',\n\t\t'',\n\t\t'Example: delete_volume({ service_id: \"svc_abc\", volume_id: \"vol_xyz\" }) → { ok: true }',\n\t].join('\\n'),\n\tinput: {\n\t\tservice_id: z.string().describe('Service publicId.'),\n\t\tvolume_id: z.string().describe('Volume publicId.'),\n\t},\n\thandler: async (args, ctx) => {\n\t\tconst teamId = await ctx.resolveTeamId();\n\t\tawait ctx.hoststack.volumes.delete(teamId, args.service_id, args.volume_id);\n\t\treturn respond({\n\t\t\tsummary: `Detached volume ${args.volume_id} from service ${args.service_id} (deprovisioning).`,\n\t\t\tdata: { ok: true },\n\t\t});\n\t},\n});\n"],"mappings":"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","names":["z","z","z","z","z","z","z","z","z","z","z","z","z","zones","z","z","z","z","z","response","data","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z"]}
|