From 97d3bfa34e461e6b4dfd868bf25e77812919f2de Mon Sep 17 00:00:00 2001 From: Waishnav <86405648+Waishnav@users.noreply.github.com> Date: Mon, 24 Aug 2026 01:05:11 +0530 Subject: [PATCH 01/75] refactor(config): decode persisted documents --- src/local-agent-config.ts | 13 +++++++---- src/user-config.ts | 48 +++++++++++++++++++++------------------ 2 files changed, 35 insertions(+), 26 deletions(-) diff --git a/src/local-agent-config.ts b/src/local-agent-config.ts index 3f1de5aad..538355d92 100644 --- a/src/local-agent-config.ts +++ b/src/local-agent-config.ts @@ -11,7 +11,7 @@ const providerSchema = z.object({ effort: z.string().trim().min(1).optional(), }).strict(); -const subagentsSchema = z.object({ +export const subagentsConfigSchema = z.object({ enabled: z.boolean(), providers: z.array(providerSchema), }).strict().superRefine((value, context) => { @@ -28,9 +28,14 @@ const subagentsSchema = z.object({ } }); +export const storedSubagentsConfigSchema = z.union([ + z.boolean(), + subagentsConfigSchema, +]); + export type SubagentProviderConfig = z.infer; -export type SubagentsConfig = z.infer; -export type StoredSubagentsConfig = boolean | SubagentsConfig; +export type SubagentsConfig = z.infer; +export type StoredSubagentsConfig = z.infer; export function resolveSubagentsConfig( value: unknown, @@ -40,7 +45,7 @@ export function resolveSubagentsConfig( ? { enabled: false, providers: [] } : typeof value === "boolean" ? legacySubagentsConfig(value) - : subagentsSchema.parse(value); + : subagentsConfigSchema.parse(value); return { ...stored, enabled: env.DEVSPACE_SUBAGENTS === undefined diff --git a/src/user-config.ts b/src/user-config.ts index 98d05ac68..4d3e33ede 100644 --- a/src/user-config.ts +++ b/src/user-config.ts @@ -7,26 +7,30 @@ import { } from "node:fs"; import { homedir } from "node:os"; import { join, resolve } from "node:path"; +import * as z from "zod/v4"; import { expandHomePath } from "./roots.js"; -import type { StoredSubagentsConfig } from "./local-agent-config.js"; - -export interface DevspaceUserConfig { - host?: string; - port?: number; - allowedRoots?: string[]; - publicBaseUrl?: string | null; - allowedHosts?: string[]; - stateDir?: string; - worktreeRoot?: string; - artifactsEnabled?: boolean; - artifactMaxFileBytes?: number; - agentDir?: string; - subagents?: StoredSubagentsConfig; -} +import { storedSubagentsConfigSchema } from "./local-agent-config.js"; -export interface DevspaceAuthConfig { - ownerToken?: string; -} +const devspaceUserConfigSchema = z.object({ + host: z.string().optional(), + port: z.number().optional(), + allowedRoots: z.array(z.string()).optional(), + publicBaseUrl: z.string().nullable().optional(), + allowedHosts: z.array(z.string()).optional(), + stateDir: z.string().optional(), + worktreeRoot: z.string().optional(), + artifactsEnabled: z.boolean().optional(), + artifactMaxFileBytes: z.number().optional(), + agentDir: z.string().optional(), + subagents: storedSubagentsConfigSchema.optional(), +}).strict(); + +const devspaceAuthConfigSchema = z.object({ + ownerToken: z.string().optional(), +}).strict(); + +export type DevspaceUserConfig = z.infer; +export type DevspaceAuthConfig = z.infer; export interface DevspaceFiles { dir: string; @@ -71,8 +75,8 @@ export function loadDevspaceFiles(env: NodeJS.ProcessEnv = process.env): Devspac authPath, configExists, authExists, - config: configExists ? readJsonFile(configPath) : {}, - auth: authExists ? readJsonFile(authPath) : {}, + config: configExists ? readJsonFile(configPath, devspaceUserConfigSchema) : {}, + auth: authExists ? readJsonFile(authPath, devspaceAuthConfigSchema) : {}, }; } @@ -100,9 +104,9 @@ export function generateOwnerToken(): string { return randomBytes(32).toString("base64url"); } -function readJsonFile(filePath: string): T { +function readJsonFile(filePath: string, schema: z.ZodType): T { try { - return JSON.parse(readFileSync(filePath, "utf8")) as T; + return schema.parse(JSON.parse(readFileSync(filePath, "utf8")) as unknown); } catch (error) { const reason = error instanceof Error ? error.message : String(error); throw new Error(`Unable to read ${filePath}: ${reason}`); From ea7fea2f5397a7746d02a4c06a41c67b0810b6e4 Mon Sep 17 00:00:00 2001 From: Waishnav <86405648+Waishnav@users.noreply.github.com> Date: Mon, 24 Aug 2026 01:05:11 +0530 Subject: [PATCH 02/75] test(config): reject invalid persisted documents --- package.json | 2 +- src/user-config.test.ts | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 src/user-config.test.ts diff --git a/package.json b/package.json index 388f99e25..723881aec 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "dev": "node scripts/dev-server.mjs", "postinstall": "node scripts/fix-node-pty-permissions.mjs", "start": "node dist/cli.js serve", - "test": "tsx src/config.test.ts && tsx src/onboarding.test.ts && tsx src/cli-workspace.test.ts && tsx src/request-meta.test.ts && tsx src/incoming-artifacts.test.ts && tsx src/artifact-download.test.ts && tsx src/ui/card-types.test.ts && tsx src/ui/patch-display.test.ts && tsx src/ui/tool-display.test.ts && tsx src/apply-patch.test.ts && tsx src/process-platform.test.ts && tsx src/process-sessions.test.ts && tsx src/mcp-sessions.test.ts && tsx src/server-shutdown.test.ts && tsx src/local-agent-config.test.ts && tsx src/local-agent-catalog.test.ts && tsx src/local-agent-presentation.test.ts && tsx src/local-agent-runtime.test.ts && tsx src/local-agent-daemon-lifecycle.test.ts && tsx src/local-agent-daemon-protocol.test.ts && tsx src/local-agent-daemon.test.ts && tsx src/local-agent-codex.test.ts && tsx src/local-agent-opencode.test.ts && tsx src/local-agent-acp.test.ts && tsx src/local-agent-grok.test.ts && tsx src/local-agent-pi-sandbox.test.ts && tsx src/local-agent-pi.test.ts && tsx src/local-agent-claude.test.ts && tsx src/local-agent-adapters.test.ts && tsx src/local-agent-availability.test.ts && tsx src/local-agent-profiles.test.ts && tsx src/local-agent-targets.test.ts && tsx src/local-agent-store.test.ts && tsx src/local-agent-manager.test.ts && tsx src/roots.test.ts && tsx src/skills.test.ts && tsx src/workspaces.test.ts && tsx src/workspace-conversation.test.ts && tsx src/review-checkpoints.test.ts && tsx src/server.test.ts && tsx src/oauth-store.test.ts && tsx src/cli.test.ts", + "test": "tsx src/user-config.test.ts && tsx src/config.test.ts && tsx src/onboarding.test.ts && tsx src/cli-workspace.test.ts && tsx src/request-meta.test.ts && tsx src/incoming-artifacts.test.ts && tsx src/artifact-download.test.ts && tsx src/ui/card-types.test.ts && tsx src/ui/patch-display.test.ts && tsx src/ui/tool-display.test.ts && tsx src/apply-patch.test.ts && tsx src/process-platform.test.ts && tsx src/process-sessions.test.ts && tsx src/mcp-sessions.test.ts && tsx src/server-shutdown.test.ts && tsx src/local-agent-config.test.ts && tsx src/local-agent-catalog.test.ts && tsx src/local-agent-presentation.test.ts && tsx src/local-agent-runtime.test.ts && tsx src/local-agent-daemon-lifecycle.test.ts && tsx src/local-agent-daemon-protocol.test.ts && tsx src/local-agent-daemon.test.ts && tsx src/local-agent-codex.test.ts && tsx src/local-agent-opencode.test.ts && tsx src/local-agent-acp.test.ts && tsx src/local-agent-grok.test.ts && tsx src/local-agent-pi-sandbox.test.ts && tsx src/local-agent-pi.test.ts && tsx src/local-agent-claude.test.ts && tsx src/local-agent-adapters.test.ts && tsx src/local-agent-availability.test.ts && tsx src/local-agent-profiles.test.ts && tsx src/local-agent-targets.test.ts && tsx src/local-agent-store.test.ts && tsx src/local-agent-manager.test.ts && tsx src/roots.test.ts && tsx src/skills.test.ts && tsx src/workspaces.test.ts && tsx src/workspace-conversation.test.ts && tsx src/review-checkpoints.test.ts && tsx src/server.test.ts && tsx src/oauth-store.test.ts && tsx src/cli.test.ts", "typecheck": "tsc -p tsconfig.json --noEmit" }, "keywords": [], diff --git a/src/user-config.test.ts b/src/user-config.test.ts new file mode 100644 index 000000000..c058e387e --- /dev/null +++ b/src/user-config.test.ts @@ -0,0 +1,41 @@ +import assert from "node:assert/strict"; +import { mkdtempSync, rmSync, writeFileSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { loadDevspaceFiles } from "./user-config.js"; + +const configDir = mkdtempSync(join(tmpdir(), "devspace-user-config-test-")); +const env = { DEVSPACE_CONFIG_DIR: configDir }; + +try { + writeFileSync(join(configDir, "config.json"), JSON.stringify({ + port: 8787, + subagents: { + enabled: true, + providers: [{ id: "codex", enabled: true }], + }, + })); + writeFileSync(join(configDir, "auth.json"), JSON.stringify({ + ownerToken: "test-owner-token", + })); + + assert.deepEqual(loadDevspaceFiles(env).config, { + port: 8787, + subagents: { + enabled: true, + providers: [{ id: "codex", enabled: true }], + }, + }); + assert.equal(loadDevspaceFiles(env).auth.ownerToken, "test-owner-token"); + + writeFileSync(join(configDir, "config.json"), JSON.stringify({ port: "8787" })); + assert.throws(() => loadDevspaceFiles(env), /expected number/i); + + writeFileSync(join(configDir, "config.json"), JSON.stringify({ unknownSetting: true })); + assert.throws(() => loadDevspaceFiles(env), /unrecognized key/i); + + writeFileSync(join(configDir, "config.json"), "{"); + assert.throws(() => loadDevspaceFiles(env), /Unable to read .*config\.json/); +} finally { + rmSync(configDir, { recursive: true, force: true }); +} From 2bfa1512221df8eb119a104a4f0e17b7681ca84b Mon Sep 17 00:00:00 2001 From: Waishnav <86405648+Waishnav@users.noreply.github.com> Date: Mon, 24 Aug 2026 01:06:11 +0530 Subject: [PATCH 03/75] fix(config): preserve legacy extension fields --- src/user-config.test.ts | 2 +- src/user-config.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/user-config.test.ts b/src/user-config.test.ts index c058e387e..f13ca46f5 100644 --- a/src/user-config.test.ts +++ b/src/user-config.test.ts @@ -32,7 +32,7 @@ try { assert.throws(() => loadDevspaceFiles(env), /expected number/i); writeFileSync(join(configDir, "config.json"), JSON.stringify({ unknownSetting: true })); - assert.throws(() => loadDevspaceFiles(env), /unrecognized key/i); + assert.equal(loadDevspaceFiles(env).config.unknownSetting, true); writeFileSync(join(configDir, "config.json"), "{"); assert.throws(() => loadDevspaceFiles(env), /Unable to read .*config\.json/); diff --git a/src/user-config.ts b/src/user-config.ts index 4d3e33ede..506b468c9 100644 --- a/src/user-config.ts +++ b/src/user-config.ts @@ -23,11 +23,11 @@ const devspaceUserConfigSchema = z.object({ artifactMaxFileBytes: z.number().optional(), agentDir: z.string().optional(), subagents: storedSubagentsConfigSchema.optional(), -}).strict(); +}).passthrough(); const devspaceAuthConfigSchema = z.object({ ownerToken: z.string().optional(), -}).strict(); +}).passthrough(); export type DevspaceUserConfig = z.infer; export type DevspaceAuthConfig = z.infer; From a3f00c983cc6852d6db6d60b4f2364d9aea95b17 Mon Sep 17 00:00:00 2001 From: Waishnav <86405648+Waishnav@users.noreply.github.com> Date: Sun, 23 Aug 2026 18:22:26 +0530 Subject: [PATCH 04/75] test(server): lock down mode tool surfaces --- src/server.test.ts | 65 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 62 insertions(+), 3 deletions(-) diff --git a/src/server.test.ts b/src/server.test.ts index cb29d11c4..ab7010cac 100644 --- a/src/server.test.ts +++ b/src/server.test.ts @@ -7,7 +7,7 @@ import test, { type TestContext } from "node:test"; import { promisify } from "node:util"; import { Client } from "@modelcontextprotocol/sdk/client/index.js"; import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js"; -import { loadConfig, type ServerConfig } from "./config.js"; +import { loadConfig, type ServerConfig, type ToolMode, type WidgetMode } from "./config.js"; import type { LocalAgentProviderAvailability } from "./local-agent-availability.js"; import { buildLocalAgentProviderStatuses } from "./local-agent-catalog.js"; import type { SubagentsConfig } from "./local-agent-config.js"; @@ -19,6 +19,63 @@ import { WorkspaceRegistry } from "./workspaces.js"; const execFileAsync = promisify(execFile); +test("tool modes expose the expected host-facing tool surface", async (t) => { + const cases: Array<{ + mode: ToolMode; + expected: string[]; + }> = [ + { + mode: "minimal", + expected: ["open_workspace", "read", "write", "edit", "bash"], + }, + { + mode: "full", + expected: ["open_workspace", "read", "write", "edit", "bash", "grep", "glob", "ls"], + }, + { + mode: "codex", + expected: ["open_workspace", "read", "apply_patch", "exec_command", "write_stdin"], + }, + ]; + + for (const { mode, expected } of cases) { + await t.test(mode, async (nested) => { + const context = await fixture(nested, { toolMode: mode, widgets: "off" }); + const tools = await context.client.listTools(); + + assert.deepEqual( + tools.tools.map((tool) => tool.name).sort(), + expected.sort(), + ); + }); + } +}); + +test("widget modes compose independently from tool modes", async (t) => { + const cases: Array<{ + widgets: WidgetMode; + showChanges: boolean; + workspaceCard: boolean; + }> = [ + { widgets: "off", showChanges: false, workspaceCard: false }, + { widgets: "changes", showChanges: true, workspaceCard: true }, + { widgets: "full", showChanges: false, workspaceCard: true }, + ]; + + for (const { widgets, showChanges, workspaceCard } of cases) { + await t.test(widgets, async (nested) => { + const context = await fixture(nested, { toolMode: "full", widgets }); + const tools = await context.client.listTools(); + const workspace = tools.tools.find((tool) => tool.name === "open_workspace"); + const changes = tools.tools.find((tool) => tool.name === "show_changes"); + const workspaceMeta = workspace?._meta as { ui?: unknown } | undefined; + + assert.equal(Boolean(changes), showChanges); + assert.equal(Boolean(workspaceMeta?.ui), workspaceCard); + }); + } +}); + test("open_workspace keeps lifecycle flags out of model output and preserves complete card metadata", async (t) => { const providerNote = "available"; const context = await fixture(t, { @@ -247,6 +304,8 @@ async function fixture( git?: boolean; localAgentProviders?: LocalAgentProviderAvailability[] | (() => LocalAgentProviderAvailability[]); subagents?: SubagentsConfig; + toolMode?: ToolMode; + widgets?: WidgetMode; } = {}, ): Promise { const root = await mkdtemp(join(tmpdir(), "devspace-server-test-")); @@ -284,8 +343,8 @@ async function fixture( DEVSPACE_ALLOWED_ROOTS: root, DEVSPACE_WORKTREE_ROOT: join(root, ".worktrees"), DEVSPACE_AGENT_DIR: agentDir, - DEVSPACE_WIDGETS: "full", - DEVSPACE_TOOL_MODE: "full", + DEVSPACE_WIDGETS: options.widgets ?? "full", + DEVSPACE_TOOL_MODE: options.toolMode ?? "full", DEVSPACE_SUBAGENTS: options.localAgentProviders ? "1" : "0", DEVSPACE_OAUTH_OWNER_TOKEN: "test-owner-token-that-is-long-enough", PORT: "1", From f45882bca3deb9caac3a0d784b27df5b40197dd8 Mon Sep 17 00:00:00 2001 From: Waishnav <86405648+Waishnav@users.noreply.github.com> Date: Sun, 23 Aug 2026 18:40:19 +0530 Subject: [PATCH 05/75] refactor(server): extract tool surface modules --- src/tool-surfaces/codex.ts | 366 ++++++++++++++++++++++ src/tool-surfaces/shared.ts | 151 +++++++++ src/tool-surfaces/standard.ts | 554 ++++++++++++++++++++++++++++++++++ src/tool-surfaces/types.ts | 104 +++++++ 4 files changed, 1175 insertions(+) create mode 100644 src/tool-surfaces/codex.ts create mode 100644 src/tool-surfaces/shared.ts create mode 100644 src/tool-surfaces/standard.ts create mode 100644 src/tool-surfaces/types.ts diff --git a/src/tool-surfaces/codex.ts b/src/tool-surfaces/codex.ts new file mode 100644 index 000000000..89153743e --- /dev/null +++ b/src/tool-surfaces/codex.ts @@ -0,0 +1,366 @@ +import { registerAppTool } from "@modelcontextprotocol/ext-apps/server"; +import * as z from "zod/v4"; +import { applyPatch } from "../apply-patch.js"; +import type { ProcessSnapshot } from "../process-sessions.js"; +import { + EDIT_TOOL_ANNOTATIONS, + SHELL_TOOL_ANNOTATIONS, + workspaceIdDescription, + type ToolRegistrationContext, +} from "./types.js"; +import { + contentText, + logToolCall, + resultOutputSchema, + textBlock, + textSummary, + toolWidgetDescriptorMeta, +} from "./shared.js"; + +type CodexRegistration = (context: ToolRegistrationContext) => void; + +export function registerCodexTools(context: ToolRegistrationContext): void { + for (const register of CODEX_REGISTRATIONS) { + register(context); + } +} + +const CODEX_REGISTRATIONS: readonly CodexRegistration[] = [ + registerApplyPatchTool, + registerCodexProcessTools, +]; + +function processResult(snapshot: ProcessSnapshot): string { + const status = snapshot.running + ? `Process running with session ID ${snapshot.sessionId}.` + : snapshot.signal + ? `Process exited after signal ${snapshot.signal}.` + : `Process exited with code ${snapshot.exitCode ?? "unknown"}.`; + return snapshot.output + ? `${snapshot.output.replace(/\n$/, "")}\n${status}` + : status; +} + +function processOutputSchema(): z.ZodRawShape { + return resultOutputSchema({ + sessionId: z.number().optional(), + running: z.boolean(), + exitCode: z.number().int().optional(), + signal: z.string().optional(), + wallTimeMs: z.number().nonnegative(), + outputTruncated: z.boolean(), + }); +} + +function processToolResponse( + tool: "exec_command" | "write_stdin", + workspaceId: string, + snapshot: ProcessSnapshot, + summary: Record, +) { + const result = processResult(snapshot); + const content = [textBlock(result)]; + const outputSummary = textSummary( + snapshot.output ? [textBlock(snapshot.output)] : [], + ); + return { + content, + _meta: { + tool, + card: { + workspaceId, + summary: { ...summary, ...outputSummary }, + payload: { content }, + }, + }, + structuredContent: { + result, + sessionId: snapshot.sessionId, + running: snapshot.running, + exitCode: snapshot.exitCode, + signal: snapshot.signal, + wallTimeMs: snapshot.wallTimeMs, + outputTruncated: snapshot.outputTruncated, + }, + }; +} + +function registerApplyPatchTool(context: ToolRegistrationContext): void { + const { server, config, workspaces } = context; + + registerAppTool( + server, + "apply_patch", + { + title: "Apply patch", + description: + "Apply one Codex-style patch in a workspace. Supports adding, overwriting, updating, deleting, and moving files. Use this for all file modifications. Paths must be relative to the workspace.", + inputSchema: { + workspaceId: z.string().describe(workspaceIdDescription), + patch: z + .string() + .describe( + "Patch text enclosed by *** Begin Patch and *** End Patch markers.", + ), + }, + outputSchema: resultOutputSchema({ + additions: z.number(), + removals: z.number(), + files: z.array( + z.object({ + path: z.string(), + previousPath: z.string().optional(), + operation: z.enum(["add", "update", "delete", "move"]), + }), + ), + }), + ...toolWidgetDescriptorMeta(config, "edit"), + annotations: EDIT_TOOL_ANNOTATIONS, + }, + async ({ workspaceId, patch }) => { + const startedAt = performance.now(); + const workspace = workspaces.getWorkspace(workspaceId); + const applied = await applyPatch(workspace.root, patch); + const paths = applied.files.map((file) => file.path).join(", "); + const result = `Applied patch to ${applied.files.length} file(s): ${paths}`; + const content = [textBlock(result)]; + const displayPath = + applied.files.length === 1 + ? applied.files[0]?.path + : `${applied.files.length} files`; + + logToolCall(config, { + tool: "apply_patch", + workspaceId, + success: true, + durationMs: Math.round(performance.now() - startedAt), + }); + + return { + content, + _meta: { + tool: "apply_patch", + card: { + workspaceId, + path: displayPath, + summary: { + files: applied.files.length, + additions: applied.additions, + removals: applied.removals, + }, + files: applied.files, + payload: { patch: applied.patch }, + }, + }, + structuredContent: { + result, + additions: applied.additions, + removals: applied.removals, + files: applied.files, + }, + }; + }, + ); +} + +function registerCodexProcessTools(context: ToolRegistrationContext): void { + const { server, config, workspaces, processSessions } = context; + + registerAppTool( + server, + "exec_command", + { + title: "Execute command", + description: + "Run a command in a workspace. Returns its result when it exits during the yield window, otherwise returns a sessionId for write_stdin. Use this for file inspection, tests, builds, package scripts, and long-running processes.", + inputSchema: { + workspaceId: z.string().describe(workspaceIdDescription), + cmd: z.string().min(1).describe("Shell command to execute."), + tty: z + .boolean() + .optional() + .describe( + "Allocate a pseudo-terminal for interactive commands. Defaults to false.", + ), + columns: z + .number() + .int() + .min(1) + .max(1_000) + .optional() + .describe("Initial PTY width. Defaults to 80."), + rows: z + .number() + .int() + .min(1) + .max(1_000) + .optional() + .describe("Initial PTY height. Defaults to 24."), + workingDirectory: z + .string() + .optional() + .describe( + "Working directory relative to the workspace root. Defaults to the workspace root.", + ), + yieldTimeMs: z + .number() + .int() + .min(0) + .max(30_000) + .optional() + .describe( + "Milliseconds to wait before returning a running session. Defaults to 10000.", + ), + maxOutputTokens: z + .number() + .int() + .positive() + .max(100_000) + .optional() + .describe("Approximate output token budget. Defaults to 10000."), + }, + outputSchema: processOutputSchema(), + ...toolWidgetDescriptorMeta(config, "shell"), + annotations: SHELL_TOOL_ANNOTATIONS, + }, + async ({ + workspaceId, + cmd, + tty, + columns, + rows, + workingDirectory, + yieldTimeMs, + maxOutputTokens, + }) => { + const startedAt = performance.now(); + const workspace = workspaces.getWorkspace(workspaceId); + const cwd = workspaces.resolveWorkingDirectory( + workspace, + workingDirectory, + ); + const snapshot = await processSessions.start({ + workspaceId, + command: cmd, + cwd, + workspaceRoot: workspace.root, + tty, + columns, + rows, + yieldTimeMs, + maxOutputTokens, + }); + + logToolCall(config, { + tool: "exec_command", + workspaceId, + workingDirectory: workingDirectory ?? ".", + command: cmd, + commandLength: cmd.length, + success: true, + durationMs: Math.round(performance.now() - startedAt), + }); + + return processToolResponse("exec_command", workspaceId, snapshot, { + command: cmd, + workingDirectory: workingDirectory ?? ".", + running: snapshot.running, + exitCode: snapshot.exitCode, + wallTimeMs: snapshot.wallTimeMs, + }); + }, + ); + + registerAppTool( + server, + "write_stdin", + { + title: "Write to process", + description: + "Poll or write characters to a process returned by exec_command. Omit chars or pass an empty string to poll. Pass \\u0003 to send Ctrl-C.", + inputSchema: { + workspaceId: z + .string() + .describe("Workspace identifier used to start the process."), + sessionId: z + .number() + .describe("Process session identifier returned by exec_command."), + chars: z + .string() + .optional() + .describe( + "Characters to write. Omit or pass an empty string to poll.", + ), + columns: z + .number() + .int() + .min(1) + .max(1_000) + .optional() + .describe("Resize a PTY to this width."), + rows: z + .number() + .int() + .min(1) + .max(1_000) + .optional() + .describe("Resize a PTY to this height."), + yieldTimeMs: z + .number() + .int() + .min(0) + .max(30_000) + .optional() + .describe( + "Milliseconds to wait for process output or completion. Defaults to 10000.", + ), + maxOutputTokens: z + .number() + .int() + .positive() + .max(100_000) + .optional() + .describe("Approximate output token budget. Defaults to 10000."), + }, + outputSchema: processOutputSchema(), + ...toolWidgetDescriptorMeta(config, "shell"), + annotations: SHELL_TOOL_ANNOTATIONS, + }, + async ({ + workspaceId, + sessionId, + chars, + columns, + rows, + yieldTimeMs, + maxOutputTokens, + }) => { + const startedAt = performance.now(); + workspaces.getWorkspace(workspaceId); + const snapshot = await processSessions.write({ + workspaceId, + sessionId, + chars, + columns, + rows, + yieldTimeMs, + maxOutputTokens, + }); + + logToolCall(config, { + tool: "write_stdin", + workspaceId, + success: true, + durationMs: Math.round(performance.now() - startedAt), + }); + + return processToolResponse("write_stdin", workspaceId, snapshot, { + sessionId, + charactersWritten: chars?.length ?? 0, + running: snapshot.running, + exitCode: snapshot.exitCode, + wallTimeMs: snapshot.wallTimeMs, + }); + }, + ); +} diff --git a/src/tool-surfaces/shared.ts b/src/tool-surfaces/shared.ts new file mode 100644 index 000000000..2e617af06 --- /dev/null +++ b/src/tool-surfaces/shared.ts @@ -0,0 +1,151 @@ +import * as z from "zod/v4"; +import { logEvent, commandPreview } from "../logger.js"; +import type { ServerConfig, WidgetMode } from "../config.js"; +import { + WORKSPACE_APP_URI, + type DiffStats, + type ToolContent, + type ToolLogFields, + type ToolWidgetDescriptorMeta, + type ToolWidgetKind, +} from "./types.js"; + +export function resultOutputSchema(extra: z.ZodRawShape = {}): z.ZodRawShape { + return { + result: z + .string() + .describe( + "Model-readable result text for follow-up reasoning and plain MCP hosts.", + ), + ...extra, + }; +} + +export function toolWidgetDescriptorMeta( + config: ServerConfig, + kind: ToolWidgetKind, +): ToolWidgetDescriptorMeta { + if (!shouldAttachWidget(config.widgets, kind)) return { _meta: {} }; + + return { + _meta: { + ui: { + resourceUri: WORKSPACE_APP_URI, + visibility: ["model"], + }, + }, + }; +} + +export function logToolCall(config: ServerConfig, fields: ToolLogFields): void { + if (!config.logging.toolCalls) return; + + const { command, ...safeFields } = fields; + logEvent(config.logging, fields.success ? "info" : "warn", "tool_call", { + ...safeFields, + commandPreview: + config.logging.shellCommands && command + ? commandPreview(command) + : undefined, + }); +} + +export function contentText(content: ToolContent[]): string { + return content + .filter( + (item): item is { type: "text"; text: string } => item.type === "text", + ) + .map((item) => item.text) + .join("\n"); +} + +function toolErrorPreview(content: ToolContent[]): string | undefined { + const text = contentText(content).replace(/\s+/g, " ").trim(); + if (!text) return undefined; + return text.length > 240 ? `${text.slice(0, 237)}...` : text; +} + +export function logFailedToolResponse( + config: ServerConfig, + fields: Omit, + content: ToolContent[], + startedAt: number, +): void { + logToolCall(config, { + ...fields, + success: false, + durationMs: Math.round(performance.now() - startedAt), + error: toolErrorPreview(content), + }); +} + +export function textBlock(text: string): ToolContent { + return { type: "text", text }; +} + +export function textSummary(content: ToolContent[]): { + lines: number; + characters: number; +} { + const text = contentText(content); + return { + lines: text.length === 0 ? 0 : text.split("\n").length, + characters: text.length, + }; +} + +export function contentLineCount(content: string): number { + if (content.length === 0) return 0; + return content.endsWith("\n") + ? content.slice(0, -1).split("\n").length + : content.split("\n").length; +} + +export function countDiffStats(diff: string | undefined): DiffStats { + if (!diff) return { additions: 0, removals: 0 }; + + let additions = 0; + let removals = 0; + + for (const line of diff.split("\n")) { + if (line.startsWith("+") && !line.startsWith("+++")) additions++; + if (line.startsWith("-") && !line.startsWith("---")) removals++; + } + + return { additions, removals }; +} + +export function newFilePatch(path: string, content: string): string { + const lines = + content.length === 0 + ? [] + : content.endsWith("\n") + ? content.slice(0, -1).split("\n") + : content.split("\n"); + const hunkLength = lines.length; + const hunkRange = hunkLength === 0 ? "+0,0" : `+1,${hunkLength}`; + const body = lines.map((line) => `+${line}`).join("\n"); + + return [ + `diff --git a/${path} b/${path}`, + "new file mode 100644", + "index 0000000..0000000", + "--- /dev/null", + `+++ b/${path}`, + `@@ -0,0 ${hunkRange} @@`, + body, + ] + .filter((line) => line.length > 0) + .join("\n"); +} + +function shouldAttachWidget(mode: WidgetMode, kind: ToolWidgetKind): boolean { + switch (mode) { + case "off": + return false; + case "changes": + return kind === "workspace" || kind === "show_changes"; + case "full": + return true; + } +} diff --git a/src/tool-surfaces/standard.ts b/src/tool-surfaces/standard.ts new file mode 100644 index 000000000..8c24d3cef --- /dev/null +++ b/src/tool-surfaces/standard.ts @@ -0,0 +1,554 @@ +import { registerAppTool } from "@modelcontextprotocol/ext-apps/server"; +import * as z from "zod/v4"; +import { + editFileTool, + findFilesTool, + grepFilesTool, + listDirectoryTool, + runShellTool, + writeFileTool, +} from "../pi-tools.js"; +import { + EDIT_TOOL_ANNOTATIONS, + SHELL_TOOL_ANNOTATIONS, + WRITE_TOOL_ANNOTATIONS, + toolNames, + workspaceIdDescription, + type ToolRegistrationContext, +} from "./types.js"; +import { + contentLineCount, + contentText, + countDiffStats, + logFailedToolResponse, + logToolCall, + newFilePatch, + resultOutputSchema, + textBlock, + textSummary, + toolWidgetDescriptorMeta, +} from "./shared.js"; + +type StandardRegistration = (context: ToolRegistrationContext) => void; + +export function registerStandardTools( + context: ToolRegistrationContext, + mode: "minimal" | "full", +): void { + for (const register of STANDARD_REGISTRATIONS[mode]) { + register(context); + } +} + +const STANDARD_REGISTRATIONS: Record< + "minimal" | "full", + readonly StandardRegistration[] +> = { + minimal: [registerStandardMutationTools, registerMinimalShellTool], + full: [ + registerStandardMutationTools, + registerSearchTools, + registerFullShellTool, + ], +}; + +const MINIMAL_SHELL_DESCRIPTION = `Run a shell command in a workspace. Use only for tests, builds, git inspection, package scripts, search, file discovery, and directory inspection. In minimal tool mode, ${toolNames.grep}, ${toolNames.glob}, and ${toolNames.ls} are disabled; use command-line tools such as grep, rg, find, ls, and tree for those read-only inspection actions. Do not use ${toolNames.shell} to create or modify files. Do not use shell redirection, heredocs, tee, sed -i, perl -i, node/python/ruby scripts, or generated scripts to write project files; use ${toolNames.edit} for targeted changes and ${toolNames.write} for new files or full rewrites. Prefer ${toolNames.read} for direct file reads. This is powerful execution and should only be exposed behind strong authentication.`; +const FULL_SHELL_DESCRIPTION = `Run a shell command in a workspace. Use only for tests, builds, git inspection, package scripts, and commands that are better executed by the shell. Do not use ${toolNames.shell} to create or modify files. Do not use shell redirection, heredocs, tee, sed -i, perl -i, node/python/ruby scripts, or generated scripts to write project files; use ${toolNames.edit} for targeted changes and ${toolNames.write} for new files or full rewrites. Prefer ${toolNames.read}, ${toolNames.grep}, ${toolNames.glob}, and ${toolNames.ls} for file inspection. This is powerful execution and should only be exposed behind strong authentication.`; + +function registerMinimalShellTool(context: ToolRegistrationContext): void { + registerShellTool(context, MINIMAL_SHELL_DESCRIPTION); +} + +function registerFullShellTool(context: ToolRegistrationContext): void { + registerShellTool(context, FULL_SHELL_DESCRIPTION); +} + +function registerStandardMutationTools(context: ToolRegistrationContext): void { + const { server, config, workspaces } = context; + + registerAppTool( + server, + toolNames.write, + { + title: "Write file", + description: `Create or completely overwrite a file in a workspace. Prefer ${toolNames.edit} for targeted changes to existing files.`, + inputSchema: { + workspaceId: z.string().describe(workspaceIdDescription), + path: z + .string() + .describe("File path to write, relative to the workspace root."), + content: z.string().describe("Complete new file content."), + }, + outputSchema: resultOutputSchema(), + ...toolWidgetDescriptorMeta(config, "write"), + annotations: WRITE_TOOL_ANNOTATIONS, + }, + async ({ workspaceId, ...input }) => { + const startedAt = performance.now(); + const workspace = workspaces.getWorkspace(workspaceId); + workspaces.resolvePath(workspace, input.path); + const response = await writeFileTool(input, { + cwd: workspace.root, + root: workspace.root, + }); + + if (response.isError) { + logFailedToolResponse( + config, + { + tool: toolNames.write, + workspaceId, + path: input.path, + }, + response.content, + startedAt, + ); + return response; + } + + const patch = newFilePatch(input.path, input.content); + const stats = countDiffStats(patch); + const summary = { + ...stats, + lines: contentLineCount(input.content), + characters: input.content.length, + }; + logToolCall(config, { + tool: toolNames.write, + workspaceId, + path: input.path, + success: true, + durationMs: Math.round(performance.now() - startedAt), + }); + + return { + ...response, + _meta: { + tool: toolNames.write, + card: { + workspaceId, + path: input.path, + summary, + payload: { + content: response.content, + patch, + }, + }, + }, + structuredContent: { + result: contentText(response.content), + }, + }; + }, + ); + + registerAppTool( + server, + toolNames.edit, + { + title: "Edit file", + description: `Edit one file in a workspace by replacing exact text blocks. Prefer this over ${toolNames.write} for targeted changes. Each oldText must match a unique, non-overlapping region of the original file; merge nearby changes into one edit and keep oldText as small as possible while still unique.`, + inputSchema: { + workspaceId: z.string().describe(workspaceIdDescription), + path: z + .string() + .describe("File path to edit, relative to the workspace root."), + edits: z + .array( + z.object({ + oldText: z + .string() + .describe( + "Exact text to replace. Must match uniquely in the original file.", + ), + newText: z.string().describe("Replacement text."), + }), + ) + .min(1), + }, + outputSchema: resultOutputSchema({ + status: z.literal("applied"), + }), + ...toolWidgetDescriptorMeta(config, "edit"), + annotations: EDIT_TOOL_ANNOTATIONS, + }, + async ({ workspaceId, ...input }) => { + const startedAt = performance.now(); + const workspace = workspaces.getWorkspace(workspaceId); + workspaces.resolvePath(workspace, input.path); + const response = await editFileTool(input, { + cwd: workspace.root, + root: workspace.root, + }); + + if (response.isError) { + logFailedToolResponse( + config, + { + tool: toolNames.edit, + workspaceId, + path: input.path, + }, + response.content, + startedAt, + ); + return response; + } + + const stats = countDiffStats( + response.details?.patch ?? response.details?.diff, + ); + const summary = { + ...stats, + editCount: input.edits.length, + }; + const editResultText = `Edited ${input.path} (+${stats.additions} -${stats.removals}).`; + const editContent = [textBlock(editResultText)]; + logToolCall(config, { + tool: toolNames.edit, + workspaceId, + path: input.path, + success: true, + durationMs: Math.round(performance.now() - startedAt), + }); + + return { + content: editContent, + _meta: { + tool: toolNames.edit, + card: { + workspaceId, + path: input.path, + summary, + payload: { + diff: response.details?.diff, + patch: response.details?.patch, + }, + }, + }, + structuredContent: { + status: "applied", + result: contentText(editContent), + }, + }; + }, + ); +} + +function registerSearchTools(context: ToolRegistrationContext): void { + const { server, config, workspaces } = context; + + registerAppTool( + server, + toolNames.grep, + { + title: "Grep", + description: + "Search file contents in a workspace. Use this before broad reads when looking for symbols, text, or usage sites. Respects project ignore rules.", + inputSchema: { + workspaceId: z.string().describe(workspaceIdDescription), + pattern: z.string().describe("Search pattern."), + path: z + .string() + .optional() + .describe( + "Optional path or glob scope relative to the workspace root.", + ), + include: z.string().optional().describe("Optional include glob."), + }, + outputSchema: resultOutputSchema(), + ...toolWidgetDescriptorMeta(config, "search"), + annotations: { readOnlyHint: true }, + }, + async ({ workspaceId, ...input }) => { + const startedAt = performance.now(); + const workspace = workspaces.getWorkspace(workspaceId); + if (input.path) workspaces.resolvePath(workspace, input.path); + const response = await grepFilesTool(input, { + cwd: workspace.root, + root: workspace.root, + }); + + if (response.isError) { + logFailedToolResponse( + config, + { + tool: toolNames.grep, + workspaceId, + path: input.path, + }, + response.content, + startedAt, + ); + return response; + } + + const summary = { + pattern: input.pattern, + scope: input.path ?? ".", + ...textSummary(response.content), + }; + logToolCall(config, { + tool: toolNames.grep, + workspaceId, + path: input.path, + success: true, + durationMs: Math.round(performance.now() - startedAt), + }); + + return { + ...response, + _meta: { + tool: toolNames.grep, + card: { + workspaceId, + path: input.path, + summary, + payload: { content: response.content }, + }, + }, + structuredContent: { + result: contentText(response.content), + }, + }; + }, + ); + + registerAppTool( + server, + toolNames.glob, + { + title: "Glob", + description: + "Find files by glob pattern in a workspace. Use this to discover filenames or narrow file sets before reading. Respects project ignore rules.", + inputSchema: { + workspaceId: z.string().describe(workspaceIdDescription), + pattern: z.string().describe("File glob pattern."), + path: z + .string() + .optional() + .describe("Optional path scope relative to the workspace root."), + }, + outputSchema: resultOutputSchema(), + ...toolWidgetDescriptorMeta(config, "search"), + annotations: { readOnlyHint: true }, + }, + async ({ workspaceId, ...input }) => { + const startedAt = performance.now(); + const workspace = workspaces.getWorkspace(workspaceId); + if (input.path) workspaces.resolvePath(workspace, input.path); + const response = await findFilesTool(input, { + cwd: workspace.root, + root: workspace.root, + }); + + if (response.isError) { + logFailedToolResponse( + config, + { + tool: toolNames.glob, + workspaceId, + path: input.path, + }, + response.content, + startedAt, + ); + return response; + } + + const summary = { + pattern: input.pattern, + scope: input.path ?? ".", + ...textSummary(response.content), + }; + logToolCall(config, { + tool: toolNames.glob, + workspaceId, + path: input.path, + success: true, + durationMs: Math.round(performance.now() - startedAt), + }); + + return { + ...response, + _meta: { + tool: toolNames.glob, + card: { + workspaceId, + path: input.path, + summary, + payload: { content: response.content }, + }, + }, + structuredContent: { + result: contentText(response.content), + }, + }; + }, + ); + + registerAppTool( + server, + toolNames.ls, + { + title: "Ls", + description: + "List a directory in a workspace. Use this for directory inspection before reading files.", + inputSchema: { + workspaceId: z.string().describe(workspaceIdDescription), + path: z + .string() + .describe("Directory path to list, relative to the workspace root."), + }, + outputSchema: resultOutputSchema(), + ...toolWidgetDescriptorMeta(config, "directory"), + annotations: { readOnlyHint: true }, + }, + async ({ workspaceId, ...input }) => { + const startedAt = performance.now(); + const workspace = workspaces.getWorkspace(workspaceId); + workspaces.resolvePath(workspace, input.path); + const response = await listDirectoryTool(input, { + cwd: workspace.root, + root: workspace.root, + }); + + if (response.isError) { + logFailedToolResponse( + config, + { + tool: toolNames.ls, + workspaceId, + path: input.path, + }, + response.content, + startedAt, + ); + return response; + } + + const summary = textSummary(response.content); + logToolCall(config, { + tool: toolNames.ls, + workspaceId, + path: input.path, + success: true, + durationMs: Math.round(performance.now() - startedAt), + }); + + return { + ...response, + _meta: { + tool: toolNames.ls, + card: { + workspaceId, + path: input.path, + summary, + payload: { content: response.content }, + }, + }, + structuredContent: { + result: contentText(response.content), + }, + }; + }, + ); +} + +function registerShellTool( + context: ToolRegistrationContext, + shellDescription: string, +): void { + const { server, config, workspaces } = context; + + registerAppTool( + server, + toolNames.shell, + { + title: "Bash", + description: shellDescription, + inputSchema: { + workspaceId: z.string().describe(workspaceIdDescription), + command: z + .string() + .describe( + `Shell command to run. Must not create or modify project files; use ${toolNames.edit} or ${toolNames.write} for file changes.`, + ), + workingDirectory: z + .string() + .optional() + .describe( + "Optional working directory relative to the workspace root. Defaults to the workspace root.", + ), + timeout: z + .number() + .positive() + .max(300) + .optional() + .describe("Timeout in seconds. Defaults to 30, max 300."), + }, + outputSchema: resultOutputSchema(), + ...toolWidgetDescriptorMeta(config, "shell"), + annotations: SHELL_TOOL_ANNOTATIONS, + }, + async ({ workspaceId, workingDirectory, ...input }) => { + const startedAt = performance.now(); + const workspace = workspaces.getWorkspace(workspaceId); + const cwd = workspaces.resolveWorkingDirectory( + workspace, + workingDirectory, + ); + const response = await runShellTool(input, { + cwd, + root: workspace.root, + }); + + if (response.isError) { + logFailedToolResponse( + config, + { + tool: toolNames.shell, + workspaceId, + workingDirectory: workingDirectory ?? ".", + command: input.command, + commandLength: input.command.length, + }, + response.content, + startedAt, + ); + return response; + } + + const summary = { + command: input.command, + workingDirectory: workingDirectory ?? ".", + ...textSummary(response.content), + }; + logToolCall(config, { + tool: toolNames.shell, + workspaceId, + workingDirectory: workingDirectory ?? ".", + command: input.command, + commandLength: input.command.length, + success: true, + durationMs: Math.round(performance.now() - startedAt), + }); + + return { + ...response, + _meta: { + tool: toolNames.shell, + card: { + workspaceId, + path: workingDirectory, + summary, + payload: { content: response.content }, + }, + }, + structuredContent: { + result: contentText(response.content), + }, + }; + }, + ); +} diff --git a/src/tool-surfaces/types.ts b/src/tool-surfaces/types.ts new file mode 100644 index 000000000..c1fc13fdf --- /dev/null +++ b/src/tool-surfaces/types.ts @@ -0,0 +1,104 @@ +import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { ProcessSessionManager } from "../process-sessions.js"; +import type { ServerConfig } from "../config.js"; +import type { WorkspaceRegistry } from "../workspaces.js"; + +export const WORKSPACE_APP_URI = "ui://devspace/workspace-app.html"; + +export const toolNames = { + openWorkspace: "open_workspace", + read: "read", + write: "write", + edit: "edit", + grep: "grep", + glob: "glob", + ls: "ls", + shell: "bash", +} as const; + +export const workspaceIdDescription = + "Workspace to use. Reuse the current project's workspaceId."; + +export const WRITE_TOOL_ANNOTATIONS = { + readOnlyHint: false, + destructiveHint: true, + idempotentHint: false, + openWorldHint: false, +}; + +export const EDIT_TOOL_ANNOTATIONS = { + readOnlyHint: false, + destructiveHint: true, + idempotentHint: false, + openWorldHint: false, +}; + +export const SHELL_TOOL_ANNOTATIONS = { + readOnlyHint: false, + destructiveHint: true, + idempotentHint: false, + openWorldHint: true, +}; + +export type ToolContent = + | { type: "text"; text: string } + | { type: "image"; data: string; mimeType: string }; + +export interface ToolLogFields { + tool: string; + workspaceId?: string; + path?: string; + workingDirectory?: string; + command?: string; + commandLength?: number; + success: boolean; + durationMs: number; + error?: string; +} + +export interface DiffStats { + additions: number; + removals: number; +} + +export type ToolWidgetKind = + | "workspace" + | "read" + | "write" + | "edit" + | "search" + | "directory" + | "shell" + | "show_changes"; + +export interface ToolDefinitionMeta extends Record { + ui: { + resourceUri: string; + visibility: ["model"]; + }; +} + +export type EmptyToolDefinitionMeta = Record & { + "ui/resourceUri"?: string; +}; + +export interface ToolWidgetDescriptorMeta { + _meta: ToolDefinitionMeta | EmptyToolDefinitionMeta; +} + +export interface ToolRegistrationContext { + server: McpServer; + config: ServerConfig; + workspaces: WorkspaceRegistry; + processSessions: ProcessSessionManager; +} + +export interface ToolInstructionContext { + agents: string; + skills: string; +} + +export interface ToolSurface { + register(context: ToolRegistrationContext): void; + instructions(context: ToolInstructionContext): string; +} From bc46c17348fea141a84020790e204bc3ba859b4c Mon Sep 17 00:00:00 2001 From: Waishnav <86405648+Waishnav@users.noreply.github.com> Date: Sun, 23 Aug 2026 18:40:23 +0530 Subject: [PATCH 06/75] refactor(server): compose selected tool surface --- src/server.ts | 1027 ++---------------------------------- src/tool-surfaces/index.ts | 40 ++ 2 files changed, 78 insertions(+), 989 deletions(-) create mode 100644 src/tool-surfaces/index.ts diff --git a/src/server.ts b/src/server.ts index 16c2010d6..768608d67 100644 --- a/src/server.ts +++ b/src/server.ts @@ -17,12 +17,11 @@ import { import express from "express"; import type { Request, Response } from "express"; import * as z from "zod/v4"; -import { applyPatch } from "./apply-patch.js"; import { isArtifactDownloadSupportedPlatform, registerArtifactTools, } from "./artifact-tools.js"; -import { loadConfig, type ServerConfig, type WidgetMode } from "./config.js"; +import { loadConfig, type ServerConfig } from "./config.js"; import { createOpenAIIncomingArtifactAdapter, type IncomingArtifactAdapter, @@ -31,24 +30,15 @@ import { logEvent, requestIp, requestPath, - commandPreview, sessionIdPrefix, } from "./logger.js"; -import { - editFileTool, - findFilesTool, - grepFilesTool, - listDirectoryTool, - readFileTool, - runShellTool, - writeFileTool, -} from "./pi-tools.js"; +import { readFileTool } from "./pi-tools.js"; import { SingleUserOAuthProvider } from "./oauth-provider.js"; import { McpSessionRegistry, type McpSessionCloseResult, } from "./mcp-sessions.js"; -import { ProcessSessionManager, type ProcessSnapshot } from "./process-sessions.js"; +import { ProcessSessionManager } from "./process-sessions.js"; import { createReviewCheckpointManager } from "./review-checkpoints.js"; import { openAiConversationScopeId } from "./request-meta.js"; import { shutdownHttpServer } from "./server-shutdown.js"; @@ -64,32 +54,30 @@ import { formatLocalAgentProviderStatusSummary, type LocalAgentProviderStatus, } from "./local-agent-catalog.js"; +import { getToolSurface } from "./tool-surfaces/index.js"; +import { + contentText, + logFailedToolResponse, + logToolCall, + resultOutputSchema, + textBlock, + textSummary, + toolWidgetDescriptorMeta, +} from "./tool-surfaces/shared.js"; +import { + WORKSPACE_APP_URI, + toolNames, + workspaceIdDescription, + type ToolContent, + type ToolSurface, +} from "./tool-surfaces/types.js"; type Transport = StreamableHTTPServerTransport; // MCP clients can reconnect without closing the previous transport. Bound stale // session retention so abandoned MCP servers do not accumulate for the life of the process. const MCP_SESSION_IDLE_TIMEOUT_MS = 24 * 60 * 60 * 1_000; const MCP_SESSION_CLEANUP_INTERVAL_MS = 5 * 60 * 1_000; -const WORKSPACE_APP_URI = "ui://devspace/workspace-app.html"; const WORKSPACE_APP_MANIFEST_ENTRY = "workspace-app.html"; -const WRITE_TOOL_ANNOTATIONS = { - readOnlyHint: false, - destructiveHint: true, - idempotentHint: false, - openWorldHint: false, -}; -const EDIT_TOOL_ANNOTATIONS = { - readOnlyHint: false, - destructiveHint: true, - idempotentHint: false, - openWorldHint: false, -}; -const SHELL_TOOL_ANNOTATIONS = { - readOnlyHint: false, - destructiveHint: true, - idempotentHint: false, - openWorldHint: true, -}; interface RunningServer { app: ReturnType; @@ -98,10 +86,6 @@ interface RunningServer { close(): Promise; } -type ToolContent = - | { type: "text"; text: string } - | { type: "image"; data: string; mimeType: string }; - interface WorkspaceAppManifestEntry { file: string; css?: string[]; @@ -110,113 +94,25 @@ interface WorkspaceAppManifestEntry { type WorkspaceAppManifest = Record; -interface DiffStats { - additions: number; - removals: number; -} - -type ToolWidgetKind = - | "workspace" - | "read" - | "write" - | "edit" - | "search" - | "directory" - | "shell" - | "show_changes"; - -interface ToolDefinitionMeta extends Record { - ui: { - resourceUri: string; - visibility: ["model"]; - }; -} - -type EmptyToolDefinitionMeta = Record & { - "ui/resourceUri"?: string; -}; - -interface ToolWidgetDescriptorMeta { - _meta: ToolDefinitionMeta | EmptyToolDefinitionMeta; -} - -function shouldAttachWidget(mode: WidgetMode, kind: ToolWidgetKind): boolean { - switch (mode) { - case "off": - return false; - case "changes": - return kind === "workspace" || kind === "show_changes"; - case "full": - return true; - } -} - -function toolWidgetDescriptorMeta( +function serverInstructions( config: ServerConfig, - kind: ToolWidgetKind, -): ToolWidgetDescriptorMeta { - if (!shouldAttachWidget(config.widgets, kind)) return { _meta: {} }; - - return { - _meta: { - ui: { - resourceUri: WORKSPACE_APP_URI, - visibility: ["model"], - }, - }, - }; -} - -const toolNames = { - openWorkspace: "open_workspace", - read: "read", - write: "write", - edit: "edit", - grep: "grep", - glob: "glob", - ls: "ls", - shell: "bash", -} as const; - -const workspaceIdDescription = - "Workspace to use. Reuse the current project's workspaceId."; - -interface ToolLogFields { - tool: string; - workspaceId?: string; - path?: string; - workingDirectory?: string; - command?: string; - commandLength?: number; - success: boolean; - durationMs: number; - error?: string; -} - -function serverInstructions(config: ServerConfig): string { - const artifactInstruction = config.artifactsEnabled && isArtifactDownloadSupportedPlatform() - ? " When the user supplies or generates a file that is not present on the DevSpace host, use download_artifact with its native file value, the existing workspace ID, and a suitable relative destination path chosen from the user's request and project structure. The tool refuses to overwrite an existing destination and returns the normalized workspace-relative path. Use normal workspace tools when explicit inspection, replacement, movement, renaming, or deletion is needed. Do not recreate binary files with write/edit calls or place signed URLs, native file objects, base64 content, or invented host paths in shell commands or logs." - : ""; + toolSurface: ToolSurface, +): string { + const artifactInstruction = + config.artifactsEnabled && isArtifactDownloadSupportedPlatform() + ? " When the user supplies or generates a file that is not present on the DevSpace host, use download_artifact with its native file value, the existing workspace ID, and a suitable relative destination path chosen from the user's request and project structure. The tool refuses to overwrite an existing destination and returns the normalized workspace-relative path. Use normal workspace tools when explicit inspection, replacement, movement, renaming, or deletion is needed. Do not recreate binary files with write/edit calls or place signed URLs, native file objects, base64 content, or invented host paths in shell commands or logs." + : ""; const showChangesInstruction = config.widgets === "changes" ? " If the turn successfully modifies files by creating, editing, overwriting, deleting, moving, or applying patches, call show_changes exactly once for that workspace after the final related file change and before your final response so the user can inspect the aggregate diff for that turn. Do not call it after every individual file change; do not skip it because individual file-change tools already returned diffs." : ""; - - if (config.toolMode === "codex") { - return `Use DevSpace for coding work. Call ${toolNames.openWorkspace} once for each project folder or isolated worktree, then keep using its workspaceId. During continued work in the same project or worktree, do not call ${toolNames.openWorkspace} again. Open another workspace only when changing projects, switching checkout/worktree mode, creating another isolated worktree, or when the current workspaceId is rejected. Use ${toolNames.read} for direct file reads, apply_patch for all file modifications, exec_command for inspection, tests, builds, and other commands, and write_stdin to poll or interact with running processes. Follow instructions returned by ${toolNames.openWorkspace}; read applicable instruction and skill files before working in their scope.${artifactInstruction}${showChangesInstruction}`; - } - - const inspection = config.toolMode !== "full" - ? `In minimal tool mode, ${toolNames.grep}, ${toolNames.glob}, and ${toolNames.ls} are disabled; use ${toolNames.shell} with command-line tools such as grep, rg, find, ls, and tree for search and directory inspection. ` - : `Prefer ${toolNames.read}, ${toolNames.grep}, ${toolNames.glob}, and ${toolNames.ls} for file inspection. `; - const skills = config.skillsEnabled ? `When ${toolNames.openWorkspace} returns available skills and a task matches a skill, use ${toolNames.read} to read that skill's path before proceeding. Skill paths may be outside the workspace, but ${toolNames.read} only permits advertised SKILL.md files and files under already-loaded skill directories. ` : ""; + const agents = `Follow instructions returned by ${toolNames.openWorkspace}. Before working under a path listed in availableAgentsFiles, use ${toolNames.read} to inspect that instruction file and follow it. `; + const common = `Use DevSpace for coding work. Call ${toolNames.openWorkspace} once for each project folder or isolated worktree, then keep using its workspaceId. During continued work in the same project or worktree, do not call ${toolNames.openWorkspace} again. Open another workspace only when changing projects, switching checkout/worktree mode, creating another isolated worktree, or when the current workspaceId is rejected.`; - const agentsMd = `Follow instructions returned by ${toolNames.openWorkspace}. Before working under a path listed in availableAgentsFiles, use ${toolNames.read} to inspect that instruction file and follow it. `; - - return `Use DevSpace for coding work. Call ${toolNames.openWorkspace} once for each project folder or isolated worktree, then keep using its workspaceId. During continued work in the same project or worktree, do not call ${toolNames.openWorkspace} again. Open another workspace only when changing projects, switching checkout/worktree mode, creating another isolated worktree, or when the current workspaceId is rejected. ${agentsMd}${skills}${inspection}Prefer ${toolNames.edit} for targeted modifications, ${toolNames.write} only for new files or complete rewrites, and ${toolNames.shell} for tests, builds, git inspection, package scripts, and commands that are better executed by the shell. Do not create or modify files with ${toolNames.shell}; avoid shell redirection, heredocs, tee, sed -i, perl -i, node/python/ruby scripts, or any command whose purpose is to write project files.${artifactInstruction}${showChangesInstruction}`; + return `${common} ${toolSurface.instructions({ agents, skills })}${artifactInstruction}${showChangesInstruction}`; } function formatVisibleAgent(agent: { @@ -244,17 +140,6 @@ function formatAvailableAgentProvider(provider: { return `${provider.id}${details ? ` (${details})` : ""}`; } -function resultOutputSchema(extra: z.ZodRawShape = {}): z.ZodRawShape { - return { - result: z - .string() - .describe( - "Model-readable result text for follow-up reasoning and plain MCP hosts.", - ), - ...extra, - }; -} - const workspaceSkillOutputSchema = z.object({ name: z.string(), description: z.string(), @@ -323,105 +208,6 @@ function requestLogFields(req: Request, config: ServerConfig): Record item.type === "text", - ) - .map((item) => item.text) - .join("\n"); -} - -function toolErrorPreview(content: ToolContent[]): string | undefined { - const text = contentText(content).replace(/\s+/g, " ").trim(); - if (!text) return undefined; - return text.length > 240 ? `${text.slice(0, 237)}...` : text; -} - -function logFailedToolResponse( - config: ServerConfig, - fields: Omit, - content: ToolContent[], - startedAt: number, -): void { - logToolCall(config, { - ...fields, - success: false, - durationMs: Math.round(performance.now() - startedAt), - error: toolErrorPreview(content), - }); -} - -function textBlock(text: string): ToolContent { - return { type: "text", text }; -} - -function textSummary(content: ToolContent[]): { - lines: number; - characters: number; -} { - const text = contentText(content); - return { - lines: text.length === 0 ? 0 : text.split("\n").length, - characters: text.length, - }; -} - -function contentLineCount(content: string): number { - if (content.length === 0) return 0; - return content.endsWith("\n") - ? content.slice(0, -1).split("\n").length - : content.split("\n").length; -} - -function countDiffStats(diff: string | undefined): DiffStats { - if (!diff) return { additions: 0, removals: 0 }; - - let additions = 0; - let removals = 0; - - for (const line of diff.split("\n")) { - if (line.startsWith("+") && !line.startsWith("+++")) additions++; - if (line.startsWith("-") && !line.startsWith("---")) removals++; - } - - return { additions, removals }; -} - -function newFilePatch(path: string, content: string): string { - const lines = - content.length === 0 - ? [] - : content.endsWith("\n") - ? content.slice(0, -1).split("\n") - : content.split("\n"); - const hunkLength = lines.length; - const hunkRange = hunkLength === 0 ? "+0,0" : `+1,${hunkLength}`; - const body = lines.map((line) => `+${line}`).join("\n"); - - return [ - `diff --git a/${path} b/${path}`, - "new file mode 100644", - "index 0000000..0000000", - "--- /dev/null", - `+++ b/${path}`, - `@@ -0,0 ${hunkRange} @@`, - body, - ] - .filter((line) => line.length > 0) - .join("\n"); -} - function assetBaseUrl(config: ServerConfig): string { return `${config.publicBaseUrl.replace(/\/+$/, "")}/mcp-app-assets`; } @@ -509,201 +295,6 @@ async function assertWorkspaceAppAssets(): Promise { } } -function processResult(snapshot: ProcessSnapshot): string { - const status = snapshot.running - ? `Process running with session ID ${snapshot.sessionId}.` - : snapshot.signal - ? `Process exited after signal ${snapshot.signal}.` - : `Process exited with code ${snapshot.exitCode ?? "unknown"}.`; - return snapshot.output ? `${snapshot.output.replace(/\n$/, "")}\n${status}` : status; -} - -function processOutputSchema(): z.ZodRawShape { - return resultOutputSchema({ - sessionId: z.number().optional(), - running: z.boolean(), - exitCode: z.number().int().optional(), - signal: z.string().optional(), - wallTimeMs: z.number().nonnegative(), - outputTruncated: z.boolean(), - }); -} - -function processToolResponse( - tool: "exec_command" | "write_stdin", - workspaceId: string, - snapshot: ProcessSnapshot, - summary: Record, -) { - const result = processResult(snapshot); - const content = [textBlock(result)]; - const outputSummary = textSummary(snapshot.output ? [textBlock(snapshot.output)] : []); - return { - content, - _meta: { - tool, - card: { - workspaceId, - summary: { ...summary, ...outputSummary }, - payload: { content }, - }, - }, - structuredContent: { - result, - sessionId: snapshot.sessionId, - running: snapshot.running, - exitCode: snapshot.exitCode, - signal: snapshot.signal, - wallTimeMs: snapshot.wallTimeMs, - outputTruncated: snapshot.outputTruncated, - }, - }; -} - -function registerCodexProcessTools( - server: McpServer, - config: ServerConfig, - workspaces: WorkspaceRegistry, - processSessions: ProcessSessionManager, -): void { - registerAppTool( - server, - "exec_command", - { - title: "Execute command", - description: - "Run a command in a workspace. Returns its result when it exits during the yield window, otherwise returns a sessionId for write_stdin. Use this for file inspection, tests, builds, package scripts, and long-running processes.", - inputSchema: { - workspaceId: z.string().describe(workspaceIdDescription), - cmd: z.string().min(1).describe("Shell command to execute."), - tty: z - .boolean() - .optional() - .describe("Allocate a pseudo-terminal for interactive commands. Defaults to false."), - columns: z.number().int().min(1).max(1_000).optional().describe("Initial PTY width. Defaults to 80."), - rows: z.number().int().min(1).max(1_000).optional().describe("Initial PTY height. Defaults to 24."), - workingDirectory: z - .string() - .optional() - .describe("Working directory relative to the workspace root. Defaults to the workspace root."), - yieldTimeMs: z - .number() - .int() - .min(0) - .max(30_000) - .optional() - .describe("Milliseconds to wait before returning a running session. Defaults to 10000."), - maxOutputTokens: z - .number() - .int() - .positive() - .max(100_000) - .optional() - .describe("Approximate output token budget. Defaults to 10000."), - }, - outputSchema: processOutputSchema(), - ...toolWidgetDescriptorMeta(config, "shell"), - annotations: SHELL_TOOL_ANNOTATIONS, - }, - async ({ workspaceId, cmd, tty, columns, rows, workingDirectory, yieldTimeMs, maxOutputTokens }) => { - const startedAt = performance.now(); - const workspace = workspaces.getWorkspace(workspaceId); - const cwd = workspaces.resolveWorkingDirectory(workspace, workingDirectory); - const snapshot = await processSessions.start({ - workspaceId, - command: cmd, - cwd, - workspaceRoot: workspace.root, - tty, - columns, - rows, - yieldTimeMs, - maxOutputTokens, - }); - - logToolCall(config, { - tool: "exec_command", - workspaceId, - workingDirectory: workingDirectory ?? ".", - command: cmd, - commandLength: cmd.length, - success: true, - durationMs: Math.round(performance.now() - startedAt), - }); - - return processToolResponse("exec_command", workspaceId, snapshot, { - command: cmd, - workingDirectory: workingDirectory ?? ".", - running: snapshot.running, - exitCode: snapshot.exitCode, - wallTimeMs: snapshot.wallTimeMs, - }); - }, - ); - - registerAppTool( - server, - "write_stdin", - { - title: "Write to process", - description: - "Poll or write characters to a process returned by exec_command. Omit chars or pass an empty string to poll. Pass \\u0003 to send Ctrl-C.", - inputSchema: { - workspaceId: z.string().describe("Workspace identifier used to start the process."), - sessionId: z.number().describe("Process session identifier returned by exec_command."), - chars: z.string().optional().describe("Characters to write. Omit or pass an empty string to poll."), - columns: z.number().int().min(1).max(1_000).optional().describe("Resize a PTY to this width."), - rows: z.number().int().min(1).max(1_000).optional().describe("Resize a PTY to this height."), - yieldTimeMs: z - .number() - .int() - .min(0) - .max(30_000) - .optional() - .describe("Milliseconds to wait for process output or completion. Defaults to 10000."), - maxOutputTokens: z - .number() - .int() - .positive() - .max(100_000) - .optional() - .describe("Approximate output token budget. Defaults to 10000."), - }, - outputSchema: processOutputSchema(), - ...toolWidgetDescriptorMeta(config, "shell"), - annotations: SHELL_TOOL_ANNOTATIONS, - }, - async ({ workspaceId, sessionId, chars, columns, rows, yieldTimeMs, maxOutputTokens }) => { - const startedAt = performance.now(); - workspaces.getWorkspace(workspaceId); - const snapshot = await processSessions.write({ - workspaceId, - sessionId, - chars, - columns, - rows, - yieldTimeMs, - maxOutputTokens, - }); - - logToolCall(config, { - tool: "write_stdin", - workspaceId, - success: true, - durationMs: Math.round(performance.now() - startedAt), - }); - - return processToolResponse("write_stdin", workspaceId, snapshot, { - sessionId, - charactersWritten: chars?.length ?? 0, - running: snapshot.running, - exitCode: snapshot.exitCode, - wallTimeMs: snapshot.wallTimeMs, - }); - }, - ); -} - export function createMcpServer( config: ServerConfig, workspaces: WorkspaceRegistry, @@ -712,6 +303,7 @@ export function createMcpServer( resolveLocalAgentProviders: () => LocalAgentProviderStatus[], incomingArtifactAdapters: readonly IncomingArtifactAdapter[], ): McpServer { + const toolSurface = getToolSurface(config.toolMode); const server = new McpServer( { name: "devspace", @@ -721,7 +313,7 @@ export function createMcpServer( "Coding tools for project workspaces. Open each project or worktree once, then reuse its workspaceId.", }, { - instructions: serverInstructions(config), + instructions: serverInstructions(config, toolSurface), }, ); @@ -1055,246 +647,12 @@ export function createMcpServer( }, ); - if (config.toolMode !== "codex") { - registerAppTool( + toolSurface.register({ server, - toolNames.write, - { - title: "Write file", - description: - `Create or completely overwrite a file in a workspace. Prefer ${toolNames.edit} for targeted changes to existing files.`, - inputSchema: { - workspaceId: z - .string() - .describe(workspaceIdDescription), - path: z - .string() - .describe("File path to write, relative to the workspace root."), - content: z.string().describe("Complete new file content."), - }, - outputSchema: resultOutputSchema(), - ...toolWidgetDescriptorMeta(config, "write"), - annotations: WRITE_TOOL_ANNOTATIONS, - }, - async ({ workspaceId, ...input }) => { - const startedAt = performance.now(); - const workspace = workspaces.getWorkspace(workspaceId); - workspaces.resolvePath(workspace, input.path); - const response = await writeFileTool(input, { - cwd: workspace.root, - root: workspace.root, - }); - - if (response.isError) { - logFailedToolResponse(config, { - tool: toolNames.write, - workspaceId, - path: input.path, - }, response.content, startedAt); - return response; - } - - const patch = newFilePatch(input.path, input.content); - const stats = countDiffStats(patch); - const summary = { - ...stats, - lines: contentLineCount(input.content), - characters: input.content.length, - }; - logToolCall(config, { - tool: toolNames.write, - workspaceId, - path: input.path, - success: true, - durationMs: Math.round(performance.now() - startedAt), - }); - - return { - ...response, - _meta: { - tool: toolNames.write, - card: { - workspaceId, - path: input.path, - summary, - payload: { - content: response.content, - patch, - }, - }, - }, - structuredContent: { - result: contentText(response.content), - }, - }; - }, - ); - - registerAppTool( - server, - toolNames.edit, - { - title: "Edit file", - description: - `Edit one file in a workspace by replacing exact text blocks. Prefer this over ${toolNames.write} for targeted changes. Each oldText must match a unique, non-overlapping region of the original file; merge nearby changes into one edit and keep oldText as small as possible while still unique.`, - inputSchema: { - workspaceId: z - .string() - .describe(workspaceIdDescription), - path: z - .string() - .describe("File path to edit, relative to the workspace root."), - edits: z - .array( - z.object({ - oldText: z - .string() - .describe( - "Exact text to replace. Must match uniquely in the original file.", - ), - newText: z.string().describe("Replacement text."), - }), - ) - .min(1), - }, - outputSchema: resultOutputSchema({ - status: z.literal("applied"), - }), - ...toolWidgetDescriptorMeta(config, "edit"), - annotations: EDIT_TOOL_ANNOTATIONS, - }, - async ({ workspaceId, ...input }) => { - const startedAt = performance.now(); - const workspace = workspaces.getWorkspace(workspaceId); - workspaces.resolvePath(workspace, input.path); - const response = await editFileTool(input, { - cwd: workspace.root, - root: workspace.root, - }); - - if (response.isError) { - logFailedToolResponse(config, { - tool: toolNames.edit, - workspaceId, - path: input.path, - }, response.content, startedAt); - return response; - } - - const stats = countDiffStats( - response.details?.patch ?? response.details?.diff, - ); - const summary = { - ...stats, - editCount: input.edits.length, - }; - const editResultText = `Edited ${input.path} (+${stats.additions} -${stats.removals}).`; - const editContent = [textBlock(editResultText)]; - logToolCall(config, { - tool: toolNames.edit, - workspaceId, - path: input.path, - success: true, - durationMs: Math.round(performance.now() - startedAt), - }); - - return { - content: editContent, - _meta: { - tool: toolNames.edit, - card: { - workspaceId, - path: input.path, - summary, - payload: { - diff: response.details?.diff, - patch: response.details?.patch, - }, - }, - }, - structuredContent: { - status: "applied", - result: contentText(editContent), - }, - }; - }, - ); - } - - if (config.toolMode === "codex") { - registerAppTool( - server, - "apply_patch", - { - title: "Apply patch", - description: - "Apply one Codex-style patch in a workspace. Supports adding, overwriting, updating, deleting, and moving files. Use this for all file modifications. Paths must be relative to the workspace.", - inputSchema: { - workspaceId: z - .string() - .describe(workspaceIdDescription), - patch: z - .string() - .describe("Patch text enclosed by *** Begin Patch and *** End Patch markers."), - }, - outputSchema: resultOutputSchema({ - additions: z.number(), - removals: z.number(), - files: z.array( - z.object({ - path: z.string(), - previousPath: z.string().optional(), - operation: z.enum(["add", "update", "delete", "move"]), - }), - ), - }), - ...toolWidgetDescriptorMeta(config, "edit"), - annotations: EDIT_TOOL_ANNOTATIONS, - }, - async ({ workspaceId, patch }) => { - const startedAt = performance.now(); - const workspace = workspaces.getWorkspace(workspaceId); - const applied = await applyPatch(workspace.root, patch); - const paths = applied.files.map((file) => file.path).join(", "); - const result = `Applied patch to ${applied.files.length} file(s): ${paths}`; - const content = [textBlock(result)]; - const displayPath = applied.files.length === 1 - ? applied.files[0]?.path - : `${applied.files.length} files`; - - logToolCall(config, { - tool: "apply_patch", - workspaceId, - success: true, - durationMs: Math.round(performance.now() - startedAt), - }); - - return { - content, - _meta: { - tool: "apply_patch", - card: { - workspaceId, - path: displayPath, - summary: { - files: applied.files.length, - additions: applied.additions, - removals: applied.removals, - }, - files: applied.files, - payload: { patch: applied.patch }, - }, - }, - structuredContent: { - result, - additions: applied.additions, - removals: applied.removals, - files: applied.files, - }, - }; - }, - ); - } + config, + workspaces, + processSessions, + }); if (config.widgets === "changes") { registerAppTool( @@ -1305,9 +663,7 @@ export function createMcpServer( description: "Show the changes made in this turn for an open workspace. Call this once after the final related file change and before your final response so the user can review the combined diff. Do not call it after each individual file change.", inputSchema: { - workspaceId: z - .string() - .describe(workspaceIdDescription), + workspaceId: z.string().describe(workspaceIdDescription), }, outputSchema: resultOutputSchema(), ...toolWidgetDescriptorMeta(config, "show_changes"), @@ -1351,313 +707,6 @@ export function createMcpServer( ); } - if (config.toolMode === "full") { - registerAppTool( - server, - toolNames.grep, - { - title: "Grep", - description: - "Search file contents in a workspace. Use this before broad reads when looking for symbols, text, or usage sites. Respects project ignore rules.", - inputSchema: { - workspaceId: z - .string() - .describe(workspaceIdDescription), - pattern: z.string().describe("Search pattern."), - path: z - .string() - .optional() - .describe( - "Optional path or glob scope relative to the workspace root.", - ), - include: z.string().optional().describe("Optional include glob."), - }, - outputSchema: resultOutputSchema(), - ...toolWidgetDescriptorMeta(config, "search"), - annotations: { readOnlyHint: true }, - }, - async ({ workspaceId, ...input }) => { - const startedAt = performance.now(); - const workspace = workspaces.getWorkspace(workspaceId); - if (input.path) workspaces.resolvePath(workspace, input.path); - const response = await grepFilesTool(input, { - cwd: workspace.root, - root: workspace.root, - }); - - if (response.isError) { - logFailedToolResponse(config, { - tool: toolNames.grep, - workspaceId, - path: input.path, - }, response.content, startedAt); - return response; - } - - const summary = { - pattern: input.pattern, - scope: input.path ?? ".", - ...textSummary(response.content), - }; - logToolCall(config, { - tool: toolNames.grep, - workspaceId, - path: input.path, - success: true, - durationMs: Math.round(performance.now() - startedAt), - }); - - return { - ...response, - _meta: { - tool: toolNames.grep, - card: { - workspaceId, - path: input.path, - summary, - payload: { content: response.content }, - }, - }, - structuredContent: { - result: contentText(response.content), - }, - }; - }, - ); - - registerAppTool( - server, - toolNames.glob, - { - title: "Glob", - description: - "Find files by glob pattern in a workspace. Use this to discover filenames or narrow file sets before reading. Respects project ignore rules.", - inputSchema: { - workspaceId: z - .string() - .describe(workspaceIdDescription), - pattern: z.string().describe("File glob pattern."), - path: z - .string() - .optional() - .describe("Optional path scope relative to the workspace root."), - }, - outputSchema: resultOutputSchema(), - ...toolWidgetDescriptorMeta(config, "search"), - annotations: { readOnlyHint: true }, - }, - async ({ workspaceId, ...input }) => { - const startedAt = performance.now(); - const workspace = workspaces.getWorkspace(workspaceId); - if (input.path) workspaces.resolvePath(workspace, input.path); - const response = await findFilesTool(input, { - cwd: workspace.root, - root: workspace.root, - }); - - if (response.isError) { - logFailedToolResponse(config, { - tool: toolNames.glob, - workspaceId, - path: input.path, - }, response.content, startedAt); - return response; - } - - const summary = { - pattern: input.pattern, - scope: input.path ?? ".", - ...textSummary(response.content), - }; - logToolCall(config, { - tool: toolNames.glob, - workspaceId, - path: input.path, - success: true, - durationMs: Math.round(performance.now() - startedAt), - }); - - return { - ...response, - _meta: { - tool: toolNames.glob, - card: { - workspaceId, - path: input.path, - summary, - payload: { content: response.content }, - }, - }, - structuredContent: { - result: contentText(response.content), - }, - }; - }, - ); - - registerAppTool( - server, - toolNames.ls, - { - title: "Ls", - description: - "List a directory in a workspace. Use this for directory inspection before reading files.", - inputSchema: { - workspaceId: z - .string() - .describe(workspaceIdDescription), - path: z - .string() - .describe( - "Directory path to list, relative to the workspace root.", - ), - }, - outputSchema: resultOutputSchema(), - ...toolWidgetDescriptorMeta(config, "directory"), - annotations: { readOnlyHint: true }, - }, - async ({ workspaceId, ...input }) => { - const startedAt = performance.now(); - const workspace = workspaces.getWorkspace(workspaceId); - workspaces.resolvePath(workspace, input.path); - const response = await listDirectoryTool(input, { - cwd: workspace.root, - root: workspace.root, - }); - - if (response.isError) { - logFailedToolResponse(config, { - tool: toolNames.ls, - workspaceId, - path: input.path, - }, response.content, startedAt); - return response; - } - - const summary = textSummary(response.content); - logToolCall(config, { - tool: toolNames.ls, - workspaceId, - path: input.path, - success: true, - durationMs: Math.round(performance.now() - startedAt), - }); - - return { - ...response, - _meta: { - tool: toolNames.ls, - card: { - workspaceId, - path: input.path, - summary, - payload: { content: response.content }, - }, - }, - structuredContent: { - result: contentText(response.content), - }, - }; - }, - ); - } - - if (config.toolMode !== "codex") { - registerAppTool( - server, - toolNames.shell, - { - title: "Bash", - description: config.toolMode !== "full" - ? `Run a shell command in a workspace. Use only for tests, builds, git inspection, package scripts, search, file discovery, and directory inspection. In minimal tool mode, ${toolNames.grep}, ${toolNames.glob}, and ${toolNames.ls} are disabled; use command-line tools such as grep, rg, find, ls, and tree for those read-only inspection actions. Do not use ${toolNames.shell} to create or modify files. Do not use shell redirection, heredocs, tee, sed -i, perl -i, node/python/ruby scripts, or generated scripts to write project files; use ${toolNames.edit} for targeted changes and ${toolNames.write} for new files or full rewrites. Prefer ${toolNames.read} for direct file reads. This is powerful execution and should only be exposed behind strong authentication.` - : `Run a shell command in a workspace. Use only for tests, builds, git inspection, package scripts, and commands that are better executed by the shell. Do not use ${toolNames.shell} to create or modify files. Do not use shell redirection, heredocs, tee, sed -i, perl -i, node/python/ruby scripts, or generated scripts to write project files; use ${toolNames.edit} for targeted changes and ${toolNames.write} for new files or full rewrites. Prefer ${toolNames.read}, ${toolNames.grep}, ${toolNames.glob}, and ${toolNames.ls} for file inspection. This is powerful execution and should only be exposed behind strong authentication.`, - inputSchema: { - workspaceId: z - .string() - .describe(workspaceIdDescription), - command: z - .string() - .describe( - `Shell command to run. Must not create or modify project files; use ${toolNames.edit} or ${toolNames.write} for file changes.`, - ), - workingDirectory: z - .string() - .optional() - .describe( - "Optional working directory relative to the workspace root. Defaults to the workspace root.", - ), - timeout: z - .number() - .positive() - .max(300) - .optional() - .describe("Timeout in seconds. Defaults to 30, max 300."), - }, - outputSchema: resultOutputSchema(), - ...toolWidgetDescriptorMeta(config, "shell"), - annotations: SHELL_TOOL_ANNOTATIONS, - }, - async ({ workspaceId, workingDirectory, ...input }) => { - const startedAt = performance.now(); - const workspace = workspaces.getWorkspace(workspaceId); - const cwd = workspaces.resolveWorkingDirectory( - workspace, - workingDirectory, - ); - const response = await runShellTool(input, { - cwd, - root: workspace.root, - }); - - if (response.isError) { - logFailedToolResponse(config, { - tool: toolNames.shell, - workspaceId, - workingDirectory: workingDirectory ?? ".", - command: input.command, - commandLength: input.command.length, - }, response.content, startedAt); - return response; - } - - const summary = { - command: input.command, - workingDirectory: workingDirectory ?? ".", - ...textSummary(response.content), - }; - logToolCall(config, { - tool: toolNames.shell, - workspaceId, - workingDirectory: workingDirectory ?? ".", - command: input.command, - commandLength: input.command.length, - success: true, - durationMs: Math.round(performance.now() - startedAt), - }); - - return { - ...response, - _meta: { - tool: toolNames.shell, - card: { - workspaceId, - path: workingDirectory, - summary, - payload: { content: response.content }, - }, - }, - structuredContent: { - result: contentText(response.content), - }, - }; - }, - ); - } - - if (config.toolMode === "codex") { - registerCodexProcessTools(server, config, workspaces, processSessions); - } - if (config.artifactsEnabled && isArtifactDownloadSupportedPlatform()) { registerArtifactTools(server, { config, diff --git a/src/tool-surfaces/index.ts b/src/tool-surfaces/index.ts new file mode 100644 index 000000000..64bc13d29 --- /dev/null +++ b/src/tool-surfaces/index.ts @@ -0,0 +1,40 @@ +import type { ToolMode } from "../config.js"; +import { registerCodexTools } from "./codex.js"; +import { registerStandardTools } from "./standard.js"; +import { + toolNames, + type ToolInstructionContext, + type ToolSurface, +} from "./types.js"; + +const MINIMAL_INSPECTION = `In minimal tool mode, ${toolNames.grep}, ${toolNames.glob}, and ${toolNames.ls} are disabled; use ${toolNames.shell} with command-line tools such as grep, rg, find, ls, and tree for search and directory inspection. `; + +const FULL_INSPECTION = `Prefer ${toolNames.read}, ${toolNames.grep}, ${toolNames.glob}, and ${toolNames.ls} for file inspection. `; + +const STANDARD_EDITING = `Prefer ${toolNames.edit} for targeted modifications, ${toolNames.write} only for new files or complete rewrites, and ${toolNames.shell} for tests, builds, git inspection, package scripts, and commands that are better executed by the shell. Do not create or modify files with ${toolNames.shell}; avoid shell redirection, heredocs, tee, sed -i, perl -i, node/python/ruby scripts, or any command whose purpose is to write project files.`; + +const CODEX_INSTRUCTIONS = `Use ${toolNames.read} for direct file reads, apply_patch for all file modifications, exec_command for inspection, tests, builds, and other commands, and write_stdin to poll or interact with running processes. Follow instructions returned by ${toolNames.openWorkspace}; read applicable instruction and skill files before working in their scope.`; + +const TOOL_SURFACES: Record = { + minimal: { + register: (context) => registerStandardTools(context, "minimal"), + instructions: standardInstructions(MINIMAL_INSPECTION), + }, + full: { + register: (context) => registerStandardTools(context, "full"), + instructions: standardInstructions(FULL_INSPECTION), + }, + codex: { + register: registerCodexTools, + instructions: () => CODEX_INSTRUCTIONS, + }, +}; + +export function getToolSurface(mode: ToolMode): ToolSurface { + return TOOL_SURFACES[mode]; +} + +function standardInstructions(inspection: string) { + return ({ agents, skills }: ToolInstructionContext): string => + `${agents}${skills}${inspection}${STANDARD_EDITING}`; +} From 8b9cff2e18de8ae116d92d1e1c5477cf8b0b3946 Mon Sep 17 00:00:00 2001 From: Waishnav <86405648+Waishnav@users.noreply.github.com> Date: Mon, 24 Aug 2026 02:01:08 +0530 Subject: [PATCH 07/75] fix(tools): log Codex operation failures --- src/tool-surfaces/codex.ts | 107 ++++++++++++++++++------------------ src/tool-surfaces/shared.ts | 25 +++++++++ 2 files changed, 80 insertions(+), 52 deletions(-) diff --git a/src/tool-surfaces/codex.ts b/src/tool-surfaces/codex.ts index 89153743e..9f8bbbe8b 100644 --- a/src/tool-surfaces/codex.ts +++ b/src/tool-surfaces/codex.ts @@ -10,8 +10,8 @@ import { } from "./types.js"; import { contentText, - logToolCall, resultOutputSchema, + runLoggedToolOperation, textBlock, textSummary, toolWidgetDescriptorMeta, @@ -119,8 +119,15 @@ function registerApplyPatchTool(context: ToolRegistrationContext): void { }, async ({ workspaceId, patch }) => { const startedAt = performance.now(); - const workspace = workspaces.getWorkspace(workspaceId); - const applied = await applyPatch(workspace.root, patch); + const applied = await runLoggedToolOperation( + config, + { tool: "apply_patch", workspaceId }, + startedAt, + async () => { + const workspace = workspaces.getWorkspace(workspaceId); + return applyPatch(workspace.root, patch); + }, + ); const paths = applied.files.map((file) => file.path).join(", "); const result = `Applied patch to ${applied.files.length} file(s): ${paths}`; const content = [textBlock(result)]; @@ -129,13 +136,6 @@ function registerApplyPatchTool(context: ToolRegistrationContext): void { ? applied.files[0]?.path : `${applied.files.length} files`; - logToolCall(config, { - tool: "apply_patch", - workspaceId, - success: true, - durationMs: Math.round(performance.now() - startedAt), - }); - return { content, _meta: { @@ -234,32 +234,35 @@ function registerCodexProcessTools(context: ToolRegistrationContext): void { maxOutputTokens, }) => { const startedAt = performance.now(); - const workspace = workspaces.getWorkspace(workspaceId); - const cwd = workspaces.resolveWorkingDirectory( - workspace, - workingDirectory, + const snapshot = await runLoggedToolOperation( + config, + { + tool: "exec_command", + workspaceId, + workingDirectory: workingDirectory ?? ".", + command: cmd, + commandLength: cmd.length, + }, + startedAt, + async () => { + const workspace = workspaces.getWorkspace(workspaceId); + const cwd = workspaces.resolveWorkingDirectory( + workspace, + workingDirectory, + ); + return processSessions.start({ + workspaceId, + command: cmd, + cwd, + workspaceRoot: workspace.root, + tty, + columns, + rows, + yieldTimeMs, + maxOutputTokens, + }); + }, ); - const snapshot = await processSessions.start({ - workspaceId, - command: cmd, - cwd, - workspaceRoot: workspace.root, - tty, - columns, - rows, - yieldTimeMs, - maxOutputTokens, - }); - - logToolCall(config, { - tool: "exec_command", - workspaceId, - workingDirectory: workingDirectory ?? ".", - command: cmd, - commandLength: cmd.length, - success: true, - durationMs: Math.round(performance.now() - startedAt), - }); return processToolResponse("exec_command", workspaceId, snapshot, { command: cmd, @@ -336,23 +339,23 @@ function registerCodexProcessTools(context: ToolRegistrationContext): void { maxOutputTokens, }) => { const startedAt = performance.now(); - workspaces.getWorkspace(workspaceId); - const snapshot = await processSessions.write({ - workspaceId, - sessionId, - chars, - columns, - rows, - yieldTimeMs, - maxOutputTokens, - }); - - logToolCall(config, { - tool: "write_stdin", - workspaceId, - success: true, - durationMs: Math.round(performance.now() - startedAt), - }); + const snapshot = await runLoggedToolOperation( + config, + { tool: "write_stdin", workspaceId }, + startedAt, + async () => { + workspaces.getWorkspace(workspaceId); + return processSessions.write({ + workspaceId, + sessionId, + chars, + columns, + rows, + yieldTimeMs, + maxOutputTokens, + }); + }, + ); return processToolResponse("write_stdin", workspaceId, snapshot, { sessionId, diff --git a/src/tool-surfaces/shared.ts b/src/tool-surfaces/shared.ts index 2e617af06..b9db4f263 100644 --- a/src/tool-surfaces/shared.ts +++ b/src/tool-surfaces/shared.ts @@ -50,6 +50,31 @@ export function logToolCall(config: ServerConfig, fields: ToolLogFields): void { }); } +export async function runLoggedToolOperation( + config: ServerConfig, + fields: Omit, + startedAt: number, + operation: () => Promise, +): Promise { + try { + const result = await operation(); + logToolCall(config, { + ...fields, + success: true, + durationMs: Math.round(performance.now() - startedAt), + }); + return result; + } catch (error) { + logToolCall(config, { + ...fields, + success: false, + durationMs: Math.round(performance.now() - startedAt), + error: error instanceof Error ? error.message : String(error), + }); + throw error; + } +} + export function contentText(content: ToolContent[]): string { return content .filter( From f60d7a8b073e56740e6a5fdf45544ad234635f83 Mon Sep 17 00:00:00 2001 From: Waishnav <86405648+Waishnav@users.noreply.github.com> Date: Mon, 24 Aug 2026 02:01:57 +0530 Subject: [PATCH 08/75] refactor(tools): keep instructions with adapters --- src/tool-surfaces/codex.ts | 7 +++++++ src/tool-surfaces/index.ts | 29 ++++++----------------------- src/tool-surfaces/standard.ts | 13 +++++++++++++ 3 files changed, 26 insertions(+), 23 deletions(-) diff --git a/src/tool-surfaces/codex.ts b/src/tool-surfaces/codex.ts index 9f8bbbe8b..42d2ae12b 100644 --- a/src/tool-surfaces/codex.ts +++ b/src/tool-surfaces/codex.ts @@ -5,6 +5,7 @@ import type { ProcessSnapshot } from "../process-sessions.js"; import { EDIT_TOOL_ANNOTATIONS, SHELL_TOOL_ANNOTATIONS, + toolNames, workspaceIdDescription, type ToolRegistrationContext, } from "./types.js"; @@ -19,6 +20,12 @@ import { type CodexRegistration = (context: ToolRegistrationContext) => void; +const CODEX_INSTRUCTIONS = `Use ${toolNames.read} for direct file reads, apply_patch for all file modifications, exec_command for inspection, tests, builds, and other commands, and write_stdin to poll or interact with running processes. Follow instructions returned by ${toolNames.openWorkspace}; read applicable instruction and skill files before working in their scope.`; + +export function codexInstructions(): string { + return CODEX_INSTRUCTIONS; +} + export function registerCodexTools(context: ToolRegistrationContext): void { for (const register of CODEX_REGISTRATIONS) { register(context); diff --git a/src/tool-surfaces/index.ts b/src/tool-surfaces/index.ts index 64bc13d29..b21140977 100644 --- a/src/tool-surfaces/index.ts +++ b/src/tool-surfaces/index.ts @@ -1,40 +1,23 @@ import type { ToolMode } from "../config.js"; -import { registerCodexTools } from "./codex.js"; -import { registerStandardTools } from "./standard.js"; -import { - toolNames, - type ToolInstructionContext, - type ToolSurface, -} from "./types.js"; - -const MINIMAL_INSPECTION = `In minimal tool mode, ${toolNames.grep}, ${toolNames.glob}, and ${toolNames.ls} are disabled; use ${toolNames.shell} with command-line tools such as grep, rg, find, ls, and tree for search and directory inspection. `; - -const FULL_INSPECTION = `Prefer ${toolNames.read}, ${toolNames.grep}, ${toolNames.glob}, and ${toolNames.ls} for file inspection. `; - -const STANDARD_EDITING = `Prefer ${toolNames.edit} for targeted modifications, ${toolNames.write} only for new files or complete rewrites, and ${toolNames.shell} for tests, builds, git inspection, package scripts, and commands that are better executed by the shell. Do not create or modify files with ${toolNames.shell}; avoid shell redirection, heredocs, tee, sed -i, perl -i, node/python/ruby scripts, or any command whose purpose is to write project files.`; - -const CODEX_INSTRUCTIONS = `Use ${toolNames.read} for direct file reads, apply_patch for all file modifications, exec_command for inspection, tests, builds, and other commands, and write_stdin to poll or interact with running processes. Follow instructions returned by ${toolNames.openWorkspace}; read applicable instruction and skill files before working in their scope.`; +import { codexInstructions, registerCodexTools } from "./codex.js"; +import { registerStandardTools, standardInstructions } from "./standard.js"; +import { type ToolSurface } from "./types.js"; const TOOL_SURFACES: Record = { minimal: { register: (context) => registerStandardTools(context, "minimal"), - instructions: standardInstructions(MINIMAL_INSPECTION), + instructions: standardInstructions("minimal"), }, full: { register: (context) => registerStandardTools(context, "full"), - instructions: standardInstructions(FULL_INSPECTION), + instructions: standardInstructions("full"), }, codex: { register: registerCodexTools, - instructions: () => CODEX_INSTRUCTIONS, + instructions: codexInstructions, }, }; export function getToolSurface(mode: ToolMode): ToolSurface { return TOOL_SURFACES[mode]; } - -function standardInstructions(inspection: string) { - return ({ agents, skills }: ToolInstructionContext): string => - `${agents}${skills}${inspection}${STANDARD_EDITING}`; -} diff --git a/src/tool-surfaces/standard.ts b/src/tool-surfaces/standard.ts index 8c24d3cef..e05bdade1 100644 --- a/src/tool-surfaces/standard.ts +++ b/src/tool-surfaces/standard.ts @@ -14,6 +14,7 @@ import { WRITE_TOOL_ANNOTATIONS, toolNames, workspaceIdDescription, + type ToolInstructionContext, type ToolRegistrationContext, } from "./types.js"; import { @@ -31,6 +32,18 @@ import { type StandardRegistration = (context: ToolRegistrationContext) => void; +const MINIMAL_INSPECTION = `In minimal tool mode, ${toolNames.grep}, ${toolNames.glob}, and ${toolNames.ls} are disabled; use ${toolNames.shell} with command-line tools such as grep, rg, find, ls, and tree for search and directory inspection. `; + +const FULL_INSPECTION = `Prefer ${toolNames.read}, ${toolNames.grep}, ${toolNames.glob}, and ${toolNames.ls} for file inspection. `; + +const STANDARD_EDITING = `Prefer ${toolNames.edit} for targeted modifications, ${toolNames.write} only for new files or complete rewrites, and ${toolNames.shell} for tests, builds, git inspection, package scripts, and commands that are better executed by the shell. Do not create or modify files with ${toolNames.shell}; avoid shell redirection, heredocs, tee, sed -i, perl -i, node/python/ruby scripts, or any command whose purpose is to write project files.`; + +export function standardInstructions(mode: "minimal" | "full") { + const inspection = mode === "minimal" ? MINIMAL_INSPECTION : FULL_INSPECTION; + return ({ agents, skills }: ToolInstructionContext): string => + `${agents}${skills}${inspection}${STANDARD_EDITING}`; +} + export function registerStandardTools( context: ToolRegistrationContext, mode: "minimal" | "full", From 5975c4a632784a2b784609916c7840cf3290a2d2 Mon Sep 17 00:00:00 2001 From: Waishnav <86405648+Waishnav@users.noreply.github.com> Date: Mon, 24 Aug 2026 02:02:17 +0530 Subject: [PATCH 09/75] fix(ui): count trailing newlines consistently --- src/tool-surfaces/shared.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tool-surfaces/shared.ts b/src/tool-surfaces/shared.ts index b9db4f263..c2bb22bda 100644 --- a/src/tool-surfaces/shared.ts +++ b/src/tool-surfaces/shared.ts @@ -114,7 +114,7 @@ export function textSummary(content: ToolContent[]): { } { const text = contentText(content); return { - lines: text.length === 0 ? 0 : text.split("\n").length, + lines: contentLineCount(text), characters: text.length, }; } From b1599dfcfb85604771804de7fb712b02717bbeae Mon Sep 17 00:00:00 2001 From: Waishnav <86405648+Waishnav@users.noreply.github.com> Date: Mon, 24 Aug 2026 02:02:42 +0530 Subject: [PATCH 10/75] fix(ui): avoid false overwrite patches --- src/tool-surfaces/standard.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/tool-surfaces/standard.ts b/src/tool-surfaces/standard.ts index e05bdade1..f18f29eae 100644 --- a/src/tool-surfaces/standard.ts +++ b/src/tool-surfaces/standard.ts @@ -1,4 +1,5 @@ import { registerAppTool } from "@modelcontextprotocol/ext-apps/server"; +import { existsSync } from "node:fs"; import * as z from "zod/v4"; import { editFileTool, @@ -99,7 +100,8 @@ function registerStandardMutationTools(context: ToolRegistrationContext): void { async ({ workspaceId, ...input }) => { const startedAt = performance.now(); const workspace = workspaces.getWorkspace(workspaceId); - workspaces.resolvePath(workspace, input.path); + const absolutePath = workspaces.resolvePath(workspace, input.path); + const overwritesExistingFile = existsSync(absolutePath); const response = await writeFileTool(input, { cwd: workspace.root, root: workspace.root, @@ -119,7 +121,11 @@ function registerStandardMutationTools(context: ToolRegistrationContext): void { return response; } - const patch = newFilePatch(input.path, input.content); + // An aggregate review can show the real replacement diff. A new-file + // patch would misrepresent an overwrite as additions with no removals. + const patch = overwritesExistingFile + ? undefined + : newFilePatch(input.path, input.content); const stats = countDiffStats(patch); const summary = { ...stats, From ef39f36112d0c91197750d3e2f1dd98c9e694b59 Mon Sep 17 00:00:00 2001 From: Waishnav <86405648+Waishnav@users.noreply.github.com> Date: Mon, 24 Aug 2026 01:10:46 +0530 Subject: [PATCH 11/75] feat(config): replace tool modes with claude and codex --- src/config.test.ts | 14 +++----------- src/config.ts | 15 ++------------- src/server.test.ts | 17 ++++++++--------- src/user-config.ts | 3 +++ 4 files changed, 16 insertions(+), 33 deletions(-) diff --git a/src/config.test.ts b/src/config.test.ts index 7b3eeeb6a..e7478ca99 100644 --- a/src/config.test.ts +++ b/src/config.test.ts @@ -15,12 +15,7 @@ assert.equal(loadConfig(baseEnv).widgets, "full"); assert.equal(loadConfig({ ...baseEnv, DEVSPACE_WIDGETS: "changes" }).widgets, "changes"); assert.equal(loadConfig({ ...baseEnv, DEVSPACE_WIDGETS: "full" }).widgets, "full"); assert.equal(loadConfig({ ...baseEnv, DEVSPACE_WIDGETS: "off" }).widgets, "off"); -assert.equal(loadConfig(baseEnv).toolMode, "minimal"); -assert.equal(loadConfig({ ...baseEnv, DEVSPACE_TOOL_MODE: "minimal" }).toolMode, "minimal"); -assert.equal(loadConfig({ ...baseEnv, DEVSPACE_TOOL_MODE: "full" }).toolMode, "full"); -assert.equal(loadConfig({ ...baseEnv, DEVSPACE_TOOL_MODE: "codex" }).toolMode, "codex"); -assert.equal(loadConfig({ ...baseEnv, DEVSPACE_MINIMAL_TOOLS: "0" }).toolMode, "full"); -assert.equal(loadConfig({ ...baseEnv, DEVSPACE_MINIMAL_TOOLS: "1" }).toolMode, "minimal"); +assert.equal(loadConfig(baseEnv).toolMode, "codex"); assert.equal(loadConfig(baseEnv).skillsEnabled, true); assert.equal(loadConfig(baseEnv).devspaceSkillsDir, join(emptyConfigDir, "skills")); assert.equal(loadConfig(baseEnv).devspaceAgentsDir, join(emptyConfigDir, "agents")); @@ -50,11 +45,6 @@ assert.throws( () => loadConfig({ ...baseEnv, DEVSPACE_WIDGETS: "write-only" }), /Invalid DEVSPACE_WIDGETS: write-only/, ); -assert.throws( - () => loadConfig({ ...baseEnv, DEVSPACE_TOOL_MODE: "invalid" }), - /Invalid DEVSPACE_TOOL_MODE: invalid/, -); - assert.deepEqual(loadConfig(baseEnv).logging, { level: "info", format: "json", @@ -163,6 +153,7 @@ writeFileSync( subagents: true, artifactsEnabled: true, artifactMaxFileBytes: 321, + tools: { mode: "claude" }, }), ); writeFileSync( @@ -180,6 +171,7 @@ assert.equal(fileConfig.subagents.enabled, true); assert.equal(fileConfig.subagents.providers.length, 7); assert.equal(fileConfig.artifactsEnabled, true); assert.equal(fileConfig.artifactMaxFileBytes, 321); +assert.equal(fileConfig.toolMode, "claude"); assert.deepEqual(fileConfig.allowedHosts, [ "localhost", "127.0.0.1", diff --git a/src/config.ts b/src/config.ts index 54a131c9a..bd8f47a84 100644 --- a/src/config.ts +++ b/src/config.ts @@ -6,7 +6,7 @@ import type { OAuthConfig } from "./oauth-provider.js"; import { devspaceAgentsDir, devspaceSkillsDir, loadDevspaceFiles } from "./user-config.js"; import { resolveSubagentsConfig, type SubagentsConfig } from "./local-agent-config.js"; -export type ToolMode = "minimal" | "full" | "codex"; +export type ToolMode = "claude" | "codex"; export type WidgetMode = "off" | "changes" | "full"; const DEFAULT_OAUTH_ACCESS_TOKEN_TTL_SECONDS = 60 * 60; const DEFAULT_OAUTH_REFRESH_TOKEN_TTL_SECONDS = 30 * 24 * 60 * 60; @@ -85,17 +85,6 @@ function parseBoolean(value: string | undefined): boolean { return ["1", "true", "yes", "on"].includes(value?.toLowerCase() ?? ""); } -function parseToolMode(env: NodeJS.ProcessEnv): ToolMode { - const mode = env.DEVSPACE_TOOL_MODE; - if (mode === "minimal" || mode === "full" || mode === "codex") return mode; - if (mode) throw new Error(`Invalid DEVSPACE_TOOL_MODE: ${mode}`); - - if (env.DEVSPACE_MINIMAL_TOOLS !== undefined) { - return parseBoolean(env.DEVSPACE_MINIMAL_TOOLS) ? "minimal" : "full"; - } - return "minimal"; -} - function parseLogLevel(value: string | undefined): LogLevel { if (!value || value === "info") return "info"; if (["silent", "error", "warn", "debug"].includes(value)) return value as LogLevel; @@ -231,7 +220,7 @@ export function loadConfig(env: NodeJS.ProcessEnv = process.env): ServerConfig { allowedRoots: parseAllowedRoots(env.DEVSPACE_ALLOWED_ROOTS ?? files.config.allowedRoots), allowedHosts: parseAllowedHosts(env.DEVSPACE_ALLOWED_HOSTS, derivedAllowedHosts), publicBaseUrl, - toolMode: parseToolMode(env), + toolMode: files.config.tools?.mode ?? "codex", widgets: parseWidgetMode(env.DEVSPACE_WIDGETS), stateDir: resolve(expandHomePath(env.DEVSPACE_STATE_DIR ?? files.config.stateDir ?? defaultStateDir())), worktreeRoot: resolve(expandHomePath(env.DEVSPACE_WORKTREE_ROOT ?? files.config.worktreeRoot ?? defaultWorktreeRoot())), diff --git a/src/server.test.ts b/src/server.test.ts index ab7010cac..592cbda26 100644 --- a/src/server.test.ts +++ b/src/server.test.ts @@ -25,13 +25,9 @@ test("tool modes expose the expected host-facing tool surface", async (t) => { expected: string[]; }> = [ { - mode: "minimal", + mode: "claude", expected: ["open_workspace", "read", "write", "edit", "bash"], }, - { - mode: "full", - expected: ["open_workspace", "read", "write", "edit", "bash", "grep", "glob", "ls"], - }, { mode: "codex", expected: ["open_workspace", "read", "apply_patch", "exec_command", "write_stdin"], @@ -64,7 +60,7 @@ test("widget modes compose independently from tool modes", async (t) => { for (const { widgets, showChanges, workspaceCard } of cases) { await t.test(widgets, async (nested) => { - const context = await fixture(nested, { toolMode: "full", widgets }); + const context = await fixture(nested, { toolMode: "claude", widgets }); const tools = await context.client.listTools(); const workspace = tools.tools.find((tool) => tool.name === "open_workspace"); const changes = tools.tools.find((tool) => tool.name === "show_changes"); @@ -344,14 +340,17 @@ async function fixture( DEVSPACE_WORKTREE_ROOT: join(root, ".worktrees"), DEVSPACE_AGENT_DIR: agentDir, DEVSPACE_WIDGETS: options.widgets ?? "full", - DEVSPACE_TOOL_MODE: options.toolMode ?? "full", DEVSPACE_SUBAGENTS: options.localAgentProviders ? "1" : "0", DEVSPACE_OAUTH_OWNER_TOKEN: "test-owner-token-that-is-long-enough", PORT: "1", }); + const modeConfig: ServerConfig = { + ...loadedConfig, + toolMode: options.toolMode ?? loadedConfig.toolMode, + }; const config: ServerConfig = options.localAgentProviders ? { - ...loadedConfig, + ...modeConfig, subagents: options.subagents ?? { enabled: true, providers: initialProviderAvailability.map((provider) => ({ @@ -360,7 +359,7 @@ async function fixture( })), }, } - : loadedConfig; + : modeConfig; const resolveProviderAvailability: () => LocalAgentProviderAvailability[] = typeof options.localAgentProviders === "function" ? options.localAgentProviders diff --git a/src/user-config.ts b/src/user-config.ts index 506b468c9..203bc7c37 100644 --- a/src/user-config.ts +++ b/src/user-config.ts @@ -23,6 +23,9 @@ const devspaceUserConfigSchema = z.object({ artifactMaxFileBytes: z.number().optional(), agentDir: z.string().optional(), subagents: storedSubagentsConfigSchema.optional(), + tools: z.object({ + mode: z.enum(["claude", "codex"]).optional(), + }).strict().optional(), }).passthrough(); const devspaceAuthConfigSchema = z.object({ From 954b44b56927356e424290526a74867af43fd5c5 Mon Sep 17 00:00:00 2001 From: Waishnav <86405648+Waishnav@users.noreply.github.com> Date: Mon, 24 Aug 2026 01:10:46 +0530 Subject: [PATCH 12/75] refactor(tools): remove dedicated search tools --- src/pi-tools.ts | 27 -- src/tool-surfaces/claude.ts | 317 +++++++++++++++++++ src/tool-surfaces/index.ts | 12 +- src/tool-surfaces/standard.ts | 573 ---------------------------------- src/tool-surfaces/types.ts | 5 - 5 files changed, 321 insertions(+), 613 deletions(-) create mode 100644 src/tool-surfaces/claude.ts delete mode 100644 src/tool-surfaces/standard.ts diff --git a/src/pi-tools.ts b/src/pi-tools.ts index 238b9c547..06f821976 100644 --- a/src/pi-tools.ts +++ b/src/pi-tools.ts @@ -1,17 +1,11 @@ import { createBashTool, createEditTool, - createFindTool, - createGrepTool, - createLsTool, createReadTool, createWriteTool, type BashToolInput, type EditToolInput, type EditToolDetails, - type FindToolInput, - type GrepToolInput, - type LsToolInput, type ReadToolInput, type WriteToolInput, type AgentToolResult, @@ -97,27 +91,6 @@ export async function editFileTool(input: EditToolInput, context: ToolContext): }, context); } -export async function grepFilesTool(input: GrepToolInput, context: ToolContext): Promise { - if (input.path) resolveAllowedPath(input.path, context.cwd, [context.root]); - const tool = createGrepTool(context.cwd); - - return runTool((params) => tool.execute("grep_files", params), input, context); -} - -export async function findFilesTool(input: FindToolInput, context: ToolContext): Promise { - if (input.path) resolveAllowedPath(input.path, context.cwd, [context.root]); - const tool = createFindTool(context.cwd); - - return runTool((params) => tool.execute("find_files", params), input, context); -} - -export async function listDirectoryTool(input: LsToolInput, context: ToolContext): Promise { - if (input.path) resolveAllowedPath(input.path, context.cwd, [context.root]); - const tool = createLsTool(context.cwd); - - return runTool((params) => tool.execute("list_directory", params), input, context); -} - export async function runShellTool(input: BashToolInput, context: ToolContext): Promise { const tool = createBashTool(context.cwd); const timeout = input.timeout === undefined ? 30 : Math.min(input.timeout, 300); diff --git a/src/tool-surfaces/claude.ts b/src/tool-surfaces/claude.ts new file mode 100644 index 000000000..eadd566a1 --- /dev/null +++ b/src/tool-surfaces/claude.ts @@ -0,0 +1,317 @@ +import { registerAppTool } from "@modelcontextprotocol/ext-apps/server"; +import { existsSync } from "node:fs"; +import * as z from "zod/v4"; +import { + editFileTool, + runShellTool, + writeFileTool, +} from "../pi-tools.js"; +import { + EDIT_TOOL_ANNOTATIONS, + SHELL_TOOL_ANNOTATIONS, + WRITE_TOOL_ANNOTATIONS, + toolNames, + workspaceIdDescription, + type ToolInstructionContext, + type ToolRegistrationContext, +} from "./types.js"; +import { + contentLineCount, + contentText, + countDiffStats, + logFailedToolResponse, + logToolCall, + newFilePatch, + resultOutputSchema, + textBlock, + textSummary, + toolWidgetDescriptorMeta, +} from "./shared.js"; + +const CLAUDE_INSTRUCTIONS = `Use ${toolNames.read} for direct file reads, ${toolNames.shell} with command-line tools such as rg, find, ls, and tree for search and directory inspection, ${toolNames.edit} for targeted modifications, and ${toolNames.write} only for new files or complete rewrites. Use ${toolNames.shell} for tests, builds, git inspection, package scripts, and other commands, but do not create or modify files through shell commands. Follow instructions returned by ${toolNames.openWorkspace}; read applicable instruction and skill files before working in their scope.`; + +export function claudeInstructions({ + agents, + skills, +}: ToolInstructionContext): string { + return `${agents}${skills}${CLAUDE_INSTRUCTIONS}`; +} + +export function registerClaudeTools(context: ToolRegistrationContext): void { + registerClaudeMutationTools(context); + registerShellTool(context); +} + +const CLAUDE_SHELL_DESCRIPTION = `Run a shell command in a workspace. Use it for tests, builds, git inspection, package scripts, search, file discovery, and directory inspection. Do not use ${toolNames.shell} to create or modify files. Do not use shell redirection, heredocs, tee, sed -i, perl -i, node/python/ruby scripts, or generated scripts to write project files; use ${toolNames.edit} for targeted changes and ${toolNames.write} for new files or full rewrites. Prefer ${toolNames.read} for direct file reads. This is powerful execution and should only be exposed behind strong authentication.`; + +function registerClaudeMutationTools(context: ToolRegistrationContext): void { + const { server, config, workspaces } = context; + + registerAppTool( + server, + toolNames.write, + { + title: "Write file", + description: `Create or completely overwrite a file in a workspace. Prefer ${toolNames.edit} for targeted changes to existing files.`, + inputSchema: { + workspaceId: z.string().describe(workspaceIdDescription), + path: z + .string() + .describe("File path to write, relative to the workspace root."), + content: z.string().describe("Complete new file content."), + }, + outputSchema: resultOutputSchema(), + ...toolWidgetDescriptorMeta(config, "write"), + annotations: WRITE_TOOL_ANNOTATIONS, + }, + async ({ workspaceId, ...input }) => { + const startedAt = performance.now(); + const workspace = workspaces.getWorkspace(workspaceId); + const absolutePath = workspaces.resolvePath(workspace, input.path); + const overwritesExistingFile = existsSync(absolutePath); + const response = await writeFileTool(input, { + cwd: workspace.root, + root: workspace.root, + }); + + if (response.isError) { + logFailedToolResponse( + config, + { + tool: toolNames.write, + workspaceId, + path: input.path, + }, + response.content, + startedAt, + ); + return response; + } + + // An aggregate review can show the real replacement diff. A new-file + // patch would misrepresent an overwrite as additions with no removals. + const patch = overwritesExistingFile + ? undefined + : newFilePatch(input.path, input.content); + const stats = countDiffStats(patch); + const summary = { + ...stats, + lines: contentLineCount(input.content), + characters: input.content.length, + }; + logToolCall(config, { + tool: toolNames.write, + workspaceId, + path: input.path, + success: true, + durationMs: Math.round(performance.now() - startedAt), + }); + + return { + ...response, + _meta: { + tool: toolNames.write, + card: { + workspaceId, + path: input.path, + summary, + payload: { + content: response.content, + patch, + }, + }, + }, + structuredContent: { + result: contentText(response.content), + }, + }; + }, + ); + + registerAppTool( + server, + toolNames.edit, + { + title: "Edit file", + description: `Edit one file in a workspace by replacing exact text blocks. Prefer this over ${toolNames.write} for targeted changes. Each oldText must match a unique, non-overlapping region of the original file; merge nearby changes into one edit and keep oldText as small as possible while still unique.`, + inputSchema: { + workspaceId: z.string().describe(workspaceIdDescription), + path: z + .string() + .describe("File path to edit, relative to the workspace root."), + edits: z + .array( + z.object({ + oldText: z + .string() + .describe( + "Exact text to replace. Must match uniquely in the original file.", + ), + newText: z.string().describe("Replacement text."), + }), + ) + .min(1), + }, + outputSchema: resultOutputSchema({ + status: z.literal("applied"), + }), + ...toolWidgetDescriptorMeta(config, "edit"), + annotations: EDIT_TOOL_ANNOTATIONS, + }, + async ({ workspaceId, ...input }) => { + const startedAt = performance.now(); + const workspace = workspaces.getWorkspace(workspaceId); + workspaces.resolvePath(workspace, input.path); + const response = await editFileTool(input, { + cwd: workspace.root, + root: workspace.root, + }); + + if (response.isError) { + logFailedToolResponse( + config, + { + tool: toolNames.edit, + workspaceId, + path: input.path, + }, + response.content, + startedAt, + ); + return response; + } + + const stats = countDiffStats( + response.details?.patch ?? response.details?.diff, + ); + const summary = { + ...stats, + editCount: input.edits.length, + }; + const editResultText = `Edited ${input.path} (+${stats.additions} -${stats.removals}).`; + const editContent = [textBlock(editResultText)]; + logToolCall(config, { + tool: toolNames.edit, + workspaceId, + path: input.path, + success: true, + durationMs: Math.round(performance.now() - startedAt), + }); + + return { + content: editContent, + _meta: { + tool: toolNames.edit, + card: { + workspaceId, + path: input.path, + summary, + payload: { + diff: response.details?.diff, + patch: response.details?.patch, + }, + }, + }, + structuredContent: { + status: "applied", + result: contentText(editContent), + }, + }; + }, + ); +} + +function registerShellTool(context: ToolRegistrationContext): void { + const { server, config, workspaces } = context; + + registerAppTool( + server, + toolNames.shell, + { + title: "Bash", + description: CLAUDE_SHELL_DESCRIPTION, + inputSchema: { + workspaceId: z.string().describe(workspaceIdDescription), + command: z + .string() + .describe( + `Shell command to run. Must not create or modify project files; use ${toolNames.edit} or ${toolNames.write} for file changes.`, + ), + workingDirectory: z + .string() + .optional() + .describe( + "Optional working directory relative to the workspace root. Defaults to the workspace root.", + ), + timeout: z + .number() + .positive() + .max(300) + .optional() + .describe("Timeout in seconds. Defaults to 30, max 300."), + }, + outputSchema: resultOutputSchema(), + ...toolWidgetDescriptorMeta(config, "shell"), + annotations: SHELL_TOOL_ANNOTATIONS, + }, + async ({ workspaceId, workingDirectory, ...input }) => { + const startedAt = performance.now(); + const workspace = workspaces.getWorkspace(workspaceId); + const cwd = workspaces.resolveWorkingDirectory( + workspace, + workingDirectory, + ); + const response = await runShellTool(input, { + cwd, + root: workspace.root, + }); + + if (response.isError) { + logFailedToolResponse( + config, + { + tool: toolNames.shell, + workspaceId, + workingDirectory: workingDirectory ?? ".", + command: input.command, + commandLength: input.command.length, + }, + response.content, + startedAt, + ); + return response; + } + + const summary = { + command: input.command, + workingDirectory: workingDirectory ?? ".", + ...textSummary(response.content), + }; + logToolCall(config, { + tool: toolNames.shell, + workspaceId, + workingDirectory: workingDirectory ?? ".", + command: input.command, + commandLength: input.command.length, + success: true, + durationMs: Math.round(performance.now() - startedAt), + }); + + return { + ...response, + _meta: { + tool: toolNames.shell, + card: { + workspaceId, + path: workingDirectory, + summary, + payload: { content: response.content }, + }, + }, + structuredContent: { + result: contentText(response.content), + }, + }; + }, + ); +} diff --git a/src/tool-surfaces/index.ts b/src/tool-surfaces/index.ts index b21140977..f86a6e118 100644 --- a/src/tool-surfaces/index.ts +++ b/src/tool-surfaces/index.ts @@ -1,16 +1,12 @@ import type { ToolMode } from "../config.js"; import { codexInstructions, registerCodexTools } from "./codex.js"; -import { registerStandardTools, standardInstructions } from "./standard.js"; +import { claudeInstructions, registerClaudeTools } from "./claude.js"; import { type ToolSurface } from "./types.js"; const TOOL_SURFACES: Record = { - minimal: { - register: (context) => registerStandardTools(context, "minimal"), - instructions: standardInstructions("minimal"), - }, - full: { - register: (context) => registerStandardTools(context, "full"), - instructions: standardInstructions("full"), + claude: { + register: registerClaudeTools, + instructions: claudeInstructions, }, codex: { register: registerCodexTools, diff --git a/src/tool-surfaces/standard.ts b/src/tool-surfaces/standard.ts deleted file mode 100644 index f18f29eae..000000000 --- a/src/tool-surfaces/standard.ts +++ /dev/null @@ -1,573 +0,0 @@ -import { registerAppTool } from "@modelcontextprotocol/ext-apps/server"; -import { existsSync } from "node:fs"; -import * as z from "zod/v4"; -import { - editFileTool, - findFilesTool, - grepFilesTool, - listDirectoryTool, - runShellTool, - writeFileTool, -} from "../pi-tools.js"; -import { - EDIT_TOOL_ANNOTATIONS, - SHELL_TOOL_ANNOTATIONS, - WRITE_TOOL_ANNOTATIONS, - toolNames, - workspaceIdDescription, - type ToolInstructionContext, - type ToolRegistrationContext, -} from "./types.js"; -import { - contentLineCount, - contentText, - countDiffStats, - logFailedToolResponse, - logToolCall, - newFilePatch, - resultOutputSchema, - textBlock, - textSummary, - toolWidgetDescriptorMeta, -} from "./shared.js"; - -type StandardRegistration = (context: ToolRegistrationContext) => void; - -const MINIMAL_INSPECTION = `In minimal tool mode, ${toolNames.grep}, ${toolNames.glob}, and ${toolNames.ls} are disabled; use ${toolNames.shell} with command-line tools such as grep, rg, find, ls, and tree for search and directory inspection. `; - -const FULL_INSPECTION = `Prefer ${toolNames.read}, ${toolNames.grep}, ${toolNames.glob}, and ${toolNames.ls} for file inspection. `; - -const STANDARD_EDITING = `Prefer ${toolNames.edit} for targeted modifications, ${toolNames.write} only for new files or complete rewrites, and ${toolNames.shell} for tests, builds, git inspection, package scripts, and commands that are better executed by the shell. Do not create or modify files with ${toolNames.shell}; avoid shell redirection, heredocs, tee, sed -i, perl -i, node/python/ruby scripts, or any command whose purpose is to write project files.`; - -export function standardInstructions(mode: "minimal" | "full") { - const inspection = mode === "minimal" ? MINIMAL_INSPECTION : FULL_INSPECTION; - return ({ agents, skills }: ToolInstructionContext): string => - `${agents}${skills}${inspection}${STANDARD_EDITING}`; -} - -export function registerStandardTools( - context: ToolRegistrationContext, - mode: "minimal" | "full", -): void { - for (const register of STANDARD_REGISTRATIONS[mode]) { - register(context); - } -} - -const STANDARD_REGISTRATIONS: Record< - "minimal" | "full", - readonly StandardRegistration[] -> = { - minimal: [registerStandardMutationTools, registerMinimalShellTool], - full: [ - registerStandardMutationTools, - registerSearchTools, - registerFullShellTool, - ], -}; - -const MINIMAL_SHELL_DESCRIPTION = `Run a shell command in a workspace. Use only for tests, builds, git inspection, package scripts, search, file discovery, and directory inspection. In minimal tool mode, ${toolNames.grep}, ${toolNames.glob}, and ${toolNames.ls} are disabled; use command-line tools such as grep, rg, find, ls, and tree for those read-only inspection actions. Do not use ${toolNames.shell} to create or modify files. Do not use shell redirection, heredocs, tee, sed -i, perl -i, node/python/ruby scripts, or generated scripts to write project files; use ${toolNames.edit} for targeted changes and ${toolNames.write} for new files or full rewrites. Prefer ${toolNames.read} for direct file reads. This is powerful execution and should only be exposed behind strong authentication.`; -const FULL_SHELL_DESCRIPTION = `Run a shell command in a workspace. Use only for tests, builds, git inspection, package scripts, and commands that are better executed by the shell. Do not use ${toolNames.shell} to create or modify files. Do not use shell redirection, heredocs, tee, sed -i, perl -i, node/python/ruby scripts, or generated scripts to write project files; use ${toolNames.edit} for targeted changes and ${toolNames.write} for new files or full rewrites. Prefer ${toolNames.read}, ${toolNames.grep}, ${toolNames.glob}, and ${toolNames.ls} for file inspection. This is powerful execution and should only be exposed behind strong authentication.`; - -function registerMinimalShellTool(context: ToolRegistrationContext): void { - registerShellTool(context, MINIMAL_SHELL_DESCRIPTION); -} - -function registerFullShellTool(context: ToolRegistrationContext): void { - registerShellTool(context, FULL_SHELL_DESCRIPTION); -} - -function registerStandardMutationTools(context: ToolRegistrationContext): void { - const { server, config, workspaces } = context; - - registerAppTool( - server, - toolNames.write, - { - title: "Write file", - description: `Create or completely overwrite a file in a workspace. Prefer ${toolNames.edit} for targeted changes to existing files.`, - inputSchema: { - workspaceId: z.string().describe(workspaceIdDescription), - path: z - .string() - .describe("File path to write, relative to the workspace root."), - content: z.string().describe("Complete new file content."), - }, - outputSchema: resultOutputSchema(), - ...toolWidgetDescriptorMeta(config, "write"), - annotations: WRITE_TOOL_ANNOTATIONS, - }, - async ({ workspaceId, ...input }) => { - const startedAt = performance.now(); - const workspace = workspaces.getWorkspace(workspaceId); - const absolutePath = workspaces.resolvePath(workspace, input.path); - const overwritesExistingFile = existsSync(absolutePath); - const response = await writeFileTool(input, { - cwd: workspace.root, - root: workspace.root, - }); - - if (response.isError) { - logFailedToolResponse( - config, - { - tool: toolNames.write, - workspaceId, - path: input.path, - }, - response.content, - startedAt, - ); - return response; - } - - // An aggregate review can show the real replacement diff. A new-file - // patch would misrepresent an overwrite as additions with no removals. - const patch = overwritesExistingFile - ? undefined - : newFilePatch(input.path, input.content); - const stats = countDiffStats(patch); - const summary = { - ...stats, - lines: contentLineCount(input.content), - characters: input.content.length, - }; - logToolCall(config, { - tool: toolNames.write, - workspaceId, - path: input.path, - success: true, - durationMs: Math.round(performance.now() - startedAt), - }); - - return { - ...response, - _meta: { - tool: toolNames.write, - card: { - workspaceId, - path: input.path, - summary, - payload: { - content: response.content, - patch, - }, - }, - }, - structuredContent: { - result: contentText(response.content), - }, - }; - }, - ); - - registerAppTool( - server, - toolNames.edit, - { - title: "Edit file", - description: `Edit one file in a workspace by replacing exact text blocks. Prefer this over ${toolNames.write} for targeted changes. Each oldText must match a unique, non-overlapping region of the original file; merge nearby changes into one edit and keep oldText as small as possible while still unique.`, - inputSchema: { - workspaceId: z.string().describe(workspaceIdDescription), - path: z - .string() - .describe("File path to edit, relative to the workspace root."), - edits: z - .array( - z.object({ - oldText: z - .string() - .describe( - "Exact text to replace. Must match uniquely in the original file.", - ), - newText: z.string().describe("Replacement text."), - }), - ) - .min(1), - }, - outputSchema: resultOutputSchema({ - status: z.literal("applied"), - }), - ...toolWidgetDescriptorMeta(config, "edit"), - annotations: EDIT_TOOL_ANNOTATIONS, - }, - async ({ workspaceId, ...input }) => { - const startedAt = performance.now(); - const workspace = workspaces.getWorkspace(workspaceId); - workspaces.resolvePath(workspace, input.path); - const response = await editFileTool(input, { - cwd: workspace.root, - root: workspace.root, - }); - - if (response.isError) { - logFailedToolResponse( - config, - { - tool: toolNames.edit, - workspaceId, - path: input.path, - }, - response.content, - startedAt, - ); - return response; - } - - const stats = countDiffStats( - response.details?.patch ?? response.details?.diff, - ); - const summary = { - ...stats, - editCount: input.edits.length, - }; - const editResultText = `Edited ${input.path} (+${stats.additions} -${stats.removals}).`; - const editContent = [textBlock(editResultText)]; - logToolCall(config, { - tool: toolNames.edit, - workspaceId, - path: input.path, - success: true, - durationMs: Math.round(performance.now() - startedAt), - }); - - return { - content: editContent, - _meta: { - tool: toolNames.edit, - card: { - workspaceId, - path: input.path, - summary, - payload: { - diff: response.details?.diff, - patch: response.details?.patch, - }, - }, - }, - structuredContent: { - status: "applied", - result: contentText(editContent), - }, - }; - }, - ); -} - -function registerSearchTools(context: ToolRegistrationContext): void { - const { server, config, workspaces } = context; - - registerAppTool( - server, - toolNames.grep, - { - title: "Grep", - description: - "Search file contents in a workspace. Use this before broad reads when looking for symbols, text, or usage sites. Respects project ignore rules.", - inputSchema: { - workspaceId: z.string().describe(workspaceIdDescription), - pattern: z.string().describe("Search pattern."), - path: z - .string() - .optional() - .describe( - "Optional path or glob scope relative to the workspace root.", - ), - include: z.string().optional().describe("Optional include glob."), - }, - outputSchema: resultOutputSchema(), - ...toolWidgetDescriptorMeta(config, "search"), - annotations: { readOnlyHint: true }, - }, - async ({ workspaceId, ...input }) => { - const startedAt = performance.now(); - const workspace = workspaces.getWorkspace(workspaceId); - if (input.path) workspaces.resolvePath(workspace, input.path); - const response = await grepFilesTool(input, { - cwd: workspace.root, - root: workspace.root, - }); - - if (response.isError) { - logFailedToolResponse( - config, - { - tool: toolNames.grep, - workspaceId, - path: input.path, - }, - response.content, - startedAt, - ); - return response; - } - - const summary = { - pattern: input.pattern, - scope: input.path ?? ".", - ...textSummary(response.content), - }; - logToolCall(config, { - tool: toolNames.grep, - workspaceId, - path: input.path, - success: true, - durationMs: Math.round(performance.now() - startedAt), - }); - - return { - ...response, - _meta: { - tool: toolNames.grep, - card: { - workspaceId, - path: input.path, - summary, - payload: { content: response.content }, - }, - }, - structuredContent: { - result: contentText(response.content), - }, - }; - }, - ); - - registerAppTool( - server, - toolNames.glob, - { - title: "Glob", - description: - "Find files by glob pattern in a workspace. Use this to discover filenames or narrow file sets before reading. Respects project ignore rules.", - inputSchema: { - workspaceId: z.string().describe(workspaceIdDescription), - pattern: z.string().describe("File glob pattern."), - path: z - .string() - .optional() - .describe("Optional path scope relative to the workspace root."), - }, - outputSchema: resultOutputSchema(), - ...toolWidgetDescriptorMeta(config, "search"), - annotations: { readOnlyHint: true }, - }, - async ({ workspaceId, ...input }) => { - const startedAt = performance.now(); - const workspace = workspaces.getWorkspace(workspaceId); - if (input.path) workspaces.resolvePath(workspace, input.path); - const response = await findFilesTool(input, { - cwd: workspace.root, - root: workspace.root, - }); - - if (response.isError) { - logFailedToolResponse( - config, - { - tool: toolNames.glob, - workspaceId, - path: input.path, - }, - response.content, - startedAt, - ); - return response; - } - - const summary = { - pattern: input.pattern, - scope: input.path ?? ".", - ...textSummary(response.content), - }; - logToolCall(config, { - tool: toolNames.glob, - workspaceId, - path: input.path, - success: true, - durationMs: Math.round(performance.now() - startedAt), - }); - - return { - ...response, - _meta: { - tool: toolNames.glob, - card: { - workspaceId, - path: input.path, - summary, - payload: { content: response.content }, - }, - }, - structuredContent: { - result: contentText(response.content), - }, - }; - }, - ); - - registerAppTool( - server, - toolNames.ls, - { - title: "Ls", - description: - "List a directory in a workspace. Use this for directory inspection before reading files.", - inputSchema: { - workspaceId: z.string().describe(workspaceIdDescription), - path: z - .string() - .describe("Directory path to list, relative to the workspace root."), - }, - outputSchema: resultOutputSchema(), - ...toolWidgetDescriptorMeta(config, "directory"), - annotations: { readOnlyHint: true }, - }, - async ({ workspaceId, ...input }) => { - const startedAt = performance.now(); - const workspace = workspaces.getWorkspace(workspaceId); - workspaces.resolvePath(workspace, input.path); - const response = await listDirectoryTool(input, { - cwd: workspace.root, - root: workspace.root, - }); - - if (response.isError) { - logFailedToolResponse( - config, - { - tool: toolNames.ls, - workspaceId, - path: input.path, - }, - response.content, - startedAt, - ); - return response; - } - - const summary = textSummary(response.content); - logToolCall(config, { - tool: toolNames.ls, - workspaceId, - path: input.path, - success: true, - durationMs: Math.round(performance.now() - startedAt), - }); - - return { - ...response, - _meta: { - tool: toolNames.ls, - card: { - workspaceId, - path: input.path, - summary, - payload: { content: response.content }, - }, - }, - structuredContent: { - result: contentText(response.content), - }, - }; - }, - ); -} - -function registerShellTool( - context: ToolRegistrationContext, - shellDescription: string, -): void { - const { server, config, workspaces } = context; - - registerAppTool( - server, - toolNames.shell, - { - title: "Bash", - description: shellDescription, - inputSchema: { - workspaceId: z.string().describe(workspaceIdDescription), - command: z - .string() - .describe( - `Shell command to run. Must not create or modify project files; use ${toolNames.edit} or ${toolNames.write} for file changes.`, - ), - workingDirectory: z - .string() - .optional() - .describe( - "Optional working directory relative to the workspace root. Defaults to the workspace root.", - ), - timeout: z - .number() - .positive() - .max(300) - .optional() - .describe("Timeout in seconds. Defaults to 30, max 300."), - }, - outputSchema: resultOutputSchema(), - ...toolWidgetDescriptorMeta(config, "shell"), - annotations: SHELL_TOOL_ANNOTATIONS, - }, - async ({ workspaceId, workingDirectory, ...input }) => { - const startedAt = performance.now(); - const workspace = workspaces.getWorkspace(workspaceId); - const cwd = workspaces.resolveWorkingDirectory( - workspace, - workingDirectory, - ); - const response = await runShellTool(input, { - cwd, - root: workspace.root, - }); - - if (response.isError) { - logFailedToolResponse( - config, - { - tool: toolNames.shell, - workspaceId, - workingDirectory: workingDirectory ?? ".", - command: input.command, - commandLength: input.command.length, - }, - response.content, - startedAt, - ); - return response; - } - - const summary = { - command: input.command, - workingDirectory: workingDirectory ?? ".", - ...textSummary(response.content), - }; - logToolCall(config, { - tool: toolNames.shell, - workspaceId, - workingDirectory: workingDirectory ?? ".", - command: input.command, - commandLength: input.command.length, - success: true, - durationMs: Math.round(performance.now() - startedAt), - }); - - return { - ...response, - _meta: { - tool: toolNames.shell, - card: { - workspaceId, - path: workingDirectory, - summary, - payload: { content: response.content }, - }, - }, - structuredContent: { - result: contentText(response.content), - }, - }; - }, - ); -} diff --git a/src/tool-surfaces/types.ts b/src/tool-surfaces/types.ts index c1fc13fdf..f10fbd9b2 100644 --- a/src/tool-surfaces/types.ts +++ b/src/tool-surfaces/types.ts @@ -10,9 +10,6 @@ export const toolNames = { read: "read", write: "write", edit: "edit", - grep: "grep", - glob: "glob", - ls: "ls", shell: "bash", } as const; @@ -66,8 +63,6 @@ export type ToolWidgetKind = | "read" | "write" | "edit" - | "search" - | "directory" | "shell" | "show_changes"; From 18e32faf26be2f4b0e781a1967134a0700e99f87 Mon Sep 17 00:00:00 2001 From: Waishnav <86405648+Waishnav@users.noreply.github.com> Date: Mon, 24 Aug 2026 01:10:46 +0530 Subject: [PATCH 13/75] docs(tools): document the converged surfaces --- docs/chatgpt-coding-workflow.md | 19 +++++++++---------- docs/configuration.md | 22 +++++++++++++--------- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/docs/chatgpt-coding-workflow.md b/docs/chatgpt-coding-workflow.md index d7a5d13ce..46beb5c5b 100644 --- a/docs/chatgpt-coding-workflow.md +++ b/docs/chatgpt-coding-workflow.md @@ -158,14 +158,7 @@ DevSpace exposes these tool names: - `edit` - `bash` -By default, DevSpace also runs in `DEVSPACE_TOOL_MODE=minimal`, so dedicated -`grep`, `glob`, and `ls` tools are hidden. Use `bash` with command-line tools -such as `rg`, `find`, and `ls` for search and directory inspection. - -Use `DEVSPACE_TOOL_MODE=full` to restore dedicated search and directory tools. - -The experimental Codex-style surface is enabled with -`DEVSPACE_TOOL_MODE=codex`. It exposes: +DevSpace uses the Codex-style surface by default. It exposes: - `open_workspace` - `read` @@ -173,11 +166,17 @@ The experimental Codex-style surface is enabled with - `exec_command` - `write_stdin` -In this mode, `write`, `edit`, `bash`, `grep`, `glob`, and `ls` are not -registered. `exec_command` returns a process session ID when a command is still +In this mode, `write`, `edit`, and `bash` are not registered. `exec_command` +returns a process session ID when a command is still running after its yield window. Use `write_stdin` to poll it, send input, resize a PTY, or send Ctrl-C. Set `tty: true` only for commands that need a terminal. +Set `tools.mode` to `claude` in `~/.devspace/config.json` to expose `write`, +`edit`, and `bash` instead of the Codex mutation and command tools. Dedicated +MCP tools for `grep`, `glob`, and `ls` are not registered in either mode; use +the configured shell tool with command-line tools such as `rg`, `find`, and +`ls`. + ## Show Changes By default, `DEVSPACE_WIDGETS=full`. diff --git a/docs/configuration.md b/docs/configuration.md index 93a3d4fa3..f38fbfc20 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -91,18 +91,23 @@ MCP clients discover metadata from: ## Tool Modes -`DEVSPACE_TOOL_MODE` controls the tool surface. +`tools.mode` in `~/.devspace/config.json` controls the tool surface: + +```json +{ + "tools": { + "mode": "codex" + } +} +``` | Value | Behavior | | --- | --- | -| `minimal` | Default. Exposes `open_workspace`, `read`, `write`, `edit`, and `bash`. Clients use `bash` with tools such as `rg`, `find`, and `ls` for inspection. | -| `full` | Exposes the minimal tools plus dedicated `grep`, `glob`, and `ls` tools. | -| `codex` | Experimental. Exposes `open_workspace`, `read`, `apply_patch`, `exec_command`, and `write_stdin`. Existing mutation and shell tools are hidden. | +| `codex` | Default. Exposes `open_workspace`, `read`, `apply_patch`, `exec_command`, and `write_stdin`. | +| `claude` | Exposes `open_workspace`, `read`, `write`, `edit`, and `bash`. Clients use `bash` with tools such as `rg`, `find`, and `ls` for inspection. | -`DEVSPACE_MINIMAL_TOOLS` remains a backward-compatible alias when -`DEVSPACE_TOOL_MODE` is unset: `1` selects `minimal` and `0` selects `full`. -The `codex` mode must be selected through `DEVSPACE_TOOL_MODE` and always uses -its fixed short tool names regardless of `DEVSPACE_TOOL_NAMING`. +The dedicated MCP tools `grep`, `glob`, and `ls` are no longer exposed. Both +modes use their shell tool for search, file discovery, and directory inspection. Codex-mode commands run without a PTY by default. Set `tty: true` on `exec_command` for interactive terminal programs. PTY support uses the optional @@ -250,7 +255,6 @@ DEVSPACE_ALLOWED_ROOTS="$HOME/personal,$HOME/work" \ DEVSPACE_PUBLIC_BASE_URL="https://devspace.example.com" \ DEVSPACE_WORKTREE_ROOT="$HOME/.devspace/worktrees" \ DEVSPACE_ARTIFACTS="1" \ -DEVSPACE_TOOL_MODE="minimal" \ DEVSPACE_WIDGETS="full" \ npx @waishnav/devspace serve ``` From 915eff75d3095b2deaad0f2d7a2ac806e14fb8bd Mon Sep 17 00:00:00 2001 From: Waishnav <86405648+Waishnav@users.noreply.github.com> Date: Mon, 24 Aug 2026 02:05:41 +0530 Subject: [PATCH 14/75] docs(tools): state local shell authority --- src/tool-surfaces/claude.ts | 4 ++-- src/tool-surfaces/codex.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/tool-surfaces/claude.ts b/src/tool-surfaces/claude.ts index eadd566a1..8c590e9ce 100644 --- a/src/tool-surfaces/claude.ts +++ b/src/tool-surfaces/claude.ts @@ -28,7 +28,7 @@ import { toolWidgetDescriptorMeta, } from "./shared.js"; -const CLAUDE_INSTRUCTIONS = `Use ${toolNames.read} for direct file reads, ${toolNames.shell} with command-line tools such as rg, find, ls, and tree for search and directory inspection, ${toolNames.edit} for targeted modifications, and ${toolNames.write} only for new files or complete rewrites. Use ${toolNames.shell} for tests, builds, git inspection, package scripts, and other commands, but do not create or modify files through shell commands. Follow instructions returned by ${toolNames.openWorkspace}; read applicable instruction and skill files before working in their scope.`; +const CLAUDE_INSTRUCTIONS = `Use ${toolNames.read} for direct file reads, ${toolNames.shell} with command-line tools such as rg, find, ls, and tree for search and directory inspection, ${toolNames.edit} for targeted modifications, and ${toolNames.write} only for new files or complete rewrites. Use ${toolNames.shell} for tests, builds, git inspection, package scripts, and other commands, but do not create or modify files through shell commands. Shell commands run with the local user's authority and are not sandboxed; workspace validation only selects their initial working directory. Follow instructions returned by ${toolNames.openWorkspace}; read applicable instruction and skill files before working in their scope.`; export function claudeInstructions({ agents, @@ -42,7 +42,7 @@ export function registerClaudeTools(context: ToolRegistrationContext): void { registerShellTool(context); } -const CLAUDE_SHELL_DESCRIPTION = `Run a shell command in a workspace. Use it for tests, builds, git inspection, package scripts, search, file discovery, and directory inspection. Do not use ${toolNames.shell} to create or modify files. Do not use shell redirection, heredocs, tee, sed -i, perl -i, node/python/ruby scripts, or generated scripts to write project files; use ${toolNames.edit} for targeted changes and ${toolNames.write} for new files or full rewrites. Prefer ${toolNames.read} for direct file reads. This is powerful execution and should only be exposed behind strong authentication.`; +const CLAUDE_SHELL_DESCRIPTION = `Run a shell command in a workspace with the local user's authority. Commands are not sandboxed; workspace validation only selects the initial working directory. Use it for tests, builds, git inspection, package scripts, search, file discovery, and directory inspection. Do not use ${toolNames.shell} to create or modify files. Do not use shell redirection, heredocs, tee, sed -i, perl -i, node/python/ruby scripts, or generated scripts to write project files; use ${toolNames.edit} for targeted changes and ${toolNames.write} for new files or full rewrites. Prefer ${toolNames.read} for direct file reads. This is powerful execution and should only be exposed behind strong authentication.`; function registerClaudeMutationTools(context: ToolRegistrationContext): void { const { server, config, workspaces } = context; diff --git a/src/tool-surfaces/codex.ts b/src/tool-surfaces/codex.ts index 42d2ae12b..3007656e6 100644 --- a/src/tool-surfaces/codex.ts +++ b/src/tool-surfaces/codex.ts @@ -20,7 +20,7 @@ import { type CodexRegistration = (context: ToolRegistrationContext) => void; -const CODEX_INSTRUCTIONS = `Use ${toolNames.read} for direct file reads, apply_patch for all file modifications, exec_command for inspection, tests, builds, and other commands, and write_stdin to poll or interact with running processes. Follow instructions returned by ${toolNames.openWorkspace}; read applicable instruction and skill files before working in their scope.`; +const CODEX_INSTRUCTIONS = `Use ${toolNames.read} for direct file reads, apply_patch for all file modifications, exec_command for inspection, tests, builds, and other commands, and write_stdin to poll or interact with running processes. Commands run with the local user's authority and are not sandboxed; workspace validation only selects their initial working directory. Follow instructions returned by ${toolNames.openWorkspace}; read applicable instruction and skill files before working in their scope.`; export function codexInstructions(): string { return CODEX_INSTRUCTIONS; @@ -179,7 +179,7 @@ function registerCodexProcessTools(context: ToolRegistrationContext): void { { title: "Execute command", description: - "Run a command in a workspace. Returns its result when it exits during the yield window, otherwise returns a sessionId for write_stdin. Use this for file inspection, tests, builds, package scripts, and long-running processes.", + "Run a command with the local user's authority. Commands are not sandboxed; workspace validation only selects the initial working directory. Returns the result when it exits during the yield window, otherwise returns a sessionId for write_stdin. Use this for file inspection, tests, builds, package scripts, and long-running processes.", inputSchema: { workspaceId: z.string().describe(workspaceIdDescription), cmd: z.string().min(1).describe("Shell command to execute."), From b4f631ff951874507e385a6ca61c28a601f97420 Mon Sep 17 00:00:00 2001 From: Waishnav <86405648+Waishnav@users.noreply.github.com> Date: Mon, 24 Aug 2026 02:05:52 +0530 Subject: [PATCH 15/75] docs(tools): qualify the Claude inventory --- docs/chatgpt-coding-workflow.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/chatgpt-coding-workflow.md b/docs/chatgpt-coding-workflow.md index 46beb5c5b..ed1fd9fa6 100644 --- a/docs/chatgpt-coding-workflow.md +++ b/docs/chatgpt-coding-workflow.md @@ -150,7 +150,7 @@ sessions for that workspace. ## Tool Names -DevSpace exposes these tool names: +The Claude surface exposes these tool names: - `open_workspace` - `read` From 9236e28e0ad25abd52612e762929c6c3b689c17c Mon Sep 17 00:00:00 2001 From: Waishnav <86405648+Waishnav@users.noreply.github.com> Date: Mon, 24 Aug 2026 02:06:00 +0530 Subject: [PATCH 16/75] docs(config): record tool mode env removal --- docs/configuration.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/configuration.md b/docs/configuration.md index f38fbfc20..4ce9c95af 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -101,6 +101,10 @@ MCP clients discover metadata from: } ``` +`DEVSPACE_TOOL_MODE` and `DEVSPACE_MINIMAL_TOOLS` are no longer read. Set +`tools.mode` in the configuration file when selecting the Claude surface; +omitting it selects Codex. + | Value | Behavior | | --- | --- | | `codex` | Default. Exposes `open_workspace`, `read`, `apply_patch`, `exec_command`, and `write_stdin`. | From e5fe89dc397cf53dd75bf444f8b13506c4977956 Mon Sep 17 00:00:00 2001 From: Waishnav <86405648+Waishnav@users.noreply.github.com> Date: Mon, 24 Aug 2026 01:15:42 +0530 Subject: [PATCH 17/75] feat(review): expose checkpoint availability --- src/review-checkpoints.test.ts | 18 ++++++++++++++++++ src/review-checkpoints.ts | 23 +++++++++++++++++++---- 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/src/review-checkpoints.test.ts b/src/review-checkpoints.test.ts index 0c2aeb7bd..499cdb9b2 100644 --- a/src/review-checkpoints.test.ts +++ b/src/review-checkpoints.test.ts @@ -21,6 +21,24 @@ test("a clean workspace reports no changes from the last-shown checkpoint", asyn assert.match(clean.result, /No changes since last shown changes/); }); +test("initialization reports whether aggregate review is available", async (t) => { + const gitRoot = await committedRepository(t); + const plainRoot = await mkdtemp(join(tmpdir(), "devspace-review-plain-test-")); + t.after(() => rm(plainRoot, { recursive: true, force: true })); + const manager = createReviewCheckpointManager(); + + assert.deepEqual( + await manager.initializeWorkspace({ workspaceId: "ws_git", root: gitRoot }), + { available: true }, + ); + const unavailable = await manager.initializeWorkspace({ + workspaceId: "ws_plain", + root: plainRoot, + }); + assert.equal(unavailable.available, false); + if (!unavailable.available) assert.match(unavailable.reason, /git repository/i); +}); + test("show_changes reports and advances the last-shown checkpoint", async (t) => { const root = await committedRepository(t); const manager = createReviewCheckpointManager(); diff --git a/src/review-checkpoints.ts b/src/review-checkpoints.ts index 0fd8bf361..21a0d6608 100644 --- a/src/review-checkpoints.ts +++ b/src/review-checkpoints.ts @@ -26,6 +26,10 @@ export interface ReviewChangesResult { patch: string; } +export type ReviewAvailability = + | { available: true } + | { available: false; reason: string }; + interface WorkspaceReviewState { root: string; gitRoot?: string; @@ -37,7 +41,7 @@ interface WorkspaceReviewState { } export interface ReviewCheckpointManager { - initializeWorkspace(input: { workspaceId: string; root: string }): Promise; + initializeWorkspace(input: { workspaceId: string; root: string }): Promise; reviewChanges(input: { workspaceId: string; root: string; @@ -57,14 +61,15 @@ export function createReviewCheckpointManager(): ReviewCheckpointManager { const existingState = states.get(workspaceId); assertWorkspaceRoot(existingState, workspaceId, root); if (existingState?.root === root && existingState.gitRoot !== undefined) { - return; + return reviewAvailability(existingState); } const pending = initializations.get(workspaceId); if (pending) { await pending; - assertWorkspaceRoot(states.get(workspaceId), workspaceId, root); - return; + const initializedState = states.get(workspaceId); + assertWorkspaceRoot(initializedState, workspaceId, root); + return reviewAvailability(initializedState); } const initialize = initializeWorkspaceState(states, workspaceId, root); @@ -76,6 +81,7 @@ export function createReviewCheckpointManager(): ReviewCheckpointManager { initializations.delete(workspaceId); } } + return reviewAvailability(states.get(workspaceId)); }, async reviewChanges({ workspaceId, root, since = "last_shown", markReviewed = true }) { @@ -193,6 +199,15 @@ async function initializeWorkspaceState( } } +function reviewAvailability(state: WorkspaceReviewState | undefined): ReviewAvailability { + return state?.gitRoot + ? { available: true } + : { + available: false, + reason: state?.diagnostic ?? "show_changes is unavailable for this workspace.", + }; +} + function isReadyState(state: WorkspaceReviewState | undefined): boolean { return state?.gitRoot !== undefined; } From 3e1e73868e32c97797991ee3b9d19425f2d26ac4 Mon Sep 17 00:00:00 2001 From: Waishnav <86405648+Waishnav@users.noreply.github.com> Date: Mon, 24 Aug 2026 01:15:42 +0530 Subject: [PATCH 18/75] feat(review): make aggregate review universal --- src/config.test.ts | 19 +----- src/config.ts | 12 +--- src/server.test.ts | 51 +++++++-------- src/server.ts | 123 ++++++++++++++++++------------------ src/tool-surfaces/claude.ts | 14 +--- src/tool-surfaces/codex.ts | 14 +--- src/tool-surfaces/shared.ts | 21 +----- src/tool-surfaces/types.ts | 8 --- src/user-config.ts | 3 + 9 files changed, 105 insertions(+), 160 deletions(-) diff --git a/src/config.test.ts b/src/config.test.ts index e7478ca99..bb464d844 100644 --- a/src/config.test.ts +++ b/src/config.test.ts @@ -11,10 +11,7 @@ const baseEnv = { DEVSPACE_OAUTH_OWNER_TOKEN: "test-owner-token-that-is-long-enough", }; -assert.equal(loadConfig(baseEnv).widgets, "full"); -assert.equal(loadConfig({ ...baseEnv, DEVSPACE_WIDGETS: "changes" }).widgets, "changes"); -assert.equal(loadConfig({ ...baseEnv, DEVSPACE_WIDGETS: "full" }).widgets, "full"); -assert.equal(loadConfig({ ...baseEnv, DEVSPACE_WIDGETS: "off" }).widgets, "off"); +assert.equal(loadConfig(baseEnv).uiEnabled, true); assert.equal(loadConfig(baseEnv).toolMode, "codex"); assert.equal(loadConfig(baseEnv).skillsEnabled, true); assert.equal(loadConfig(baseEnv).devspaceSkillsDir, join(emptyConfigDir, "skills")); @@ -33,18 +30,6 @@ assert.deepEqual(loadConfig({ ...baseEnv, DEVSPACE_SUBAGENTS: "1" }).subagents, enabled: true, providers: [], }); -assert.throws( - () => loadConfig({ ...baseEnv, DEVSPACE_WIDGETS: "invalid" }), - /Invalid DEVSPACE_WIDGETS: invalid/, -); -assert.throws( - () => loadConfig({ ...baseEnv, DEVSPACE_WIDGETS: "minimal" }), - /Invalid DEVSPACE_WIDGETS: minimal/, -); -assert.throws( - () => loadConfig({ ...baseEnv, DEVSPACE_WIDGETS: "write-only" }), - /Invalid DEVSPACE_WIDGETS: write-only/, -); assert.deepEqual(loadConfig(baseEnv).logging, { level: "info", format: "json", @@ -154,6 +139,7 @@ writeFileSync( artifactsEnabled: true, artifactMaxFileBytes: 321, tools: { mode: "claude" }, + ui: { enabled: false }, }), ); writeFileSync( @@ -172,6 +158,7 @@ assert.equal(fileConfig.subagents.providers.length, 7); assert.equal(fileConfig.artifactsEnabled, true); assert.equal(fileConfig.artifactMaxFileBytes, 321); assert.equal(fileConfig.toolMode, "claude"); +assert.equal(fileConfig.uiEnabled, false); assert.deepEqual(fileConfig.allowedHosts, [ "localhost", "127.0.0.1", diff --git a/src/config.ts b/src/config.ts index bd8f47a84..e42365574 100644 --- a/src/config.ts +++ b/src/config.ts @@ -7,7 +7,6 @@ import { devspaceAgentsDir, devspaceSkillsDir, loadDevspaceFiles } from "./user- import { resolveSubagentsConfig, type SubagentsConfig } from "./local-agent-config.js"; export type ToolMode = "claude" | "codex"; -export type WidgetMode = "off" | "changes" | "full"; const DEFAULT_OAUTH_ACCESS_TOKEN_TTL_SECONDS = 60 * 60; const DEFAULT_OAUTH_REFRESH_TOKEN_TTL_SECONDS = 30 * 24 * 60 * 60; const DEFAULT_ARTIFACT_MAX_FILE_BYTES = 100 * 1024 * 1024; @@ -20,7 +19,7 @@ export interface ServerConfig { allowedHosts: string[]; publicBaseUrl: string; toolMode: ToolMode; - widgets: WidgetMode; + uiEnabled: boolean; stateDir: string; worktreeRoot: string; artifactsEnabled: boolean; @@ -145,13 +144,6 @@ function parseLoggingConfig(env: NodeJS.ProcessEnv): LoggingConfig { }; } -function parseWidgetMode(value: string | undefined): WidgetMode { - if (!value || value === "full") return "full"; - if (value === "off" || value === "changes") return value; - - throw new Error(`Invalid DEVSPACE_WIDGETS: ${value}`); -} - function parseRequiredSecret(value: string | undefined, name: string): string { const secret = value?.trim(); if (!secret) { @@ -221,7 +213,7 @@ export function loadConfig(env: NodeJS.ProcessEnv = process.env): ServerConfig { allowedHosts: parseAllowedHosts(env.DEVSPACE_ALLOWED_HOSTS, derivedAllowedHosts), publicBaseUrl, toolMode: files.config.tools?.mode ?? "codex", - widgets: parseWidgetMode(env.DEVSPACE_WIDGETS), + uiEnabled: files.config.ui?.enabled ?? true, stateDir: resolve(expandHomePath(env.DEVSPACE_STATE_DIR ?? files.config.stateDir ?? defaultStateDir())), worktreeRoot: resolve(expandHomePath(env.DEVSPACE_WORKTREE_ROOT ?? files.config.worktreeRoot ?? defaultWorktreeRoot())), artifactsEnabled: diff --git a/src/server.test.ts b/src/server.test.ts index 592cbda26..beb12a8a1 100644 --- a/src/server.test.ts +++ b/src/server.test.ts @@ -7,7 +7,7 @@ import test, { type TestContext } from "node:test"; import { promisify } from "node:util"; import { Client } from "@modelcontextprotocol/sdk/client/index.js"; import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js"; -import { loadConfig, type ServerConfig, type ToolMode, type WidgetMode } from "./config.js"; +import { loadConfig, type ServerConfig, type ToolMode } from "./config.js"; import type { LocalAgentProviderAvailability } from "./local-agent-availability.js"; import { buildLocalAgentProviderStatuses } from "./local-agent-catalog.js"; import type { SubagentsConfig } from "./local-agent-config.js"; @@ -26,17 +26,17 @@ test("tool modes expose the expected host-facing tool surface", async (t) => { }> = [ { mode: "claude", - expected: ["open_workspace", "read", "write", "edit", "bash"], + expected: ["open_workspace", "read", "write", "edit", "bash", "show_changes"], }, { mode: "codex", - expected: ["open_workspace", "read", "apply_patch", "exec_command", "write_stdin"], + expected: ["open_workspace", "read", "apply_patch", "exec_command", "write_stdin", "show_changes"], }, ]; for (const { mode, expected } of cases) { await t.test(mode, async (nested) => { - const context = await fixture(nested, { toolMode: mode, widgets: "off" }); + const context = await fixture(nested, { toolMode: mode, uiEnabled: false }); const tools = await context.client.listTools(); assert.deepEqual( @@ -47,31 +47,32 @@ test("tool modes expose the expected host-facing tool surface", async (t) => { } }); -test("widget modes compose independently from tool modes", async (t) => { - const cases: Array<{ - widgets: WidgetMode; - showChanges: boolean; - workspaceCard: boolean; - }> = [ - { widgets: "off", showChanges: false, workspaceCard: false }, - { widgets: "changes", showChanges: true, workspaceCard: true }, - { widgets: "full", showChanges: false, workspaceCard: true }, - ]; - - for (const { widgets, showChanges, workspaceCard } of cases) { - await t.test(widgets, async (nested) => { - const context = await fixture(nested, { toolMode: "claude", widgets }); +test("UI metadata is limited to workspace and aggregate review", async (t) => { + for (const uiEnabled of [true, false]) { + await t.test(uiEnabled ? "enabled" : "disabled", async (nested) => { + const context = await fixture(nested, { toolMode: "claude", uiEnabled }); const tools = await context.client.listTools(); - const workspace = tools.tools.find((tool) => tool.name === "open_workspace"); - const changes = tools.tools.find((tool) => tool.name === "show_changes"); - const workspaceMeta = workspace?._meta as { ui?: unknown } | undefined; + const toolsWithUi = tools.tools + .filter((tool) => Boolean((tool._meta as { ui?: unknown } | undefined)?.ui)) + .map((tool) => tool.name) + .sort(); - assert.equal(Boolean(changes), showChanges); - assert.equal(Boolean(workspaceMeta?.ui), workspaceCard); + assert.deepEqual(toolsWithUi, uiEnabled ? ["open_workspace", "show_changes"] : []); }); } }); +test("open_workspace reports aggregate review availability", async (t) => { + const plain = await fixture(t); + const gitWorkspace = await fixture(t, { git: true }); + + const plainReview = structuredContent(await callOpen(plain.client, plain.project, "plain")).review; + const gitReview = structuredContent(await callOpen(gitWorkspace.client, gitWorkspace.project, "git")).review; + + assert.equal((plainReview as { available: boolean }).available, false); + assert.deepEqual(gitReview, { available: true }); +}); + test("open_workspace keeps lifecycle flags out of model output and preserves complete card metadata", async (t) => { const providerNote = "available"; const context = await fixture(t, { @@ -301,7 +302,7 @@ async function fixture( localAgentProviders?: LocalAgentProviderAvailability[] | (() => LocalAgentProviderAvailability[]); subagents?: SubagentsConfig; toolMode?: ToolMode; - widgets?: WidgetMode; + uiEnabled?: boolean; } = {}, ): Promise { const root = await mkdtemp(join(tmpdir(), "devspace-server-test-")); @@ -339,7 +340,6 @@ async function fixture( DEVSPACE_ALLOWED_ROOTS: root, DEVSPACE_WORKTREE_ROOT: join(root, ".worktrees"), DEVSPACE_AGENT_DIR: agentDir, - DEVSPACE_WIDGETS: options.widgets ?? "full", DEVSPACE_SUBAGENTS: options.localAgentProviders ? "1" : "0", DEVSPACE_OAUTH_OWNER_TOKEN: "test-owner-token-that-is-long-enough", PORT: "1", @@ -347,6 +347,7 @@ async function fixture( const modeConfig: ServerConfig = { ...loadedConfig, toolMode: options.toolMode ?? loadedConfig.toolMode, + uiEnabled: options.uiEnabled ?? loadedConfig.uiEnabled, }; const config: ServerConfig = options.localAgentProviders ? { diff --git a/src/server.ts b/src/server.ts index 768608d67..cd42581b5 100644 --- a/src/server.ts +++ b/src/server.ts @@ -62,7 +62,7 @@ import { resultOutputSchema, textBlock, textSummary, - toolWidgetDescriptorMeta, + workspaceAppDescriptorMeta, } from "./tool-surfaces/shared.js"; import { WORKSPACE_APP_URI, @@ -103,9 +103,7 @@ function serverInstructions( ? " When the user supplies or generates a file that is not present on the DevSpace host, use download_artifact with its native file value, the existing workspace ID, and a suitable relative destination path chosen from the user's request and project structure. The tool refuses to overwrite an existing destination and returns the normalized workspace-relative path. Use normal workspace tools when explicit inspection, replacement, movement, renaming, or deletion is needed. Do not recreate binary files with write/edit calls or place signed URLs, native file objects, base64 content, or invented host paths in shell commands or logs." : ""; const showChangesInstruction = - config.widgets === "changes" - ? " If the turn successfully modifies files by creating, editing, overwriting, deleting, moving, or applying patches, call show_changes exactly once for that workspace after the final related file change and before your final response so the user can inspect the aggregate diff for that turn. Do not call it after every individual file change; do not skip it because individual file-change tools already returned diffs." - : ""; + " If the turn successfully modifies files by creating, editing, overwriting, deleting, moving, or applying patches, call show_changes exactly once for that workspace after the final related file change and before your final response so the user can inspect the aggregate diff for that turn. Do not call it after every individual file change."; const skills = config.skillsEnabled ? `When ${toolNames.openWorkspace} returns available skills and a task matches a skill, use ${toolNames.read} to read that skill's path before proceeding. Skill paths may be outside the workspace, but ${toolNames.read} only permits advertised SKILL.md files and files under already-loaded skill directories. ` : ""; @@ -393,9 +391,16 @@ export function createMcpServer( agentProviders: z.array(workspaceLocalAgentProviderOutputSchema).optional(), agents: z.array(workspaceLocalAgentOutputSchema).optional(), skillDiagnostics: z.array(z.unknown()).optional(), + review: z.discriminatedUnion("available", [ + z.object({ available: z.literal(true) }), + z.object({ + available: z.literal(false), + reason: z.string(), + }), + ]), instruction: z.string(), }, - ...toolWidgetDescriptorMeta(config, "workspace"), + ...workspaceAppDescriptorMeta(config), annotations: { readOnlyHint: true }, }, async ({ path, mode, baseRef }, { _meta }) => { @@ -410,12 +415,10 @@ export function createMcpServer( { path, mode, baseRef }, { conversationScopeId: openAiConversationScopeId(_meta) }, ); - if (config.widgets === "changes") { - await reviewCheckpoints.initializeWorkspace({ - workspaceId: workspace.id, - root: workspace.root, - }); - } + const review = await reviewCheckpoints.initializeWorkspace({ + workspaceId: workspace.id, + root: workspace.root, + }); const cardSkills = workspace.skills .filter((skill) => !skill.disableModelInvocation) .map((skill) => ({ @@ -517,6 +520,7 @@ export function createMcpServer( skills: cardSkills, agentProviders: cardAgentProviders, agents: cardAgents, + review, instruction: cardInstruction, summary: { mode: workspace.mode, @@ -534,6 +538,7 @@ export function createMcpServer( mode: workspace.mode, sourceRoot: workspace.sourceRoot, worktree: workspace.worktree, + review, ...(includeBootstrapContext ? { agentsFiles: loadedAgentsFiles, @@ -550,8 +555,7 @@ export function createMcpServer( }, ); - registerAppTool( - server, + server.registerTool( toolNames.read, { title: "Read file", @@ -590,7 +594,6 @@ export function createMcpServer( .describe("Maximum number of lines to read."), }, outputSchema: resultOutputSchema(), - ...toolWidgetDescriptorMeta(config, "read"), annotations: { readOnlyHint: true }, }, async ({ workspaceId, ...input }) => { @@ -654,58 +657,56 @@ export function createMcpServer( processSessions, }); - if (config.widgets === "changes") { - registerAppTool( - server, - "show_changes", - { - title: "Show changes", - description: - "Show the changes made in this turn for an open workspace. Call this once after the final related file change and before your final response so the user can review the combined diff. Do not call it after each individual file change.", - inputSchema: { - workspaceId: z.string().describe(workspaceIdDescription), - }, - outputSchema: resultOutputSchema(), - ...toolWidgetDescriptorMeta(config, "show_changes"), - annotations: { readOnlyHint: true }, + registerAppTool( + server, + "show_changes", + { + title: "Show changes", + description: + "Show the changes made in this turn for an open workspace. Call this once after the final related file change and before your final response so the user can review the combined diff. Do not call it after each individual file change.", + inputSchema: { + workspaceId: z.string().describe(workspaceIdDescription), }, - async ({ workspaceId }) => { - const startedAt = performance.now(); - const workspace = workspaces.getWorkspace(workspaceId); - const review = await reviewCheckpoints.reviewChanges({ - workspaceId, - root: workspace.root, - markReviewed: true, - }); + outputSchema: resultOutputSchema(), + ...workspaceAppDescriptorMeta(config), + annotations: { readOnlyHint: true }, + }, + async ({ workspaceId }) => { + const startedAt = performance.now(); + const workspace = workspaces.getWorkspace(workspaceId); + const review = await reviewCheckpoints.reviewChanges({ + workspaceId, + root: workspace.root, + markReviewed: true, + }); - const content = [textBlock(review.result)]; - logToolCall(config, { - tool: "show_changes", - workspaceId, - success: true, - durationMs: Math.round(performance.now() - startedAt), - }); + const content = [textBlock(review.result)]; + logToolCall(config, { + tool: "show_changes", + workspaceId, + success: true, + durationMs: Math.round(performance.now() - startedAt), + }); - return { - content, - _meta: { - tool: "show_changes", - card: { - workspaceId, - summary: review.summary, - files: review.files, - payload: { - patch: review.patch, - }, + return { + content, + _meta: { + tool: "show_changes", + card: { + workspaceId, + summary: review.summary, + files: review.files, + payload: { + patch: review.patch, }, }, - structuredContent: { - result: contentText(content), - }, - }; - }, - ); - } + }, + structuredContent: { + result: contentText(content), + }, + }; + }, + ); if (config.artifactsEnabled && isArtifactDownloadSupportedPlatform()) { registerArtifactTools(server, { diff --git a/src/tool-surfaces/claude.ts b/src/tool-surfaces/claude.ts index 8c590e9ce..19a470498 100644 --- a/src/tool-surfaces/claude.ts +++ b/src/tool-surfaces/claude.ts @@ -1,4 +1,3 @@ -import { registerAppTool } from "@modelcontextprotocol/ext-apps/server"; import { existsSync } from "node:fs"; import * as z from "zod/v4"; import { @@ -25,7 +24,6 @@ import { resultOutputSchema, textBlock, textSummary, - toolWidgetDescriptorMeta, } from "./shared.js"; const CLAUDE_INSTRUCTIONS = `Use ${toolNames.read} for direct file reads, ${toolNames.shell} with command-line tools such as rg, find, ls, and tree for search and directory inspection, ${toolNames.edit} for targeted modifications, and ${toolNames.write} only for new files or complete rewrites. Use ${toolNames.shell} for tests, builds, git inspection, package scripts, and other commands, but do not create or modify files through shell commands. Shell commands run with the local user's authority and are not sandboxed; workspace validation only selects their initial working directory. Follow instructions returned by ${toolNames.openWorkspace}; read applicable instruction and skill files before working in their scope.`; @@ -47,8 +45,7 @@ const CLAUDE_SHELL_DESCRIPTION = `Run a shell command in a workspace with the lo function registerClaudeMutationTools(context: ToolRegistrationContext): void { const { server, config, workspaces } = context; - registerAppTool( - server, + server.registerTool( toolNames.write, { title: "Write file", @@ -61,7 +58,6 @@ function registerClaudeMutationTools(context: ToolRegistrationContext): void { content: z.string().describe("Complete new file content."), }, outputSchema: resultOutputSchema(), - ...toolWidgetDescriptorMeta(config, "write"), annotations: WRITE_TOOL_ANNOTATIONS, }, async ({ workspaceId, ...input }) => { @@ -128,8 +124,7 @@ function registerClaudeMutationTools(context: ToolRegistrationContext): void { }, ); - registerAppTool( - server, + server.registerTool( toolNames.edit, { title: "Edit file", @@ -155,7 +150,6 @@ function registerClaudeMutationTools(context: ToolRegistrationContext): void { outputSchema: resultOutputSchema({ status: z.literal("applied"), }), - ...toolWidgetDescriptorMeta(config, "edit"), annotations: EDIT_TOOL_ANNOTATIONS, }, async ({ workspaceId, ...input }) => { @@ -224,8 +218,7 @@ function registerClaudeMutationTools(context: ToolRegistrationContext): void { function registerShellTool(context: ToolRegistrationContext): void { const { server, config, workspaces } = context; - registerAppTool( - server, + server.registerTool( toolNames.shell, { title: "Bash", @@ -251,7 +244,6 @@ function registerShellTool(context: ToolRegistrationContext): void { .describe("Timeout in seconds. Defaults to 30, max 300."), }, outputSchema: resultOutputSchema(), - ...toolWidgetDescriptorMeta(config, "shell"), annotations: SHELL_TOOL_ANNOTATIONS, }, async ({ workspaceId, workingDirectory, ...input }) => { diff --git a/src/tool-surfaces/codex.ts b/src/tool-surfaces/codex.ts index 3007656e6..c9a196f37 100644 --- a/src/tool-surfaces/codex.ts +++ b/src/tool-surfaces/codex.ts @@ -1,4 +1,3 @@ -import { registerAppTool } from "@modelcontextprotocol/ext-apps/server"; import * as z from "zod/v4"; import { applyPatch } from "../apply-patch.js"; import type { ProcessSnapshot } from "../process-sessions.js"; @@ -15,7 +14,6 @@ import { runLoggedToolOperation, textBlock, textSummary, - toolWidgetDescriptorMeta, } from "./shared.js"; type CodexRegistration = (context: ToolRegistrationContext) => void; @@ -95,8 +93,7 @@ function processToolResponse( function registerApplyPatchTool(context: ToolRegistrationContext): void { const { server, config, workspaces } = context; - registerAppTool( - server, + server.registerTool( "apply_patch", { title: "Apply patch", @@ -121,7 +118,6 @@ function registerApplyPatchTool(context: ToolRegistrationContext): void { }), ), }), - ...toolWidgetDescriptorMeta(config, "edit"), annotations: EDIT_TOOL_ANNOTATIONS, }, async ({ workspaceId, patch }) => { @@ -173,8 +169,7 @@ function registerApplyPatchTool(context: ToolRegistrationContext): void { function registerCodexProcessTools(context: ToolRegistrationContext): void { const { server, config, workspaces, processSessions } = context; - registerAppTool( - server, + server.registerTool( "exec_command", { title: "Execute command", @@ -227,7 +222,6 @@ function registerCodexProcessTools(context: ToolRegistrationContext): void { .describe("Approximate output token budget. Defaults to 10000."), }, outputSchema: processOutputSchema(), - ...toolWidgetDescriptorMeta(config, "shell"), annotations: SHELL_TOOL_ANNOTATIONS, }, async ({ @@ -281,8 +275,7 @@ function registerCodexProcessTools(context: ToolRegistrationContext): void { }, ); - registerAppTool( - server, + server.registerTool( "write_stdin", { title: "Write to process", @@ -333,7 +326,6 @@ function registerCodexProcessTools(context: ToolRegistrationContext): void { .describe("Approximate output token budget. Defaults to 10000."), }, outputSchema: processOutputSchema(), - ...toolWidgetDescriptorMeta(config, "shell"), annotations: SHELL_TOOL_ANNOTATIONS, }, async ({ diff --git a/src/tool-surfaces/shared.ts b/src/tool-surfaces/shared.ts index c2bb22bda..45c3ba837 100644 --- a/src/tool-surfaces/shared.ts +++ b/src/tool-surfaces/shared.ts @@ -1,13 +1,12 @@ import * as z from "zod/v4"; import { logEvent, commandPreview } from "../logger.js"; -import type { ServerConfig, WidgetMode } from "../config.js"; +import type { ServerConfig } from "../config.js"; import { WORKSPACE_APP_URI, type DiffStats, type ToolContent, type ToolLogFields, type ToolWidgetDescriptorMeta, - type ToolWidgetKind, } from "./types.js"; export function resultOutputSchema(extra: z.ZodRawShape = {}): z.ZodRawShape { @@ -21,11 +20,8 @@ export function resultOutputSchema(extra: z.ZodRawShape = {}): z.ZodRawShape { }; } -export function toolWidgetDescriptorMeta( - config: ServerConfig, - kind: ToolWidgetKind, -): ToolWidgetDescriptorMeta { - if (!shouldAttachWidget(config.widgets, kind)) return { _meta: {} }; +export function workspaceAppDescriptorMeta(config: ServerConfig): ToolWidgetDescriptorMeta { + if (!config.uiEnabled) return { _meta: {} }; return { _meta: { @@ -163,14 +159,3 @@ export function newFilePatch(path: string, content: string): string { .filter((line) => line.length > 0) .join("\n"); } - -function shouldAttachWidget(mode: WidgetMode, kind: ToolWidgetKind): boolean { - switch (mode) { - case "off": - return false; - case "changes": - return kind === "workspace" || kind === "show_changes"; - case "full": - return true; - } -} diff --git a/src/tool-surfaces/types.ts b/src/tool-surfaces/types.ts index f10fbd9b2..a9d8131b8 100644 --- a/src/tool-surfaces/types.ts +++ b/src/tool-surfaces/types.ts @@ -58,14 +58,6 @@ export interface DiffStats { removals: number; } -export type ToolWidgetKind = - | "workspace" - | "read" - | "write" - | "edit" - | "shell" - | "show_changes"; - export interface ToolDefinitionMeta extends Record { ui: { resourceUri: string; diff --git a/src/user-config.ts b/src/user-config.ts index 203bc7c37..00535be27 100644 --- a/src/user-config.ts +++ b/src/user-config.ts @@ -26,6 +26,9 @@ const devspaceUserConfigSchema = z.object({ tools: z.object({ mode: z.enum(["claude", "codex"]).optional(), }).strict().optional(), + ui: z.object({ + enabled: z.boolean().optional(), + }).strict().optional(), }).passthrough(); const devspaceAuthConfigSchema = z.object({ From 28496f9d062019712b9f0e1964871e1380e5f0e3 Mon Sep 17 00:00:00 2001 From: Waishnav <86405648+Waishnav@users.noreply.github.com> Date: Mon, 24 Aug 2026 01:15:42 +0530 Subject: [PATCH 19/75] docs(review): document the aggregate UI contract --- docs/chatgpt-coding-workflow.md | 22 ++++++++++------------ docs/configuration.md | 22 ++++++++++++++-------- docs/gotchas.md | 14 ++++++-------- 3 files changed, 30 insertions(+), 28 deletions(-) diff --git a/docs/chatgpt-coding-workflow.md b/docs/chatgpt-coding-workflow.md index ed1fd9fa6..0eb39fdc3 100644 --- a/docs/chatgpt-coding-workflow.md +++ b/docs/chatgpt-coding-workflow.md @@ -179,18 +179,16 @@ the configured shell tool with command-line tools such as `rg`, `find`, and ## Show Changes -By default, `DEVSPACE_WIDGETS=full`. - -In that mode, DevSpace attaches widget UI to the exposed workspace, file, edit, -and shell tools. The aggregate `show_changes` tool is not exposed by default. - -Use `DEVSPACE_WIDGETS=off` to disable widget UI, or `DEVSPACE_WIDGETS=changes` -to expose the aggregate show-changes flow. - -When `show_changes` is exposed, call it exactly once after the final file -modification in any turn that changes files. It shows the combined changes for -that turn and advances the review point automatically. Reusing a workspace does -not change this workflow. +DevSpace exposes `show_changes` in both tool modes and attaches widget UI only +to `open_workspace` and `show_changes`. Reads, edits, and commands return normal +MCP results without creating an iframe for each call. Set `ui.enabled` to +`false` in `~/.devspace/config.json` to disable UI metadata while keeping the +aggregate review tool available. + +Call `show_changes` exactly once after the final file modification in any turn +that changes files. It shows the combined changes for that turn and advances +the review point automatically. Reusing a workspace does not change this +workflow. ## Shell Use diff --git a/docs/configuration.md b/docs/configuration.md index 4ce9c95af..d246f4f1b 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -118,15 +118,22 @@ Codex-mode commands run without a PTY by default. Set `tty: true` on `node-pty` dependency; `write_stdin` can send input, poll output, and resize PTY sessions. -## Widgets +## UI -`DEVSPACE_WIDGETS` controls ChatGPT Apps iframe usage. +DevSpace attaches ChatGPT Apps UI metadata only to `open_workspace` and +`show_changes`. This avoids creating an iframe for every read, edit, or command +tool call. The aggregate `show_changes` tool remains available to every MCP +host, including hosts that ignore UI metadata. -| Value | Behavior | -| --- | --- | -| `full` | Default. Widget UI is attached to exposed workspace, file, edit, and shell tools. | -| `changes` | Enables the aggregate `show_changes` tool and attaches widget UI to `open_workspace` and `show_changes`. | -| `off` | Disables widget UI. | +UI is enabled by default. Disable it without removing `show_changes`: + +```json +{ + "ui": { + "enabled": false + } +} +``` ## Skills @@ -259,7 +266,6 @@ DEVSPACE_ALLOWED_ROOTS="$HOME/personal,$HOME/work" \ DEVSPACE_PUBLIC_BASE_URL="https://devspace.example.com" \ DEVSPACE_WORKTREE_ROOT="$HOME/.devspace/worktrees" \ DEVSPACE_ARTIFACTS="1" \ -DEVSPACE_WIDGETS="full" \ npx @waishnav/devspace serve ``` diff --git a/docs/gotchas.md b/docs/gotchas.md index 495243bb5..cd5369314 100644 --- a/docs/gotchas.md +++ b/docs/gotchas.md @@ -253,12 +253,10 @@ If a skill appears in `open_workspace`, the model must read that skill's ## Review Card Does Not Appear -Per-tool widget cards are enabled by default with: +DevSpace attaches widget UI only to `open_workspace` and `show_changes`. +Ordinary reads, edits, and commands intentionally render as normal tool results +to avoid one iframe per call. Plain MCP clients may ignore ChatGPT Apps widget +metadata and only show text results; `show_changes` remains available there. -```bash -DEVSPACE_WIDGETS=full -``` - -The aggregate `show_changes` tool is only exposed with -`DEVSPACE_WIDGETS=changes`. Plain MCP clients may ignore ChatGPT Apps widget -metadata and only show text results. +If both cards are missing in ChatGPT, confirm that `ui.enabled` is not `false` +in `~/.devspace/config.json` and reconnect the MCP server. From cc68d9439458b1a0ff6db0281de1a8612d90eaed Mon Sep 17 00:00:00 2001 From: Waishnav <86405648+Waishnav@users.noreply.github.com> Date: Mon, 24 Aug 2026 02:07:40 +0530 Subject: [PATCH 20/75] docs(review): include aggregate tool in inventories --- docs/chatgpt-coding-workflow.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/chatgpt-coding-workflow.md b/docs/chatgpt-coding-workflow.md index 0eb39fdc3..a2296fa8e 100644 --- a/docs/chatgpt-coding-workflow.md +++ b/docs/chatgpt-coding-workflow.md @@ -157,6 +157,7 @@ The Claude surface exposes these tool names: - `write` - `edit` - `bash` +- `show_changes` DevSpace uses the Codex-style surface by default. It exposes: @@ -165,6 +166,7 @@ DevSpace uses the Codex-style surface by default. It exposes: - `apply_patch` - `exec_command` - `write_stdin` +- `show_changes` In this mode, `write`, `edit`, and `bash` are not registered. `exec_command` returns a process session ID when a command is still From 4102eeec0fcf51f222392d9842183be768ac8f17 Mon Sep 17 00:00:00 2001 From: Waishnav <86405648+Waishnav@users.noreply.github.com> Date: Mon, 24 Aug 2026 02:08:23 +0530 Subject: [PATCH 21/75] fix(review): expose aggregate diff to MCP hosts --- src/server.test.ts | 38 ++++++++++++++++++++++++++++++++++++++ src/server.ts | 9 ++++++++- 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/src/server.test.ts b/src/server.test.ts index beb12a8a1..4f1215c18 100644 --- a/src/server.test.ts +++ b/src/server.test.ts @@ -73,6 +73,44 @@ test("open_workspace reports aggregate review availability", async (t) => { assert.deepEqual(gitReview, { available: true }); }); +test("show_changes exposes the aggregate diff to plain MCP hosts", async (t) => { + const context = await fixture(t, { git: true, uiEnabled: false }); + const opened = structuredContent( + await callOpen(context.client, context.project, "review"), + ); + const workspaceId = opened.workspaceId; + assert.equal(typeof workspaceId, "string"); + + await writeFile(join(context.project, "README.md"), "goodbye\n"); + const review = await context.client.callTool({ + name: "show_changes", + arguments: { workspaceId }, + }); + const structured = structuredContent(review); + + assert.deepEqual(structured.summary, { + files: 1, + additions: 1, + removals: 1, + }); + assert.deepEqual(structured.files, [ + { + path: "README.md", + type: "change", + additions: 1, + removals: 1, + }, + ]); + assert.match(structured.patch as string, /-hello\n\+goodbye/); + + const tools = await context.client.listTools(); + const outputProperties = tools.tools.find((tool) => tool.name === "show_changes") + ?.outputSchema?.properties; + assert.ok(outputProperties && "summary" in outputProperties); + assert.ok(outputProperties && "files" in outputProperties); + assert.ok(outputProperties && "patch" in outputProperties); +}); + test("open_workspace keeps lifecycle flags out of model output and preserves complete card metadata", async (t) => { const providerNote = "available"; const context = await fixture(t, { diff --git a/src/server.ts b/src/server.ts index cd42581b5..398399586 100644 --- a/src/server.ts +++ b/src/server.ts @@ -667,7 +667,11 @@ export function createMcpServer( inputSchema: { workspaceId: z.string().describe(workspaceIdDescription), }, - outputSchema: resultOutputSchema(), + outputSchema: resultOutputSchema({ + summary: reviewSummaryOutputSchema, + files: z.array(reviewFileOutputSchema), + patch: z.string(), + }), ...workspaceAppDescriptorMeta(config), annotations: { readOnlyHint: true }, }, @@ -703,6 +707,9 @@ export function createMcpServer( }, structuredContent: { result: contentText(content), + summary: review.summary, + files: review.files, + patch: review.patch, }, }; }, From 4eb087f9d39fef17e44418dcc2b873dc5c6358dc Mon Sep 17 00:00:00 2001 From: Waishnav <86405648+Waishnav@users.noreply.github.com> Date: Mon, 24 Aug 2026 01:23:27 +0530 Subject: [PATCH 22/75] refactor(tools): remove generic card payloads --- src/server.ts | 15 ---------- src/tool-surfaces/claude.ts | 60 +------------------------------------ src/tool-surfaces/codex.ts | 53 ++------------------------------ src/tool-surfaces/shared.ts | 42 -------------------------- 4 files changed, 4 insertions(+), 166 deletions(-) diff --git a/src/server.ts b/src/server.ts index 398399586..dd0a15e82 100644 --- a/src/server.ts +++ b/src/server.ts @@ -61,7 +61,6 @@ import { logToolCall, resultOutputSchema, textBlock, - textSummary, workspaceAppDescriptorMeta, } from "./tool-surfaces/shared.js"; import { @@ -619,11 +618,6 @@ export function createMcpServer( } workspaces.markReadPathLoaded(workspace, readPath); - const summary = { - ...textSummary(response.content), - offset: input.offset ?? 1, - limited: input.limit !== undefined, - }; logToolCall(config, { tool: toolNames.read, workspaceId, @@ -634,15 +628,6 @@ export function createMcpServer( return { ...response, - _meta: { - tool: toolNames.read, - card: { - workspaceId, - path: input.path, - summary, - payload: { content: response.content }, - }, - }, structuredContent: { result: contentText(response.content), }, diff --git a/src/tool-surfaces/claude.ts b/src/tool-surfaces/claude.ts index 19a470498..e1b98858b 100644 --- a/src/tool-surfaces/claude.ts +++ b/src/tool-surfaces/claude.ts @@ -1,4 +1,3 @@ -import { existsSync } from "node:fs"; import * as z from "zod/v4"; import { editFileTool, @@ -15,15 +14,12 @@ import { type ToolRegistrationContext, } from "./types.js"; import { - contentLineCount, contentText, countDiffStats, logFailedToolResponse, logToolCall, - newFilePatch, resultOutputSchema, textBlock, - textSummary, } from "./shared.js"; const CLAUDE_INSTRUCTIONS = `Use ${toolNames.read} for direct file reads, ${toolNames.shell} with command-line tools such as rg, find, ls, and tree for search and directory inspection, ${toolNames.edit} for targeted modifications, and ${toolNames.write} only for new files or complete rewrites. Use ${toolNames.shell} for tests, builds, git inspection, package scripts, and other commands, but do not create or modify files through shell commands. Shell commands run with the local user's authority and are not sandboxed; workspace validation only selects their initial working directory. Follow instructions returned by ${toolNames.openWorkspace}; read applicable instruction and skill files before working in their scope.`; @@ -63,8 +59,7 @@ function registerClaudeMutationTools(context: ToolRegistrationContext): void { async ({ workspaceId, ...input }) => { const startedAt = performance.now(); const workspace = workspaces.getWorkspace(workspaceId); - const absolutePath = workspaces.resolvePath(workspace, input.path); - const overwritesExistingFile = existsSync(absolutePath); + workspaces.resolvePath(workspace, input.path); const response = await writeFileTool(input, { cwd: workspace.root, root: workspace.root, @@ -84,17 +79,6 @@ function registerClaudeMutationTools(context: ToolRegistrationContext): void { return response; } - // An aggregate review can show the real replacement diff. A new-file - // patch would misrepresent an overwrite as additions with no removals. - const patch = overwritesExistingFile - ? undefined - : newFilePatch(input.path, input.content); - const stats = countDiffStats(patch); - const summary = { - ...stats, - lines: contentLineCount(input.content), - characters: input.content.length, - }; logToolCall(config, { tool: toolNames.write, workspaceId, @@ -105,18 +89,6 @@ function registerClaudeMutationTools(context: ToolRegistrationContext): void { return { ...response, - _meta: { - tool: toolNames.write, - card: { - workspaceId, - path: input.path, - summary, - payload: { - content: response.content, - patch, - }, - }, - }, structuredContent: { result: contentText(response.content), }, @@ -178,10 +150,6 @@ function registerClaudeMutationTools(context: ToolRegistrationContext): void { const stats = countDiffStats( response.details?.patch ?? response.details?.diff, ); - const summary = { - ...stats, - editCount: input.edits.length, - }; const editResultText = `Edited ${input.path} (+${stats.additions} -${stats.removals}).`; const editContent = [textBlock(editResultText)]; logToolCall(config, { @@ -194,18 +162,6 @@ function registerClaudeMutationTools(context: ToolRegistrationContext): void { return { content: editContent, - _meta: { - tool: toolNames.edit, - card: { - workspaceId, - path: input.path, - summary, - payload: { - diff: response.details?.diff, - patch: response.details?.patch, - }, - }, - }, structuredContent: { status: "applied", result: contentText(editContent), @@ -274,11 +230,6 @@ function registerShellTool(context: ToolRegistrationContext): void { return response; } - const summary = { - command: input.command, - workingDirectory: workingDirectory ?? ".", - ...textSummary(response.content), - }; logToolCall(config, { tool: toolNames.shell, workspaceId, @@ -291,15 +242,6 @@ function registerShellTool(context: ToolRegistrationContext): void { return { ...response, - _meta: { - tool: toolNames.shell, - card: { - workspaceId, - path: workingDirectory, - summary, - payload: { content: response.content }, - }, - }, structuredContent: { result: contentText(response.content), }, diff --git a/src/tool-surfaces/codex.ts b/src/tool-surfaces/codex.ts index c9a196f37..526e175bb 100644 --- a/src/tool-surfaces/codex.ts +++ b/src/tool-surfaces/codex.ts @@ -13,7 +13,6 @@ import { resultOutputSchema, runLoggedToolOperation, textBlock, - textSummary, } from "./shared.js"; type CodexRegistration = (context: ToolRegistrationContext) => void; @@ -57,27 +56,11 @@ function processOutputSchema(): z.ZodRawShape { }); } -function processToolResponse( - tool: "exec_command" | "write_stdin", - workspaceId: string, - snapshot: ProcessSnapshot, - summary: Record, -) { +function processToolResponse(snapshot: ProcessSnapshot) { const result = processResult(snapshot); const content = [textBlock(result)]; - const outputSummary = textSummary( - snapshot.output ? [textBlock(snapshot.output)] : [], - ); return { content, - _meta: { - tool, - card: { - workspaceId, - summary: { ...summary, ...outputSummary }, - payload: { content }, - }, - }, structuredContent: { result, sessionId: snapshot.sessionId, @@ -134,27 +117,9 @@ function registerApplyPatchTool(context: ToolRegistrationContext): void { const paths = applied.files.map((file) => file.path).join(", "); const result = `Applied patch to ${applied.files.length} file(s): ${paths}`; const content = [textBlock(result)]; - const displayPath = - applied.files.length === 1 - ? applied.files[0]?.path - : `${applied.files.length} files`; return { content, - _meta: { - tool: "apply_patch", - card: { - workspaceId, - path: displayPath, - summary: { - files: applied.files.length, - additions: applied.additions, - removals: applied.removals, - }, - files: applied.files, - payload: { patch: applied.patch }, - }, - }, structuredContent: { result, additions: applied.additions, @@ -265,13 +230,7 @@ function registerCodexProcessTools(context: ToolRegistrationContext): void { }, ); - return processToolResponse("exec_command", workspaceId, snapshot, { - command: cmd, - workingDirectory: workingDirectory ?? ".", - running: snapshot.running, - exitCode: snapshot.exitCode, - wallTimeMs: snapshot.wallTimeMs, - }); + return processToolResponse(snapshot); }, ); @@ -356,13 +315,7 @@ function registerCodexProcessTools(context: ToolRegistrationContext): void { }, ); - return processToolResponse("write_stdin", workspaceId, snapshot, { - sessionId, - charactersWritten: chars?.length ?? 0, - running: snapshot.running, - exitCode: snapshot.exitCode, - wallTimeMs: snapshot.wallTimeMs, - }); + return processToolResponse(snapshot); }, ); } diff --git a/src/tool-surfaces/shared.ts b/src/tool-surfaces/shared.ts index 45c3ba837..abfc0ba3a 100644 --- a/src/tool-surfaces/shared.ts +++ b/src/tool-surfaces/shared.ts @@ -104,24 +104,6 @@ export function textBlock(text: string): ToolContent { return { type: "text", text }; } -export function textSummary(content: ToolContent[]): { - lines: number; - characters: number; -} { - const text = contentText(content); - return { - lines: contentLineCount(text), - characters: text.length, - }; -} - -export function contentLineCount(content: string): number { - if (content.length === 0) return 0; - return content.endsWith("\n") - ? content.slice(0, -1).split("\n").length - : content.split("\n").length; -} - export function countDiffStats(diff: string | undefined): DiffStats { if (!diff) return { additions: 0, removals: 0 }; @@ -135,27 +117,3 @@ export function countDiffStats(diff: string | undefined): DiffStats { return { additions, removals }; } - -export function newFilePatch(path: string, content: string): string { - const lines = - content.length === 0 - ? [] - : content.endsWith("\n") - ? content.slice(0, -1).split("\n") - : content.split("\n"); - const hunkLength = lines.length; - const hunkRange = hunkLength === 0 ? "+0,0" : `+1,${hunkLength}`; - const body = lines.map((line) => `+${line}`).join("\n"); - - return [ - `diff --git a/${path} b/${path}`, - "new file mode 100644", - "index 0000000..0000000", - "--- /dev/null", - `+++ b/${path}`, - `@@ -0,0 ${hunkRange} @@`, - body, - ] - .filter((line) => line.length > 0) - .join("\n"); -} From 46ce4fb2def6c910f5ba19c2d977dfa5a4009612 Mon Sep 17 00:00:00 2001 From: Waishnav <86405648+Waishnav@users.noreply.github.com> Date: Mon, 24 Aug 2026 01:23:27 +0530 Subject: [PATCH 23/75] refactor(ui): keep workspace and review cards only --- package.json | 2 +- src/ui/card-types.test.ts | 120 ++++--------------- src/ui/card-types.ts | 132 ++++----------------- src/ui/heavy-payload.tsx | 182 ----------------------------- src/ui/icons.ts | 19 --- src/ui/patch-display.test.ts | 219 +++++----------------------------- src/ui/patch-display.ts | 115 ++++-------------- src/ui/tool-display.test.ts | 179 ---------------------------- src/ui/tool-display.ts | 221 ----------------------------------- src/ui/workspace-app.css | 77 +----------- src/ui/workspace-app.tsx | 188 +++++++++++++---------------- 11 files changed, 173 insertions(+), 1281 deletions(-) delete mode 100644 src/ui/heavy-payload.tsx delete mode 100644 src/ui/tool-display.test.ts delete mode 100644 src/ui/tool-display.ts diff --git a/package.json b/package.json index 723881aec..301edf4c4 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "dev": "node scripts/dev-server.mjs", "postinstall": "node scripts/fix-node-pty-permissions.mjs", "start": "node dist/cli.js serve", - "test": "tsx src/user-config.test.ts && tsx src/config.test.ts && tsx src/onboarding.test.ts && tsx src/cli-workspace.test.ts && tsx src/request-meta.test.ts && tsx src/incoming-artifacts.test.ts && tsx src/artifact-download.test.ts && tsx src/ui/card-types.test.ts && tsx src/ui/patch-display.test.ts && tsx src/ui/tool-display.test.ts && tsx src/apply-patch.test.ts && tsx src/process-platform.test.ts && tsx src/process-sessions.test.ts && tsx src/mcp-sessions.test.ts && tsx src/server-shutdown.test.ts && tsx src/local-agent-config.test.ts && tsx src/local-agent-catalog.test.ts && tsx src/local-agent-presentation.test.ts && tsx src/local-agent-runtime.test.ts && tsx src/local-agent-daemon-lifecycle.test.ts && tsx src/local-agent-daemon-protocol.test.ts && tsx src/local-agent-daemon.test.ts && tsx src/local-agent-codex.test.ts && tsx src/local-agent-opencode.test.ts && tsx src/local-agent-acp.test.ts && tsx src/local-agent-grok.test.ts && tsx src/local-agent-pi-sandbox.test.ts && tsx src/local-agent-pi.test.ts && tsx src/local-agent-claude.test.ts && tsx src/local-agent-adapters.test.ts && tsx src/local-agent-availability.test.ts && tsx src/local-agent-profiles.test.ts && tsx src/local-agent-targets.test.ts && tsx src/local-agent-store.test.ts && tsx src/local-agent-manager.test.ts && tsx src/roots.test.ts && tsx src/skills.test.ts && tsx src/workspaces.test.ts && tsx src/workspace-conversation.test.ts && tsx src/review-checkpoints.test.ts && tsx src/server.test.ts && tsx src/oauth-store.test.ts && tsx src/cli.test.ts", + "test": "tsx src/user-config.test.ts && tsx src/config.test.ts && tsx src/onboarding.test.ts && tsx src/cli-workspace.test.ts && tsx src/request-meta.test.ts && tsx src/incoming-artifacts.test.ts && tsx src/artifact-download.test.ts && tsx src/ui/card-types.test.ts && tsx src/ui/patch-display.test.ts && tsx src/apply-patch.test.ts && tsx src/process-platform.test.ts && tsx src/process-sessions.test.ts && tsx src/mcp-sessions.test.ts && tsx src/server-shutdown.test.ts && tsx src/local-agent-config.test.ts && tsx src/local-agent-catalog.test.ts && tsx src/local-agent-presentation.test.ts && tsx src/local-agent-runtime.test.ts && tsx src/local-agent-daemon-lifecycle.test.ts && tsx src/local-agent-daemon-protocol.test.ts && tsx src/local-agent-daemon.test.ts && tsx src/local-agent-codex.test.ts && tsx src/local-agent-opencode.test.ts && tsx src/local-agent-acp.test.ts && tsx src/local-agent-grok.test.ts && tsx src/local-agent-pi-sandbox.test.ts && tsx src/local-agent-pi.test.ts && tsx src/local-agent-claude.test.ts && tsx src/local-agent-adapters.test.ts && tsx src/local-agent-availability.test.ts && tsx src/local-agent-profiles.test.ts && tsx src/local-agent-targets.test.ts && tsx src/local-agent-store.test.ts && tsx src/local-agent-manager.test.ts && tsx src/roots.test.ts && tsx src/skills.test.ts && tsx src/workspaces.test.ts && tsx src/workspace-conversation.test.ts && tsx src/review-checkpoints.test.ts && tsx src/server.test.ts && tsx src/oauth-store.test.ts && tsx src/cli.test.ts", "typecheck": "tsc -p tsconfig.json --noEmit" }, "keywords": [], diff --git a/src/ui/card-types.test.ts b/src/ui/card-types.test.ts index 3c05d1449..2ae9f55e9 100644 --- a/src/ui/card-types.test.ts +++ b/src/ui/card-types.test.ts @@ -1,113 +1,35 @@ import assert from "node:assert/strict"; import test from "node:test"; import { - isEditTool, isExpandableCard, isInitiallyExpandedCard, - isPatchTool, - isShellTool, isToolName, } from "./card-types.js"; -test("the supported coding tools are recognized as card tools", () => { - for (const tool of ["apply_patch", "exec_command", "write_stdin"]) { - assert.equal(isToolName(tool), true, `${tool} should be a recognized card tool`); - } +test("only UI-backed tools are recognized as card tools", () => { + assert.equal(isToolName("open_workspace"), true); + assert.equal(isToolName("show_changes"), true); + assert.equal(isToolName("read"), false); }); -test("tool classification distinguishes patch, edit, and shell operations", () => { - assert.equal(isPatchTool("apply_patch"), true); - assert.equal(isEditTool("apply_patch"), false); - assert.equal(isShellTool("apply_patch"), false); - assert.equal(isShellTool("exec_command"), true); - assert.equal(isShellTool("write_stdin"), true); - assert.equal(isEditTool("exec_command"), false); +test("aggregate review opens when a patch is available", () => { + const card = { + tool: "show_changes" as const, + files: [{ path: "src/a.ts", type: "change" as const }], + payload: { patch: "diff --git a/src/a.ts b/src/a.ts" }, + }; + assert.equal(isExpandableCard(card), true); + assert.equal(isInitiallyExpandedCard(card), true); }); -test("a patch card expands only when it contains patch content", () => { - assert.equal( - isExpandableCard({ tool: "apply_patch", payload: { patch: "diff --git a/a b/a" } }), - true, - ); - assert.equal(isExpandableCard({ tool: "apply_patch" }), false); -}); - -test("a single-file patch opens immediately", () => { - assert.equal( - isInitiallyExpandedCard({ - tool: "apply_patch", - files: [{ path: "src/a.ts", operation: "update" }], - payload: { patch: "diff --git a/src/a.ts b/src/a.ts" }, - }), - true, - ); -}); - -test("a multi-file patch stays collapsed", () => { - assert.equal( - isInitiallyExpandedCard({ - tool: "apply_patch", - files: [ - { path: "src/a.ts", operation: "update" }, - { path: "src/b.ts", operation: "add" }, - ], - payload: { patch: "diff --git a/src/a.ts b/src/a.ts" }, - }), - false, - ); -}); - -test("show changes still opens immediately", () => { - assert.equal( - isInitiallyExpandedCard({ - tool: "show_changes", - files: [{ path: "src/a.ts", type: "change" }], - payload: { patch: "diff --git a/src/a.ts b/src/a.ts" }, - }), - true, - ); -}); - -test("a workspace card expands when it contains provider metadata", () => { - assert.equal( - isExpandableCard({ - tool: "open_workspace", - agentProviders: [{ id: "codex" }], - }), - true, - ); -}); - -test("a workspace card with details opens immediately", () => { - assert.equal( - isInitiallyExpandedCard({ - tool: "open_workspace", - skills: [{ name: "research" }], - }), - true, - ); -}); - -test("a workspace card expands when it contains agent metadata", () => { - assert.equal( - isExpandableCard({ - tool: "open_workspace", - agents: [{ name: "reviewer", provider: "codex" }], - }), - true, - ); -}); - -test("a workspace card expands when it contains available instruction files", () => { - assert.equal( - isExpandableCard({ - tool: "open_workspace", - availableAgentsFiles: [{ path: "nested/AGENTS.md" }], - }), - true, - ); -}); - -test("an empty workspace card stays collapsed", () => { +test("workspace details open only when there is useful context", () => { assert.equal(isExpandableCard({ tool: "open_workspace" }), false); + assert.equal(isInitiallyExpandedCard({ + tool: "open_workspace", + skills: [{ name: "research" }], + }), true); + assert.equal(isExpandableCard({ + tool: "open_workspace", + review: { available: false, reason: "Not a Git repository." }, + }), true); }); diff --git a/src/ui/card-types.ts b/src/ui/card-types.ts index cac8b8fd4..5ab0a5321 100644 --- a/src/ui/card-types.ts +++ b/src/ui/card-types.ts @@ -1,22 +1,8 @@ import type { App } from "@modelcontextprotocol/ext-apps"; -export type ToolName = - | "open_workspace" - | "show_changes" - | "apply_patch" - | "exec_command" - | "write_stdin" - | "read" - | "write" - | "edit" - | "grep" - | "glob" - | "ls" - | "bash"; - +export type ToolName = "open_workspace" | "show_changes"; export type HostContext = NonNullable>; -export type PatchOperation = "add" | "update" | "delete" | "move"; export type ReviewFileType = | "change" | "rename-pure" @@ -41,17 +27,18 @@ export interface ToolResultCard { detached?: boolean; managed?: boolean; }; - status?: string; + review?: + | { available: true } + | { available: false; reason: string }; summary?: Record; files?: Array<{ path?: string; previousPath?: string; - operation?: PatchOperation; type?: ReviewFileType; additions?: number; removals?: number; }>; - payload?: ToolPayload; + payload?: { patch?: string }; agentsFiles?: Array<{ path?: string; content?: string; @@ -80,80 +67,14 @@ export interface ToolResultCard { instruction?: string; } -export interface ToolContent { - type: "text" | "image"; - text?: string; - data?: string; - mimeType?: string; -} - -export interface ToolPayload { - content?: ToolContent[]; - diff?: string; - patch?: string; -} - export function isToolName(value: unknown): value is ToolName { - return ( - value === "open_workspace" || - value === "show_changes" || - value === "apply_patch" || - value === "exec_command" || - value === "write_stdin" || - value === "read" || - value === "write" || - value === "edit" || - value === "grep" || - value === "glob" || - value === "ls" || - value === "bash" - ); -} - -export function isReadTool(tool: ToolName): boolean { - return tool === "read"; -} - -export function isWriteTool(tool: ToolName): boolean { - return tool === "write"; -} - -export function isEditTool(tool: ToolName): boolean { - return tool === "edit"; -} - -export function isPatchTool(tool: ToolName): boolean { - return tool === "apply_patch"; -} - -export function isSearchTool(tool: ToolName): boolean { - return tool === "grep" || tool === "glob"; -} - -export function isShellTool(tool: ToolName): boolean { - return tool === "bash" || tool === "exec_command" || tool === "write_stdin"; -} - -export function isReviewTool(tool: ToolName): boolean { - return tool === "show_changes"; + return value === "open_workspace" || value === "show_changes"; } export function isToolResultCard(value: unknown): value is Omit { return Boolean(value && typeof value === "object"); } -export function payloadText(payload: ToolPayload | undefined): string { - return ( - payload?.content - ?.map((item) => { - if (item.type === "text") return item.text ?? ""; - return `[${item.mimeType ?? "image"} image payload]`; - }) - .filter(Boolean) - .join("\n\n") ?? "" - ); -} - export function summaryNumber( summary: Record | undefined, key: string, @@ -163,33 +84,26 @@ export function summaryNumber( } export function isExpandableCard(card: ToolResultCard): boolean { - if (card.tool === "open_workspace") { - return ( - Number(card.summary?.agentsFiles ?? 0) > 0 || - Number(card.summary?.skills ?? 0) > 0 || - Number(card.summary?.agentProviders ?? 0) > 0 || - Number(card.summary?.agents ?? 0) > 0 || - Boolean(card.agentsFiles?.length) || - Boolean(card.availableAgentsFiles?.length) || - Boolean(card.skills?.length) || - Boolean(card.agentProviders?.length) || - Boolean(card.agents?.length) || - Boolean(card.worktree) || - Boolean(card.instruction) - ); + if (card.tool === "show_changes") { + return Boolean(card.files?.length || card.payload?.patch); } - if (isReviewTool(card.tool)) return Boolean(card.files?.length || card.payload?.patch); - if (isPatchTool(card.tool)) return Boolean(card.payload?.patch); - - return Boolean(card.payload); + return ( + Number(card.summary?.agentsFiles ?? 0) > 0 || + Number(card.summary?.skills ?? 0) > 0 || + Number(card.summary?.agentProviders ?? 0) > 0 || + Number(card.summary?.agents ?? 0) > 0 || + Boolean(card.agentsFiles?.length) || + Boolean(card.availableAgentsFiles?.length) || + Boolean(card.skills?.length) || + Boolean(card.agentProviders?.length) || + Boolean(card.agents?.length) || + Boolean(card.worktree) || + Boolean(card.instruction) || + card.review?.available === false + ); } export function isInitiallyExpandedCard(card: ToolResultCard): boolean { - if (card.tool === "open_workspace") return isExpandableCard(card); - if (isReviewTool(card.tool)) return isExpandableCard(card); - if (isPatchTool(card.tool)) { - return card.files?.length === 1 && isExpandableCard(card); - } - return false; + return isExpandableCard(card); } diff --git a/src/ui/heavy-payload.tsx b/src/ui/heavy-payload.tsx deleted file mode 100644 index a61e6dacb..000000000 --- a/src/ui/heavy-payload.tsx +++ /dev/null @@ -1,182 +0,0 @@ -import { useEffect, useMemo, useRef } from "react"; -import { createRoot, type Root } from "react-dom/client"; -import { FileStream, getFiletypeFromFileName } from "@pierre/diffs"; -import type { FileStreamOptions } from "@pierre/diffs"; -import { PatchDiff } from "@pierre/diffs/react"; -import { - isEditTool, - isReadTool, - isWriteTool, - payloadText, - summaryNumber, - type HostContext, - type ToolResultCard, -} from "./card-types.js"; -import { pierrePrettyScrollbarCss } from "./scrollbar.js"; - -type ThemeType = "light" | "dark"; - -interface PayloadRendererOptions { - card: ToolResultCard; - hostContext?: HostContext; - errorMessage?: string | null; -} - -interface MountedPayload { - update(options: PayloadRendererOptions): void; - unmount(): void; -} - -export function mountHeavyPayload( - container: HTMLElement, - options: PayloadRendererOptions, -): MountedPayload { - const root = createRoot(container); - root.render(); - - return { - update(nextOptions) { - root.render(); - }, - unmount() { - root.unmount(); - }, - }; -} - -export type { MountedPayload, PayloadRendererOptions }; - -function HeavyPayload({ - card, - hostContext, - errorMessage = null, -}: PayloadRendererOptions) { - const themeType: ThemeType = hostContext?.theme === "light" ? "light" : "dark"; - - if (errorMessage) { - return ; - } - - if (isEditTool(card.tool) || isWriteTool(card.tool)) { - const patch = card.payload?.patch || card.payload?.diff; - if (!patch) return ; - - return ; - } - - const text = payloadText(card.payload); - if (!text) return ; - - if (isReadTool(card.tool)) { - return ( - - ); - } - - return
{text}
; -} - -function FilePayload({ - path, - text, - startLine, - themeType, -}: { - path: string; - text: string; - startLine: number; - themeType: ThemeType; -}) { - const wrapperRef = useRef(null); - const fileOptions: FileStreamOptions = useMemo( - () => ({ - theme: { - light: "pierre-light", - dark: "pierre-dark", - }, - themeType, - overflow: "scroll", - unsafeCSS: pierrePrettyScrollbarCss, - }), - [themeType], - ); - - useEffect(() => { - const wrapper = wrapperRef.current; - if (!wrapper) return; - - const fileStream = new FileStream({ - ...fileOptions, - lang: getFiletypeFromFileName(path), - startingLineIndex: startLine, - }); - const source = new ReadableStream({ - start(controller) { - controller.enqueue(text); - controller.close(); - }, - }); - let disposed = false; - - void fileStream.setup(source, wrapper).then(() => { - if (!disposed) return; - fileStream.cleanUp(); - wrapper.replaceChildren(); - }); - - return () => { - disposed = true; - fileStream.cleanUp(); - wrapper.replaceChildren(); - }; - }, [fileOptions, path, startLine, text]); - - return
; -} - -function DiffPayload({ - patch, - themeType, -}: { - patch: string; - themeType: ThemeType; -}) { - return ( - - ); -} - -function StatusLine({ - message, - tone = "muted", -}: { - message: string; - tone?: "muted" | "error"; -}) { - return
{message}
; -} diff --git a/src/ui/icons.ts b/src/ui/icons.ts index 022105d03..8bada4133 100644 --- a/src/ui/icons.ts +++ b/src/ui/icons.ts @@ -6,20 +6,11 @@ import { Cpu, FileDiff, FileCheck2, - FileMinus, - FilePenLine, - FilePlus, FileText, - Files, FolderGit2, FolderOpen, - FolderTree, GitBranch, GitCommitHorizontal, - LoaderCircle, - Search, - SquareTerminal, - Terminal, createElement, type IconNode, } from "lucide"; @@ -28,26 +19,16 @@ export const toolIcons = { agents: Bot, base: GitCommitHorizontal, chevronDown: ChevronDown, - deleteFile: FileMinus, diff: FileDiff, - editFile: FilePenLine, - files: Files, folderOpen: FolderOpen, - folderTree: FolderTree, gitBranch: GitBranch, instructions: FileText, instructionAvailable: FileText, instructionLoaded: FileCheck2, - loading: LoaderCircle, providers: Cpu, - readFile: FileText, - search: Search, skills: Blocks, sourceCheckout: FolderGit2, - terminal: Terminal, - terminalSquare: SquareTerminal, warning: CircleAlert, - writeFile: FilePlus, } as const satisfies Record; export type ToolIcon = IconNode; diff --git a/src/ui/patch-display.test.ts b/src/ui/patch-display.test.ts index 612809ff6..9b49f53e9 100644 --- a/src/ui/patch-display.test.ts +++ b/src/ui/patch-display.test.ts @@ -1,205 +1,38 @@ import assert from "node:assert/strict"; +import test from "node:test"; import { getFileChangePathDisplay, getPatchDisplayParts, getRenderedFileChangeKind, - getRenderedFileChangePathDisplay, } from "./patch-display.js"; -assert.deepEqual(getPatchDisplayParts({}), { - title: "Applied patch", - tone: "edit", -}); - -assert.deepEqual( - getPatchDisplayParts({ files: [{ path: "created.ts", operation: "add" }] }), - { - title: "Added 1 file", - iconKind: "added", - tone: "write", - }, -); - -assert.deepEqual( - getPatchDisplayParts({ +test("review titles describe a uniform or mixed file set", () => { + assert.equal(getPatchDisplayParts({ + files: [{ path: "a.ts", type: "new" }], + }).title, "Added 1 file"); + assert.equal(getPatchDisplayParts({ files: [ - { path: "a.ts", operation: "add" }, - { path: "b.ts", operation: "add" }, - ], - }), - { - title: "Added 2 files", - iconKind: "added", - tone: "write", - }, -); - -assert.deepEqual( - getFileChangePathDisplay({ - path: "src/new-name.ts", - previousPath: "src/old-name.ts", - }), - { - current: "new-name.ts", - previous: "old-name.ts", - title: "src/old-name.ts → src/new-name.ts", - }, -); - -assert.deepEqual( - getFileChangePathDisplay({ - path: "packages/new/file.ts", - previousPath: "src/old/file.ts", - }), - { - current: "packages/new/file.ts", - previous: "src/old/file.ts", - title: "src/old/file.ts → packages/new/file.ts", - }, -); - -assert.deepEqual( - getRenderedFileChangePathDisplay( - [{ path: "src/new-name.ts", previousPath: "src/old-name.ts", operation: "move" }], - { path: "src/new-name.ts" }, - 0, - ), - { - current: "new-name.ts", - previous: "old-name.ts", - title: "src/old-name.ts → src/new-name.ts", - }, -); - -assert.deepEqual( - getRenderedFileChangePathDisplay( - [ - { path: "shared.ts", previousPath: "first.ts", operation: "move" }, - { path: "shared.ts", previousPath: "second.ts", operation: "move" }, - ], - { path: "shared.ts" }, - 1, - ), - { - current: "shared.ts", - previous: "second.ts", - title: "second.ts → shared.ts", - }, -); - -assert.equal( - getRenderedFileChangeKind( - [ - { path: "same.tmp", operation: "add" }, - { path: "same.tmp", operation: "delete" }, - ], - { path: "same.tmp", type: "new" }, - 0, - ), - "added", -); - -assert.equal( - getRenderedFileChangeKind( - [ - { path: "same.tmp", operation: "add" }, - { path: "same.tmp", operation: "delete" }, + { path: "a.ts", type: "new" }, + { path: "b.ts", type: "change" }, ], - { path: "same.tmp", type: "deleted" }, - 1, - ), - "deleted", -); + }).title, "Changed 2 files"); +}); -assert.equal( - getRenderedFileChangeKind( - [{ path: "report.md", operation: "add" }], - { path: "report.md", type: "change" }, - 0, - ), - "edited", -); +test("rename paths stay compact within one directory", () => { + assert.deepEqual(getFileChangePathDisplay({ + path: "src/new.ts", + previousPath: "src/old.ts", + }), { + current: "new.ts", + previous: "old.ts", + title: "src/old.ts → src/new.ts", + }); +}); -assert.equal( - getRenderedFileChangeKind( - [{ path: "renamed.md", previousPath: "old.md", operation: "move" }], - { path: "renamed.md", type: "change" }, +test("card metadata fills gaps in parsed diff metadata", () => { + assert.equal(getRenderedFileChangeKind( + [{ path: "renamed.ts", type: "rename-pure" }], + { path: "renamed.ts" }, 0, - ), - "renamed", -); - -assert.deepEqual( - getPatchDisplayParts({ files: [{ path: "created.ts", type: "new" }] }), - { - title: "Added 1 file", - iconKind: "added", - tone: "write", - }, -); - -assert.deepEqual( - getPatchDisplayParts({ files: [{ path: "renamed.ts", type: "rename-changed" }] }), - { - title: "Renamed and edited 1 file", - iconKind: "renamed-edited", - tone: "edit", - }, -); - -assert.deepEqual( - getPatchDisplayParts({ files: [{ path: "removed.ts", type: "deleted" }] }), - { - title: "Deleted 1 file", - iconKind: "deleted", - tone: "delete", - }, -); - -assert.deepEqual( - getPatchDisplayParts({ files: [{ path: "unknown.ts" }] }), - { - title: "Changed 1 file", - tone: "edit", - }, -); - -assert.deepEqual( - getPatchDisplayParts({ - files: [ - { path: "created.ts", operation: "add" }, - { path: "edited.ts", operation: "update" }, - ], - }), - { - title: "Changed 2 files", - tone: "edit", - }, -); - -assert.deepEqual( - getPatchDisplayParts({ - files: [ - { path: "same.ts", operation: "add" }, - { path: "same.ts", operation: "update" }, - ], - }), - { - title: "Changed 1 file", - tone: "edit", - }, -); - -assert.deepEqual( - getPatchDisplayParts({ - files: [ - { path: "edited.ts", operation: "update" }, - { path: "moved.ts", previousPath: "old.ts", operation: "move" }, - { path: "removed.ts", operation: "delete" }, - ], - }), - { - title: "Changed 3 files", - tone: "edit", - }, -); + ), "renamed"); +}); diff --git a/src/ui/patch-display.ts b/src/ui/patch-display.ts index ec1f7ad29..83b990e9d 100644 --- a/src/ui/patch-display.ts +++ b/src/ui/patch-display.ts @@ -8,13 +8,7 @@ export type FileChangeKind = | "renamed-edited" | "unknown"; -type ToolResultFile = NonNullable[number]; - -export interface PatchDisplayParts { - title: string; - iconKind?: FileChangeKind; - tone: "edit" | "write" | "delete"; -} +type ReviewFile = NonNullable[number]; export interface FileChangePathDisplay { current: string; @@ -33,37 +27,22 @@ const fileChangeLabels: Record, string> = { export function getPatchDisplayParts( card: Pick, options: { emptyTitle?: string } = {}, -): PatchDisplayParts { +): { title: string } { const files = card.files ?? []; const fileCount = countChangedFiles(files); - - if (fileCount === 0) { - return { title: options.emptyTitle ?? "Applied patch", tone: "edit" }; - } + if (fileCount === 0) return { title: options.emptyTitle ?? "Changes ready" }; const kinds = new Set(files.map(getFileChangeKind)); - const singleKind = kinds.size === 1 ? [...kinds][0] : undefined; - const display: PatchDisplayParts = { - title: changeTitle(singleKind, fileCount), - tone: changeTone(singleKind), + const kind = kinds.size === 1 ? [...kinds][0] : undefined; + const noun = fileCount === 1 ? "file" : "files"; + return { + title: kind && kind !== "unknown" + ? `${fileChangeLabels[kind]} ${fileCount} ${noun}` + : `Changed ${fileCount} ${noun}`, }; - - if (singleKind && singleKind !== "unknown") display.iconKind = singleKind; - return display; } -export function getFileChangeKind(file: ToolResultFile): FileChangeKind { - switch (file.operation) { - case "add": - return "added"; - case "update": - return "edited"; - case "delete": - return "deleted"; - case "move": - return "renamed"; - } - +export function getFileChangeKind(file: ReviewFile): FileChangeKind { switch (file.type) { case "new": return "added"; @@ -82,51 +61,23 @@ export function getFileChangeKind(file: ToolResultFile): FileChangeKind { export function getRenderedFileChangeKind( files: NonNullable, - parsedFile: Pick, + parsedFile: Pick, index: number, ): FileChangeKind { const parsedKind = getFileChangeKind(parsedFile); - - // The diff parser is authoritative for additions, deletions, and native Git - // rename metadata. This also keeps repeated operations on the same path from - // reusing the first matching card entry. - if (parsedKind !== "edited" && parsedKind !== "unknown") return parsedKind; - - // apply_patch emits one card file per generated diff in the same order. Its - // move patch currently lacks Git rename metadata, so preserve the explicit - // move operation when the destination lines up with the parsed diff. - const indexedFile = files[index]; - if ( - indexedFile?.operation === "move" && - (!parsedFile.path || indexedFile.path === parsedFile.path) - ) { - return "renamed"; - } - - const movedFile = files.find((file) => ( - file.operation === "move" && - file.path === parsedFile.path && - (!parsedFile.previousPath || file.previousPath === parsedFile.previousPath) - )); - if (movedFile) return "renamed"; - - // A parsed content change is more accurate than an "add" directive that - // overwrote an existing file. - if (parsedKind === "edited") return "edited"; - - return indexedFile ? getFileChangeKind(indexedFile) : "unknown"; + return parsedKind === "unknown" + ? getFileChangeKind(files[index] ?? {}) + : parsedKind; } export function getFileChangePathDisplay( - file: Pick, + file: Pick, ): FileChangePathDisplay | undefined { const current = file.path ?? file.previousPath; if (!current) return undefined; const previous = file.previousPath; - if (!previous || previous === current) { - return { current, title: current }; - } + if (!previous || previous === current) return { current, title: current }; const sameDirectory = pathDirectory(previous) === pathDirectory(current); return { @@ -138,16 +89,13 @@ export function getFileChangePathDisplay( export function getRenderedFileChangePathDisplay( files: NonNullable, - parsedFile: Pick, + parsedFile: Pick, index: number, ): FileChangePathDisplay | undefined { const indexedFile = files[index]; const matchedFile = indexedFile?.path === parsedFile.path ? indexedFile - : files.find((file) => ( - file.path === parsedFile.path && - (!parsedFile.previousPath || !file.previousPath || file.previousPath === parsedFile.previousPath) - )); + : files.find((file) => file.path === parsedFile.path); const cardFile = matchedFile ?? indexedFile; return getFileChangePathDisplay({ @@ -163,37 +111,14 @@ export function fileChangeKindLabel(kind: FileChangeKind): string { function countChangedFiles(files: NonNullable): number { const paths = new Set(); let unnamedFiles = 0; - for (const file of files) { const path = file.path ?? file.previousPath; - if (path) { - paths.add(path); - } else { - unnamedFiles += 1; - } + if (path) paths.add(path); + else unnamedFiles += 1; } - return paths.size + unnamedFiles; } -function changeTitle(kind: FileChangeKind | undefined, fileCount: number): string { - if (kind && kind !== "unknown") { - return `${fileChangeLabels[kind]} ${fileCount} ${fileNoun(fileCount)}`; - } - - return `Changed ${fileCount} ${fileNoun(fileCount)}`; -} - -function changeTone(kind: FileChangeKind | undefined): PatchDisplayParts["tone"] { - if (kind === "added") return "write"; - if (kind === "deleted") return "delete"; - return "edit"; -} - -function fileNoun(fileCount: number): "file" | "files" { - return fileCount === 1 ? "file" : "files"; -} - function pathDirectory(path: string): string { const separatorIndex = Math.max(path.lastIndexOf("/"), path.lastIndexOf("\\")); return separatorIndex === -1 ? "" : path.slice(0, separatorIndex); diff --git a/src/ui/tool-display.test.ts b/src/ui/tool-display.test.ts deleted file mode 100644 index 71d3504d7..000000000 --- a/src/ui/tool-display.test.ts +++ /dev/null @@ -1,179 +0,0 @@ -import assert from "node:assert/strict"; -import type { ToolResultCard } from "./card-types.js"; -import { toolIcons } from "./icons.js"; -import { getToolDisplay, getToolHeaderSummary } from "./tool-display.js"; - -const displayCases: Array<[ToolResultCard, { title: string; tone: string }]> = [ - [{ tool: "open_workspace", root: "/tmp/project" }, { title: "Opened workspace", tone: "workspace" }], - [{ tool: "open_workspace", root: "/tmp/project", workspaceReused: true }, { title: "Reused workspace", tone: "workspace" }], - [{ tool: "open_workspace", root: "/tmp/project", mode: "worktree" }, { title: "Opened workspace", tone: "workspace" }], - [{ tool: "open_workspace", root: "/tmp/project", mode: "worktree", workspaceReused: true }, { title: "Reused workspace", tone: "workspace" }], - [{ tool: "read", path: "src/read.ts" }, { title: "Read file", tone: "read" }], - [{ tool: "write", path: "src/write.ts" }, { title: "Wrote file", tone: "write" }], - [{ tool: "edit", path: "src/edit.ts" }, { title: "Edited file", tone: "edit" }], - [{ - tool: "apply_patch", - files: [{ path: "src/new.ts", operation: "add" }], - }, { title: "Added 1 file", tone: "write" }], - [{ - tool: "grep", - summary: { pattern: "needle", scope: "src" }, - }, { title: "Searched files", tone: "search" }], - [{ tool: "ls", path: "src" }, { title: "Listed directory", tone: "directory" }], - [{ tool: "bash", summary: { command: "npm test", exitCode: 0 } }, { title: "Ran command", tone: "shell" }], -]; - -for (const [card, expected] of displayCases) { - assert.deepEqual(pickDisplay(getToolDisplay(card)), expected); -} - -assert.equal(getToolDisplay({ tool: "open_workspace", root: "/tmp/project" }).label, "/tmp/project"); -assert.equal( - getToolDisplay({ tool: "open_workspace", root: "/tmp/project" }).icon, - toolIcons.folderOpen, -); -assert.equal( - getToolDisplay({ tool: "open_workspace", root: "/tmp/project", mode: "worktree" }).icon, - toolIcons.gitBranch, -); -assert.equal( - getToolDisplay({ tool: "grep", summary: { pattern: "needle", scope: "src" } }).label, - "needle in src", -); - -assert.equal( - getToolDisplay({ - tool: "apply_patch", - files: [{ - path: "src/new-name.ts", - previousPath: "src/old-name.ts", - operation: "move", - }], - }).label, - "src/old-name.ts → src/new-name.ts", -); - -assert.deepEqual( - pickDisplay(getToolDisplay({ - tool: "show_changes", - files: [ - { path: "src/a.ts", type: "change" }, - { path: "src/b.ts", type: "change" }, - ], - })), - { title: "Edited 2 files", tone: "review" }, -); - -assert.deepEqual( - pickDisplay(getToolDisplay({ - tool: "show_changes", - files: [ - { path: "src/a.ts", type: "new" }, - { path: "src/b.ts", type: "change" }, - ], - })), - { title: "Changed 2 files", tone: "review" }, -); - -assert.deepEqual( - pickDisplay(getToolDisplay({ - tool: "show_changes", - files: [{ path: "src/old.ts", type: "deleted" }], - })), - { title: "Deleted 1 file", tone: "review" }, -); - -assert.equal( - getToolDisplay({ tool: "show_changes", payload: { patch: "diff --git a/a b/a" } }).title, - "Changes ready", -); - -assert.equal(getToolDisplay({ tool: "show_changes" }).title, "No changes"); - -assert.equal( - getToolDisplay({ tool: "exec_command", summary: { running: true, command: "npm test" } }).title, - "Command running", -); -assert.equal( - getToolDisplay({ tool: "exec_command", summary: { running: false, exitCode: 1 } }).title, - "Command failed", -); -assert.equal( - getToolDisplay({ tool: "write_stdin", summary: { running: false, exitCode: 0 } }).title, - "Process finished", -); -assert.equal( - getToolDisplay({ tool: "exec_command", summary: { running: true } }).state, - "running", -); -assert.equal( - getToolDisplay({ tool: "exec_command", summary: { running: false, exitCode: 0 } }).state, - "success", -); -assert.equal( - getToolDisplay({ tool: "exec_command", summary: { running: false, exitCode: 1 } }).state, - "error", -); - -assert.deepEqual( - pickDisplay(getToolDisplay({ tool: "glob", summary: { lines: 1, pattern: "**/*.ts" } })), - { title: "Found files", tone: "search" }, -); - -assert.deepEqual( - getToolHeaderSummary({ tool: "glob", summary: { lines: 1 } }), - { kind: "empty" }, -); - -assert.equal( - getToolDisplay({ - tool: "apply_patch", - files: [{ path: "src/removed.ts", operation: "delete" }], - }).icon, - toolIcons.deleteFile, -); - -assert.deepEqual( - getToolHeaderSummary({ tool: "show_changes", summary: { additions: 14, removals: 1 } }), - { kind: "diff", additions: 14, removals: 1 }, -); - -assert.deepEqual( - getToolHeaderSummary({ - tool: "open_workspace", - summary: { mode: "worktree", agentsFiles: 1, skills: 4 }, - }), - { kind: "text", text: "1 instruction · 4 skills" }, -); - -assert.deepEqual( - getToolHeaderSummary({ tool: "exec_command", summary: { lines: 3, wallTimeMs: 1_500 } }), - { kind: "text", text: "3 lines · 1.5s" }, -); - -assert.deepEqual( - getToolHeaderSummary({ tool: "grep", summary: { lines: 2 } }), - { kind: "text", text: "2 lines" }, -); - -assert.deepEqual( - getToolHeaderSummary({ tool: "read", summary: { lines: 1 } }), - { kind: "text", text: "1 line" }, -); - -assert.deepEqual( - getToolHeaderSummary({ tool: "ls", summary: { lines: 0 } }), - { kind: "text", text: "0 lines" }, -); - -assert.deepEqual( - getToolHeaderSummary({ tool: "open_workspace" }), - { kind: "empty" }, -); - -function pickDisplay(display: ReturnType) { - return { - title: display.title, - tone: display.tone, - }; -} diff --git a/src/ui/tool-display.ts b/src/ui/tool-display.ts deleted file mode 100644 index be64e0b00..000000000 --- a/src/ui/tool-display.ts +++ /dev/null @@ -1,221 +0,0 @@ -import { - isEditTool, - isPatchTool, - isReviewTool, - isShellTool, - isWriteTool, - summaryNumber, - type ToolResultCard, -} from "./card-types.js"; -import { toolIcons, type ToolIcon } from "./icons.js"; -import { - getFileChangePathDisplay, - getPatchDisplayParts, -} from "./patch-display.js"; - -export interface ToolDisplay { - icon: ToolIcon; - title: string; - label?: string; - tone: string; - state?: "running" | "success" | "error"; -} - -export type ToolHeaderSummary = - | { kind: "diff"; additions: number; removals: number } - | { kind: "text"; text: string } - | { kind: "empty" }; - -export function getToolDisplay(card: ToolResultCard): ToolDisplay { - switch (card.tool) { - case "open_workspace": - return { - icon: card.mode === "worktree" ? toolIcons.gitBranch : toolIcons.folderOpen, - title: workspaceTitle(card), - label: card.root ?? card.path, - tone: "workspace", - }; - case "read": - return { - icon: toolIcons.readFile, - title: "Read file", - label: card.path, - tone: "read", - }; - case "write": - return { - icon: toolIcons.writeFile, - title: "Wrote file", - label: card.path, - tone: "write", - }; - case "edit": - return { - icon: toolIcons.editFile, - title: "Edited file", - label: card.path, - tone: "edit", - }; - case "apply_patch": { - const display = getPatchDisplayParts(card); - return { - icon: patchIcon(display.iconKind), - title: display.title, - label: singleFilePath(card), - tone: display.tone, - }; - } - case "grep": - return { - icon: toolIcons.search, - title: "Searched files", - label: searchLabel(card), - tone: "search", - }; - case "glob": { - return { - icon: toolIcons.files, - title: "Found files", - label: searchLabel(card), - tone: "search", - }; - } - case "ls": - return { - icon: toolIcons.folderTree, - title: "Listed directory", - label: card.path, - tone: "directory", - }; - case "bash": - case "exec_command": - return { - icon: toolIcons.terminalSquare, - title: processTitle(card, "command"), - label: processLabel(card), - tone: "shell", - state: processState(card), - }; - case "write_stdin": - return { - icon: toolIcons.terminal, - title: processTitle(card, "process"), - label: processLabel(card), - tone: "shell", - state: processState(card), - }; - case "show_changes": { - const display = getPatchDisplayParts(card, { emptyTitle: "Changes ready" }); - const fileCount = card.files?.length ?? 0; - return { - icon: toolIcons.diff, - title: fileCount > 0 || card.payload?.patch - ? display.title - : "No changes", - label: singleFilePath(card), - tone: "review", - }; - } - } -} - -export function getToolHeaderSummary(card: ToolResultCard): ToolHeaderSummary { - const summary = card.summary ?? {}; - - if (isReviewTool(card.tool) || isPatchTool(card.tool) || isEditTool(card.tool) || isWriteTool(card.tool)) { - return { - kind: "diff", - additions: summaryNumber(summary, "additions") ?? 0, - removals: summaryNumber(summary, "removals") ?? 0, - }; - } - - if (card.tool === "open_workspace") { - const parts = [ - countLabel(summaryNumber(summary, "agentsFiles"), "instruction"), - countLabel(summaryNumber(summary, "skills"), "skill"), - ].filter((part): part is string => Boolean(part)); - return parts.length > 0 ? { kind: "text", text: parts.join(" · ") } : { kind: "empty" }; - } - - if (isShellTool(card.tool)) { - const parts = [ - countLabel(summaryNumber(summary, "lines"), "line"), - durationLabel(summaryNumber(summary, "wallTimeMs")), - ].filter((part): part is string => Boolean(part)); - return parts.length > 0 ? { kind: "text", text: parts.join(" · ") } : { kind: "empty" }; - } - - if (card.tool === "grep" || card.tool === "read" || card.tool === "ls") { - const lines = countLabel(summaryNumber(summary, "lines"), "line"); - return lines ? { kind: "text", text: lines } : { kind: "empty" }; - } - - return { kind: "empty" }; -} - -function patchIcon(kind: ReturnType["iconKind"]): ToolIcon { - if (kind === "added") return toolIcons.writeFile; - if (kind === "deleted") return toolIcons.deleteFile; - if (kind === "renamed" || kind === "renamed-edited") return toolIcons.files; - return toolIcons.editFile; -} - -function workspaceTitle(card: ToolResultCard): string { - return `${card.workspaceReused ? "Reused" : "Opened"} workspace`; -} - -function singleFilePath(card: ToolResultCard): string | undefined { - if (card.files?.length === 1) { - return getFileChangePathDisplay(card.files[0])?.title ?? card.path; - } - return undefined; -} - -function searchLabel(card: ToolResultCard): string | undefined { - const pattern = card.summary?.pattern; - const scope = card.summary?.scope; - if (typeof pattern !== "string") return card.path; - return typeof scope === "string" && scope !== "." ? `${pattern} in ${scope}` : pattern; -} - -function processTitle(card: ToolResultCard, subject: "command" | "process"): string { - if (card.summary?.running === true) { - return subject === "command" ? "Command running" : "Process running"; - } - - const exitCode = summaryNumber(card.summary, "exitCode"); - if (exitCode !== undefined && exitCode !== 0) { - return subject === "command" ? "Command failed" : "Process failed"; - } - - return subject === "command" ? "Ran command" : "Process finished"; -} - -function processState(card: ToolResultCard): ToolDisplay["state"] { - if (card.summary?.running === true) return "running"; - const exitCode = summaryNumber(card.summary, "exitCode"); - if (exitCode !== undefined && exitCode !== 0) return "error"; - return exitCode === 0 ? "success" : undefined; -} - -function processLabel(card: ToolResultCard): string | undefined { - const command = card.summary?.command; - if (typeof command === "string") return command; - const sessionId = card.summary?.sessionId; - if (typeof sessionId === "number" || typeof sessionId === "string") { - return `Session ${String(sessionId)}`; - } - return card.path; -} - -function countLabel(count: number | undefined, noun: string): string | undefined { - if (count === undefined) return undefined; - return `${count} ${noun}${count === 1 ? "" : "s"}`; -} - -function durationLabel(durationMs: number | undefined): string | undefined { - if (durationMs === undefined) return undefined; - if (durationMs < 1_000) return `${Math.round(durationMs)}ms`; - return `${(durationMs / 1_000).toFixed(durationMs < 10_000 ? 1 : 0)}s`; -} diff --git a/src/ui/workspace-app.css b/src/ui/workspace-app.css index bee72228f..906cbeb65 100644 --- a/src/ui/workspace-app.css +++ b/src/ui/workspace-app.css @@ -42,45 +42,14 @@ body { color: var(--color-text-primary, #f5f5f6); } -.tool-card.workspace, -.tool-card.directory { +.tool-card.workspace { --tool-accent: color-mix(in srgb, var(--color-text-primary, #f5f5f6) 34%, #3b82f6 66%); } -.tool-card.read, -.tool-card.search { - --tool-accent: color-mix(in srgb, var(--color-text-primary, #f5f5f6) 32%, #06b6d4 68%); -} - -.tool-card.write { - --tool-accent: var(--color-success-text, #6fda83); -} - -.tool-card.edit, .tool-card.review { --tool-accent: color-mix(in srgb, var(--color-text-primary, #f5f5f6) 28%, #d99742 72%); } -.tool-card.delete { - --tool-accent: var(--color-danger-text, #ee7676); -} - -.tool-card.shell { - --tool-accent: color-mix(in srgb, var(--color-text-primary, #f5f5f6) 42%, #64748b 58%); -} - -.tool-card.state-success { - --tool-accent: var(--color-success-text, #6fda83); -} - -.tool-card.state-error { - --tool-accent: var(--color-danger-text, #ee7676); -} - -.tool-card.state-running { - --tool-accent: color-mix(in srgb, var(--color-text-primary, #f5f5f6) 30%, #38bdf8 70%); -} - @supports selector(::-webkit-scrollbar) { .pretty-scrollbar::-webkit-scrollbar { width: 12px; @@ -248,29 +217,6 @@ body { transform: rotate(180deg); } -.chevron.loading { - transform: none; -} - -.chevron.loading .icon-svg { - animation: payload-spinner 700ms linear infinite; - fill: none; - stroke-linecap: round; - stroke-dasharray: 38 14; -} - -@keyframes payload-spinner { - to { - transform: rotate(360deg); - } -} - -@media (prefers-reduced-motion: reduce) { - .chevron.loading .icon-svg { - animation: none; - } -} - .tool-body { border-top: 1px solid var(--tool-card-divider); background: var(--tool-card-body-bg); @@ -836,8 +782,7 @@ body { color: var(--color-danger-text, #ee7676); } -.pierre-diff, -.pierre-file { +.pierre-diff { --diffs-bg: var(--tool-payload-bg, var(--color-background-primary, #101114)); --diffs-light-bg: var(--color-background-primary, #ffffff); --diffs-dark-bg: var(--tool-payload-bg, var(--color-background-primary, #101114)); @@ -852,24 +797,6 @@ body { border-bottom-left-radius: 8px; } -.text-payload { - max-height: 420px; - margin: 0; - overflow: auto; - padding: 10px 12px; - color: var(--color-text-secondary, #c7c7ce); - font-family: var(--font-mono, ui-monospace, SFMono-Regular, monospace); - font-size: var(--font-text-sm-size, 12px); - line-height: 1.55; - white-space: pre-wrap; - overflow-wrap: break-word; -} - -.text-payload.bash { - color: var(--color-text-primary, #f5f5f6); - background: var(--color-background-primary, #101114); -} - @media (max-width: 520px) { .tool-header { grid-template-columns: 36px minmax(0, 1fr) auto 18px; diff --git a/src/ui/workspace-app.tsx b/src/ui/workspace-app.tsx index cd833c2cc..c3c6f36e8 100644 --- a/src/ui/workspace-app.tsx +++ b/src/ui/workspace-app.tsx @@ -6,28 +6,29 @@ import { } from "@modelcontextprotocol/ext-apps"; import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; import { - isEditTool, isExpandableCard, isInitiallyExpandedCard, - isPatchTool, - isReadTool, - isReviewTool, isToolName, isToolResultCard, - isWriteTool, - payloadText, + summaryNumber, type HostContext, type ToolName, type ToolResultCard, } from "./card-types.js"; import { getProviderLogo, renderIcon, toolIcons, type ToolIcon } from "./icons.js"; import { - getToolDisplay, - getToolHeaderSummary, - type ToolDisplay, -} from "./tool-display.js"; + getFileChangePathDisplay, + getPatchDisplayParts, +} from "./patch-display.js"; import "./workspace-app.css"; +interface CardDisplay { + icon: ToolIcon; + title: string; + label?: string; + tone: "workspace" | "review"; +} + interface MountedPayload { update(options: { card: ToolResultCard; @@ -162,8 +163,8 @@ function render(): void { return; } - const display = getToolDisplay(card); - if (isReviewTool(card.tool)) { + const display = cardDisplay(card); + if (card.tool === "show_changes") { renderReviewCard(card, display); return; } @@ -241,72 +242,25 @@ async function renderPayloadIfNeeded(): Promise { return; } - if (shouldUseHeavyPayload(card)) { - if (currentPayload) { - currentPayload.update({ card, hostContext, errorMessage }); - return; - } - - setPayloadLoading(target, true); - - try { - const { mountHeavyPayload } = await import("./heavy-payload.js"); - if (target !== currentPayloadContainer || !expanded || !card) return; - - setPayloadLoading(target, false); - currentPayload = mountHeavyPayload(target, { - card, - hostContext, - errorMessage, - }); - } catch (loadError) { - if (target !== currentPayloadContainer || !expanded) return; - - setPayloadLoading(target, false); - renderStatus( - target, - loadError instanceof Error ? loadError.message : "Unable to load details.", - "error", - ); - } - return; - } - - if (isReviewTool(card.tool) || isPatchTool(card.tool)) { - const visibleFileCount = isReviewTool(card.tool) && !reviewFilesExpanded - ? Math.max(3, (card.files ?? []).slice(0, 3).length) - : undefined; - - if (currentPayload) { - currentPayload.update({ card, hostContext, errorMessage, visibleFileCount }); - return; - } - - renderStatus(target, isReviewTool(card.tool) ? "Loading review..." : "Loading diff..."); - - const { mountReviewPayload } = await import("./review-payload.js"); - if (target !== currentPayloadContainer || !card) return; + const visibleFileCount = !reviewFilesExpanded + ? Math.max(3, (card.files ?? []).slice(0, 3).length) + : undefined; - currentPayload = mountReviewPayload(target, { - card, - hostContext, - errorMessage, - visibleFileCount, - }); + if (currentPayload) { + currentPayload.update({ card, hostContext, errorMessage, visibleFileCount }); return; } - const text = payloadText(card.payload); - if (!text) { - renderStatus(target, "No details available."); - return; - } - - renderPrePayload(target, text, card.tool); -} + renderStatus(target, "Loading review..."); + const { mountReviewPayload } = await import("./review-payload.js"); + if (target !== currentPayloadContainer || !card) return; -function shouldUseHeavyPayload(card: ToolResultCard): boolean { - return isReadTool(card.tool) || isEditTool(card.tool) || isWriteTool(card.tool); + currentPayload = mountReviewPayload(target, { + card, + hostContext, + errorMessage, + visibleFileCount, + }); } function unmountPayload(): void { @@ -329,40 +283,36 @@ function renderStatus( container.replaceChildren(element("div", { className: `status ${tone}`, text: message })); } -function renderPrePayload( - container: HTMLElement, - text: string, - tool: string, -): void { - unmountCurrentPayload(); - container.replaceChildren(element("pre", { - className: `text-payload pretty-scrollbar ${tool}`, - text, - })); -} - function renderHeaderSummary(card: ToolResultCard): HTMLElement { - const summary = getToolHeaderSummary(card); - - if (summary.kind === "diff") { + if (card.tool === "show_changes") { const stats = element("span", { className: "stats" }); stats.setAttribute("aria-label", "Diff statistics"); stats.append( - element("span", { className: "add", text: `+${String(summary.additions)}` }), - element("span", { className: "remove", text: `-${String(summary.removals)}` }), + element("span", { + className: "add", + text: `+${String(summaryNumber(card.summary, "additions") ?? 0)}`, + }), + element("span", { + className: "remove", + text: `-${String(summaryNumber(card.summary, "removals") ?? 0)}`, + }), ); return stats; } + const parts = [ + countLabel(summaryNumber(card.summary, "agentsFiles"), "instruction"), + countLabel(summaryNumber(card.summary, "skills"), "skill"), + ].filter((part): part is string => Boolean(part)); const meta = element("span", { - className: `header-meta ${summary.kind === "empty" ? "empty" : ""}`, - text: summary.kind === "text" ? summary.text : "", + className: `header-meta ${parts.length === 0 ? "empty" : ""}`, + text: parts.join(" · "), }); - if (summary.kind === "empty") meta.setAttribute("aria-hidden", "true"); + if (parts.length === 0) meta.setAttribute("aria-hidden", "true"); return meta; } -function renderReviewCard(card: ToolResultCard, display: ToolDisplay): void { +function renderReviewCard(card: ToolResultCard, display: CardDisplay): void { unmountPayload(); const files = card.files ?? []; @@ -445,24 +395,37 @@ function renderChevron(isExpanded: boolean, visible: boolean): HTMLElement { return chevron; } -function toolCardClassName(display: ToolDisplay): string { - return ["tool-card", display.tone, display.state ? `state-${display.state}` : undefined] - .filter(Boolean) - .join(" "); +function toolCardClassName(display: CardDisplay): string { + return `tool-card ${display.tone}`; } -function setPayloadLoading(container: HTMLElement, loading: boolean): void { - const header = container.previousElementSibling; - const chevron = header?.querySelector(".chevron"); - if (!chevron) return; +function cardDisplay(card: ToolResultCard): CardDisplay { + if (card.tool === "open_workspace") { + return { + icon: card.mode === "worktree" ? toolIcons.gitBranch : toolIcons.folderOpen, + title: `${card.workspaceReused ? "Reused" : "Opened"} workspace`, + label: card.root ?? card.path, + tone: "workspace", + }; + } + + const display = getPatchDisplayParts(card, { emptyTitle: "Changes ready" }); + return { + icon: toolIcons.diff, + title: card.files?.length || card.payload?.patch ? display.title : "No changes", + label: singleFilePath(card), + tone: "review", + }; +} - chevron.classList.toggle("loading", loading); - chevron.replaceChildren( - renderIcon(loading ? toolIcons.loading : toolIcons.chevronDown), - ); +function singleFilePath(card: ToolResultCard): string | undefined { + if (card.files?.length !== 1) return undefined; + return getFileChangePathDisplay(card.files[0])?.title ?? card.path; +} - const button = header instanceof HTMLButtonElement ? header : null; - if (button) button.setAttribute("aria-busy", String(loading)); +function countLabel(count: number | undefined, noun: string): string | undefined { + if (count === undefined) return undefined; + return `${count} ${noun}${count === 1 ? "" : "s"}`; } function renderWorkspacePayload(container: HTMLElement, card: ToolResultCard): void { @@ -510,6 +473,15 @@ function renderWorkspacePayload(container: HTMLElement, card: ToolResultCard): v ); } + if (card.review?.available === false) { + appendWorkspaceTextRow( + rows, + "Review", + card.review.reason, + toolIcons.warning, + ); + } + appendWorkspaceInstructions( rows, card.agentsFiles ?? [], From 15f17dfbcf0f2088c628c526952a40972e681c1e Mon Sep 17 00:00:00 2001 From: Waishnav <86405648+Waishnav@users.noreply.github.com> Date: Mon, 24 Aug 2026 01:23:27 +0530 Subject: [PATCH 24/75] docs(ui): add review surface evidence --- docs/assets/v11-review-ui-after.png | Bin 0 -> 276402 bytes docs/assets/v11-review-ui-before.png | Bin 0 -> 247322 bytes docs/assets/v11-review-ui-interaction.mp4 | Bin 0 -> 39984 bytes 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/assets/v11-review-ui-after.png create mode 100644 docs/assets/v11-review-ui-before.png create mode 100644 docs/assets/v11-review-ui-interaction.mp4 diff --git a/docs/assets/v11-review-ui-after.png b/docs/assets/v11-review-ui-after.png new file mode 100644 index 0000000000000000000000000000000000000000..adc33c121280b6831658c9224bdf7c9751b5a091 GIT binary patch literal 276402 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¨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�%x^S_P4w= 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
d~DZL=k&Vs{Z1WBW1%_x&vRN_^{

+H=}T133QD8~3ME7d(`vkT_YEp;5^Ng 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 O0000%HwZ{~cXz{562i+l|My%x zH*@#QXXf|K3007T0MLlg5D*XmU!}yA5D?x~At1b+KzaMRf@Mzn7Xbkq;j6f)id)7> zyLFlCx(nWQ#FUZ;(X!G3gXHgRBtIpUvp#WSzhtZ+wJM|=V^nM!+Mh-$@3h3j{AjH{ zauH3Y6md(I4Nwf}7U642qt_uHo`?`>G1p zqm#Sf(;!%!7rws?uBVB61uMtq{l>H0Trmm9aN+S;-=}fEQOG(&mdaskA83cp_E8M; zX@YwAj;l?=Sw`(L;-UyOXY%)Dv|Ri)G0?j8u8u$*BluR$UHA;fgXgN;l%?BIwMXUV zLh57VAUQ|L+u{sn$lfq~6e2docgcq1DkQlZ|F}@NobehO532{}Q9f-Lxo^(WWoVaX zYWYgqY1!7#Twj=VmIqU>7hpb0=~o3Z&vk}{^eijp`h?pmIMMejbuXM&b8MRK#1S>z zGU|Dk;8v@T8)!=|LFz_OM}to8lheLJHQk%EV!#04F_gKf>$5STjqC}0%r84K8W4ob z9_Wfbj-b$CRYI-Bk3nt52woIj&w zD1o&Va`JXrCwZ3>qjeR0ZPm-btXym91KZ7PoewJ!JrMpytm#-G5C7(*Qk7q5$fqeJ z>(tX;(Xg<&qS1E8>XKK^eQu+1NxaU(TsXG7Vm}ZSJX&vef}G_^1P`@aYoJ1VP1M{sHNGWz6rv9=M!5^?q1b?~ZFE~8uWm`^_YHypu=y#VtsEhPs4-za4Dve@UZbktfnO6DKPx>aq<%Qn=c1%=Vzyw*5-8NLK79LGdhcckrynQ$> za`HYQ<&hz04W_RPAw_h|CLA0;c;JRiDAHe?SjF-5E1ycl9Vt|LXCzr-ETopQr@Kcv z32 zp!(^tfJFJ`J0t(et{zE*ZC9U$yGCI#2CWQ|G|nKO1*I0TuQLir4t_tt0P{#H+k_IS zo#oG@fYtt@p|b@h%@N#5F{Jdeye%J&(qz`AH1$A%-6&K9xnL;}o(HU#v6E}v{?1}o z;NS+c<&jJWu+Be@lRVzLuln_~zaO6LkTEq%SG>;S#}L+Z%BsMgOU(iDeS@FJ6|dr& zu>RUNx&AR;o%UJdeM0E(#UU|w52Q@bE<(;Nnonu5Nc&%f?tjJiEizE@)*ZzZo+SK% zw%&3XPFcPYTQJK-Qc)MmPx&lHC+I%WoM0bSlD(r^%^eb~OedKJ%b0dd*}riraq+tA z(GI?-oKO6iPRDqP6a0zOhh9y;!LFCwSTh2e>~>bQ`ny^4Zfs0WPxbHWwpz!(6L;ON z_X?My+)ZY`%33>ShINm_W5XADwo7eX!|&PUJ)1>|_vf|*90gn0!DTsnyV}Eej+wR_ zv_a<9H&Kq7J9MR4FGR=j`%WuOCr+iv%au6NpL~SGdX=FR84hynxAm+~PBove2pjC; zT?sVXw1^@@&=9*n|inGE%4`-pR1`-TdInIv8wi$*gG#o-KX=?;?4VR82`Pbm6PV9{p@eS`&qGv*s^3ksZe{s8giqlhk=DZH)IB$zAiog_$G)IH0U zie?!;m!k!n`Oc=Xg3js(alNq*o@J}F@^gttzBVxS-(nAo+VN+f0Rl$8B1)OQkD%9?QoC|)jRPF)%?2$@(T{~|en$Dz~U_YUt&!5>hg!N=LYo2ixQ@b~~?k*nzO z5*#Qm?!a`wrmB_Emgkcc8Z3vG-5!Es4Z7mfiF2Hi1}P%+iX7&~G=p4>i+GE?zwk>S zF;2tY#ff5lg?5Q*AIzy*s+oS{lLRtHucqkxFQRhgt&unJfSjJs1i^wA{d6i5CvJt+_vyG#AdjK5n`BBtTN8^Lw09}xg5 zie@P)ElTwh2}N=~R%VI!)cZRkMKan8i3Fd9Q~*~B5#PiMc@yoH<4i&63LOU^_f5T3 z>V(#SJUmuumS(!>Fg5-nAv31{?rm%P1BQ4vAWHfNVSLiPqBoAvGe5!X2p%-!h#+=p zlho_!D5O-vsqZd~=!WfQpfInf{(tZ4l9xDT&i!1Fn)32jS&T|1aCeDn6eDH}e8#Fn zgd(X1;U8g#x;xIBKa>%r&U7CGb~b5KquN7UVKQIqfQpiMgumSHZb1C5^+oF)%oS0^UVQEiIT4n6msxZS74}O_(?7DdH6|8x$mFb1x&NW%5UPcZIR*X z$(B49hDzR+9{%I!cy_!d8#ZB^>EDbRMu>Y3fX_;7=5zR9CzQBtjT9>?ecMa4eXo+C zdAsWf!1+uJU7;*ArhGjW1wY+p$7}O!xOBD9>r|*}3p&2?ySKi+1B9O`K=MEU)0*q*1Y7QJ@@ny?qq8Mi9$TYWatHpVh-{P)+SoO^kx>Pu9r z{;F19@8EG%;2R=AS8d;8?2fVB!uRNW5Mk{JKC05TqIS^|YQkJ0?-Zb^mlo`A%kV+$ z7t{C2Zz@{V3B?`)`nNw0?$j%8BHZl$Y!^+64lPQ`cJn%-biEjLY?x;&zeR0+;7H>4 zb`0*L!xK@Hm3&3c6Y)=0Rm4S2jGQ%Nr|=~@9Z)wuDs{d?v;shVa>qJ`@oRqCuA7lb z@3Bjues_USSdAR1opq&{wXD|a(m=W8743E%n?k>+YM(50qK#85^Ia^~?L3V8I}xH% zUgm^3ufbBXNk8Jzhmhu&_n%@{dkm+5CWM^GkAN87H~GSSHI=OcdxdxpRz^%%CbRnP z75;F~S;{l!Jtm*s|;bopWp8oMakbr8es~1XY1>lxmSli2lMx^=| zm0}8#?!@4DXrek|jVLHy17)Q?NV0k%R)@jJUi|&YsVZE;qpYf;im4Z$^HWA~h_43S zB)2Uw8M=4jth8Bbsvoy9U$X-=4cx?ZUt+Hk`j6cyk4SWLyx6Y(r+9yw{?;kunW^Ks2IDys+se-E?u4+xoL4Nom5P9 zF*W|@iHiv>bGLeyQXE?uv}TuRvdEqg+6!ceqQr{)A@g6ivF&>%&8=3BnGWKni{ zV1B%+WI%s7!33geJwE9-nJ+6J6f@`mMwYXQX)F<(1awVeAXs1Nil~J7?rcrEP|hRi zPURPau+xdb|6-)6lU{@lhgErFli&b)R(sWhcjy}r6ZZ`ALgRBYP5Z2}WkWaQr+;en z_)#u@&?~iz9>}EoH@IK9t@GG*(>L3UEt8cerC9KuWVEd}^inUo((HRbZA-p3df$5U z)W6ZT1ID6(lqXY;7x|esJ>#1#35#Z_Z1MLVR~Rmyb->@2Jj<|2z!-y7yH@onAj{Ai z$|PV*SZo^Y$#mxb9Hg!Rdx*c$n*gpnX%KANG}N@m1In4}R<}-z7B%+qg!SR}t?Rq{ zf}Ed)N7nV}t}2-3jfd>Oq|I!{O%*iE&)rNn+v|}e0i}cuZd%&M+H~&Wd#EDG+WLDx zbqc1%b)RXU^+;MscfH3I3MH4HM4*cLn^U*dv*;}Kc*50_KDld({MJKBYME`$DEc0R zj+Y56JNfcbzR_GwbITFm(<4ePNOe+fBAROkqC{&t`10~GMwVRESSi{?6LIAucsLf? zJ2)<7qQs_OkMa)_LaZu8RIp?z#~#Q;MQ_0wsolY`NOreas!~cemB%m(Lp}AFolp8y z^Vu^xCju|NxtF9z<07KCjIQ|`EuIX!?GZej~<+S;hp(nI)!rxA3sa<#;!%!``|g5(R%Bh?OZ55oWF1!S}T!zTQGiz>^2 zEVe4loR!ghn-Bq-lSj%;?FCx;KF#8If1VkG{Q07S)&xX8!~M`tOI01@-44=>70TP@ z|D7l_JRqGBe2OefR2F9=*79YOh8z>< zUtr2?jX&W0U(G-P1H=Vl9}_OeAQ6=D!LC9^u_OVzwkCLT`^w(vA!K-%St1J;FiK+% zZ6kiZ$bi%^AXg{|&9Ugj>X2WPDP_S9qHCW%kbKH;FPOO+GG@KGaB;;u*WIrG>Oy&6 zN=g!y_c`T3q)9)tvl{vxb93(D&8z9A0(XYfEDL&dKjKSaMa>&e6uT$L;BpAOR=qF~ zKiCQs{2X|jqsA$5HL9m3()2b#M|`0KuVj#o_3~)O(Y=1%cwf(@@5|PYIXTVA`#pxr z#GmU1@s@uE(Vj31C9ebQ3l3aFA6C996p-E~#eY$E}El!YYPqDngc*%dvK=(f?tX zqiU-ROLyMVd$>a(lY>WCP?GNXW;5G7;I@sZ!<+|>_ZN}l4(n`e`t9kV{t!xK-8U*O z6Z{p1rvN2y{GA16&+!|UPZt4Q8~4#g|Nvp^a&**vE1 zzsEZo%Ej2YEv-HMTQ(C4X(cc*@?zWL-Y2XXY_WIkss1huMZVl#Ab1s&Ic4qn7{3ZB zE`R6Q%+nD&J|wIzzc~c7fZt->p}Hed03+=oYY}*bEoG_#(K!Q)0Fl@?Nt3sT_a)3} z;IBG$4svco0aHesia91>rehI&42N`7-JQsq-keE9`Ab>9cc5<2*I5pIF55 z1OU}Z7!e%k7nCjel#8l>2~lzxn3yi$x#4F^Gg0osQ9GhYuvL zwl4gv^mn7OgJUaj9#4MCc8EXg8aW$!>1P-*Dn;v+c=~{7%;DC^nJ0ejqmyB8$FgSJ z(O9j`BwSOCdlxIVw6Q%=+}ZAi$kL5`I@c@*epyU**rV-xtWR4m#2c(UqbSVav>DYp z+Su|;A6=bS+QH=WYKXUkhV$GCkC7$t-WJU@wnn%NwYRHhmx;Hu+c#Qgg2q_Xna_m^ zQkQ@$MUP9k(%8APBm9E@tX~uw7Y-{XIDdz8md&ofuH5S4^&QF`dA8HGxAx81fVu)e zMRd-4W+KpgK~|p;$WggV9+&?YzI90OuM@vMpa*cCh}Drtu-X%J50SUt+`PXw8xkm3 zI?z#Rt?%*ay@#(wU9etVNleok;?Uov3+b5vrq zK>rc5znn^Zxmv2#Ma;ul5%EWE3~^h3j%z7uO59F1__Q6fWONJVBgHhtI5j3Aw&T4@ zp9z@>baSF)7-jLx?TSe1*HY#&Wz9kx$>O2$G`r&ST(yZp8&wxyAeDkUK)b&_%L5eO z>t-Xjv%sHiRuYjfA5uVjibpq37>A9__hmq`;Bz0RZ(x+9{`XQalb7M;;igm`O%_fV z5AfGD`@p9a3XIh35}AJ@32WrtuwT#(j_k+w5`KQ|M=UDGUw|bM8(Qb%`5$3B_V&W& z@iIkAlk=wT>%f!Uq5Pp#ay+y#zT5O?9^HThoTM;+_%QvsQ!Sn?eWoYVnkDm2r7~Z_2Kbga0_}3^^^1h?ikro%F` z=bXWK+32cf*%V?^YhAk26X`gM5p~A7An8_zIGp zzSp?sloIB2pc46a5*cJXxf$SuLN^vk(nY6~KzjblR|R$ZEIB#bY0mrk&M!#uuN0_j z(oa$ee)F-K5v5{%ap?duS4P`Zxo1SY|9mk<>gC+K7j6EDj>%S~*~_QVK77dcV0X~z z`A9R{es(zZ21(w7Xh-2T<(r^g=lw@(PbleSooD*WW*wN1rN$W#!aSRPkdNGSeSv$o zDYg_ADSOuf1TBwmDX?B8hidzhml$6r%SBgo1g2Xy$vk zGBY4L2VJSPU6NIg8COzoNf6(v<;+QiQ3Q9*SQJs@3hZQfw{47F zDr&o(i&Ix`hE69n$!2m(MWj%e)_pK6y!P+*vwFeL8RrhH#lUqE=^vra_B@r-3CZ%L zhCZb!@-hN)c32g1Yw#rNV>UItjH2%gatUlUB+AaYg&|mdjhLIqIy!*dC7znJxWc0Q z5i2&UQ?T%C#5pf{uDR&5Fm;1 zNcp2g<+5;|g2{ZiIA5i_z>Az=+bBQ>az` zEurV(e{n=Lh+@MK?XD)!#=kR*z%K{*DsK?=dy0-&1xDr#olu8(p%Y5ztPn~Ft&7ml z938Oc$pQ3rO*h#g+_6m&C$(?(y}NmOK0On6f7=A@{P=tZiu{nX$(gs_`dGf*=qIe8 zB=fF2iLRMCM|@V)bIR zrE@-})E?C~$#A-}4|Tg;cW=93`YoAh~gsGBjYayn6|(dpuh zwxf3IhnmKgO(Biy_ReAZERnX3y!y2x+tRpTj;>j!TR#HZM(P&pG5f(LL*(=Z_wdMD^TDXMJYG{)E-@8>%$)w&tz4Cg6{v&{yU zp!pfM?>Axjq}{~^5R^X6spjrt4eym!2%T@?iqUBg@Xf6mgxZo#|CGlQl-Zjb6_77g zAP>o)x~#uL)<3uJbyq z<4w_Y7oSO;V>3UcODJ?Gn${=)q|$v&$VfJ*jP8v}mZ?X4KLJ-7+Zkw$D zL0?Nio6G4jl)zL&8%DNVV03-;f(Lc^7xAeusDl~G240CpB_3gN@yq4US`o;+@a zdm}ubv)DwTGILRJRtpf`NByqU5vj|8jSx(`;z^<+S|1i8A9}Gj*G3VNqnYeNpS+? zcmX(j&8}OGJ9R_G_7znGgCG(_1n|S7Soul%X2vI&iFMWx86U5rj>@9rkDWjd-wh9& zJqmaKANE!s#R(L~V{D{_)ON4MCaYX6KWz zr)}vry)RgVc;K5lB3Ng~tO#;qT91cP%V|yliAC2EE@*}Su=bN<^4Y|;OifE%D0#ds z5GKe*wv>)S8EEF>2-$eoo2TWd0yWJ1%yWkg56kCu8+g?>cRszS{yT@4tWk9tt-N6; zdKVKzsb%!Jbne8pM47dKyn*z7*-K`)*=js5>5uVB#)gq%txk4dlaKdyjrI~#1AUh6 z&oMP!ggY9Y@Donw8AEJvgLCn|-TW;c-F<4s^nf|2v{(KHT#G*={<{yu+>La4BCOE; zYRMZdtH0doyv4q#x#`~ZE#)^D^i(qY6f>+lzBhC!6A$U*=m6E~_Kz28g@^hOX@6h5 zAcmpHt9~=~%L~v;=5C;1@*etJ7h7~duKdU9g@HziN<1@AHnOLnjZ>?tIP{LYPtaOs z?SL*mw6P?Z(quHHkHfmpP*?Z5CRG>mNOME*_pdCcDN)vqdUKQUW#}* zipVsLF?TCR!m{@-D__28S>^??OMb+3>~ya3Ml4BiQSJLesVVa$S6^;$uS-e%f8kPc zHkPArF=XciK>5;*>#RrV5O%Nv?wc1>L- z?=K`dFaa-yezlUnQrwY>rh8P0X1ZVNJIQv@cp*d1ES;{Hp?LI{ccpH0e_A_ozN_T-Z>(@5v8yT} z2^VeSOd{+tnM3;uo~SUyT;6Hhhe~&;IY11;5v0$YZ4EY+!bq4r2xu|KZjtUhIeD0P zVaHd7aPQ+IlXX@s_I*516&*n01hv^P)6V->YwO%!!uu=_;p4W1<%w*rsT_N@hJ5 zNhT6fYp#{rj{#$x9pKaNg2bQr6n~o8Mix)l8tnO78X(`HlI#=I%hQKt*QB#}u~62L z{@nUl?GXsoy~!J#Erc?clIHUd7OAA!XHJA#bGcBzRalOd6H0k_g-Rhd(~~sO$d(r; zO6tmhp~?_ZHG1^Nl*Y#BYfioLR`=~mb@dGAI{2yic&UZZ_X$6vP<@j^$yB&#d&Wji z{O+Ke&K;f`@qVZ~59{+2;QX_`MjPvm2eO+*F76$UV`l4z$7O4#Ytk>9)#?&Th>z5L zjkpG@fCf+IXZKJ*gG?9iL4y0H^ZWz*pSy9?-;oX4E?QMA(coVI%~JGhV3q-|&7T(A zc+;yjWCu!cy^5$?d3-N(>w>E*S zo3jN9!;Bj%y@@BcyCH@x?Jo&S6?*G}y#?|$um%AVSC>gE>&uEKmQqV~<*4P-hxugN zL>T0oP!}jYNWYZ>gBW<+<``2t`@3Ock?54Eq44rQdNTZlW2oljyVAa7x%*WJaV!;f ze!iu8$W{7){B||}aBB8QCX?w;wZos>=ZL7iR&$>l1oCSB-d@zVs*=-%Ji@yRv~-(|Y&91twrAXBw?8`U#IWHUmfg@zUev-Z2Ml;?_) z|DA#KO$aB~`(-t+44j;mToC)BrV{haQ<&8U8jrBO-j=m%){oVt$>EKhKMxAUbe*qJ z5)#+TOSLZFqVDhMBAub78A=i=Ui}#%suf)V5;Oc_-xr&lxV|T`1SV@#yX38@?S9-8|f#ed7BY? zd&}AQNFR10bO*+WAtvPH3uKPoIhE5hV}M6Gdp7%f;XYsJfXK|xT`}Y2CPU6Mj*sye zUnq77q0fJHV6kMgid|;z7P_WmyiLbSSVR;oLL%m^<;N@bd7k*{*!cG;dMsyd3p&5Q z0qprEt&aB&XT3|9@(&xZ1g#OxA2mtB%1`zs#xJjAF6s;d!-HAjxe-5)K&KVj^>gM< z(NE4!@69`tl!~4Ie@;1`#%3`33n<7H1N|%y%wgj9?df#fYfuBis9C2&j88Y>zjBA+ za@if33=*lUsDNxs0h`%w^`Yyy0l-lO(&imQZLvTyC5%z5M=pn+G+?I6^Z7*?o~!%l zi7ZcNTq1}2{=a{~TOXMUH{y)-VfVoRWWtU1$6$LpP7?a_#95k+}GR6Ky1tq<2225!z;`ht*eAi0SU=! zi5*Q_q8|aJY?=~cbOJ%${;E8ZrW{Iv7^Ne>y1DnNxQ7NLC(7qr%Cy^5aGUwLbxId) z={cG?Y!`lWsINvqD##1kX1?RVQ5H+RgVp{mBX@PG;vpV@xScZkoZbyg#}tW^4j}GR zLi&+swC!6O`ifJw*{vtT#O5Ei-t8csNHMoRm^+6*`4-XKw{XB_pq=9Kh5&a@O6J7p>E7Wj!=67a_f{O3P!`S= z?ykQEC|5=m<0e^!F^b0>88mvI#02!f-TTpgr^sW#56D0fVX{Y$HSVl}N9kMo={lQ_&zQx<2qH!o$ zDppYWIaJ#&0X3^nnFa6ES)OsJfX)Sf({H-4+3-?3ZSg>}ta6%FP?fdS_N?Hk9=_ujT_h z28Z8F6**qC2#*y$6RYCdhP>z2J%_73FuJ+wN7CjZAxUt^P92`;4>6-T`6ZvST?cBG zqkErT_+Qv;{7cbqQEyhpQ4|+k`WlB2ntT^UMhUKRlig9=E$cG=d5gLVlPbVnK%q8s z7FS`tqB!IO9n{rVVz!v|!iFTrOm`IWzabnFq*x{{v0z?+u88*w&wg(?o9-9Io`_H4 zfR*n}Zu@o0?d9l+=sh#mfUTdU?u@Hy{J!Dqy$$fSpTh^kT4OM*J1O9RE-h|w z3P9WIDNt39U(YXGiYabZFe7 z#-_6JQFEHPF{q7{3GNhAarc2B^SH(=B+w>eoUo9ee#&5q>pV5&nHnle2`3H>xpB!Xz z2B{or{qc%>rt$&v)_+(!XFt>B8m z94{phlj6Su#KiY|G&>Sm#%92O4Xt*0HLqQt1l(Cn1#{Ck;wosDEzW6wTwuM8;s;%6 zuNgN~#`%uXk)+!c%|0m)Iil!YbSO1^ zD`g=l$}!hAdjg#q`1gV+!ntOiuj&>#hx@TUjO?bW(DxBE%cr%-WC# z(e87m=p@Zx$oa?E6F14kQ2dD-)Z?bb3T-vXnFp>Vt@^ogZ}HgBA}5UWx2jRKZwK?q zit2Z&hx%dP`c)6DzE?R9rO>6&wF6??)lrXtO`%C^qltgDbO670E6vu20DNLsd2FLgLWu_iSGUEfjFWEo!>plqDMFaIp>FLsQm22c`hsl&&Ie zRn2x2`bM!EyaWu^tRUISLCP@0FEta^lEg*w7QfsD!^3Dran4c>GvZT5%f*&U_HFsa zf@(@B_pAmv4^b1i3;I8FTP`-|%4;$YhmnPIqb?WmquOZZr7pzTNnzE}5%Qyw5e$F5 zdDT~XY+r|cTq4GViiYLszQDzco;F(0gZTBpac+1vy#H4U>MLa$8)0JRrP6b=y}TAC zIEfOhxOVXC4@mP^Ip^J_p=Q)3i;p(=?oA{M7towhN zci*-Mev%%ntyeUC_@s7@*t*@vL~5PgWf$#&2pTmLV9a+sb@4OWKzE{nWaP9o**e87 z@Fl?eJ~de7wZVGGN_fW9l~(nP1=3Lv7h!_SMGl9FJg=EoBfF>bWDC;15E?7k@lFi) zkaQJG(&_vYe&0FgO%`yuJcIEd6+WuS_7CP%7<-d{YdV@Uuihc^G;))7)krGhkQ1Tv z_k){mdz*A;=26a;jn9l`)^nvbYNJd|d%H$+rcFgiZSa5}Si83Tku`h229BhK+8qyXPUj8&j6T9->2WH(vzUR_PQ)yfA1FUxBmAVYkTaV3bFx?Fd4Um zJgaroA4Vm>AECezdmA9af1;~;@&f6u{`eTE<8czdn1RMbgnAf zCawjBEmITykOkT;_3G3E3aaXz1S6{OwwRI16Ep>aKfLlaxFLaf4TI0xdBG|$@fD+% z0TP~;&dH_?ReA_Xb@UAscSVV~GnSBn(ulJ3o@N{)jw7<{@I5$nf3n^rgAJe}wD0vU*03yi zMusTmF(6OD@ej41F-4Nri3Tk~lRT&~c%;G6HSZ$PlSotc!`}k=0ZhL*^u1-AUd?+- z*|>hS49-^BcK5otBqLJsU*G0Q)In6GZ|mhj;~&Bk<3mHH{a&}I+c6Aj-+h)_hXn)@ zxDfAkF~0bUQD4FJ=Y6jH-`5i*pUgfFF{=kzvwC;C&)Z_vxWqy$mIq^7yw!#X_oLk= zezc8)x*ymzbKXsGfYGP0ev^e166SxC|;EC>R(S z`Sg8DAmb{T5tx6x7MWE&I69}u;=?SWUu6aB@7a5sW%#{+#i`!;%IRFlvI1#b4CuBI z=ib|VV3W3v5i;@ze0wr$x!D^^l$GO~<6Sf>vzXWG?suX7+80v!TPW6$_Ou1JN=9~? zJz+@Jniae3^Qsj;L&ixP&GS~Zosm_(scMWBPE)kOAClmWm8dtpU;bfQbDG2hENHT!&mgm++yOr+QS|n4{MZ_vp08SvJBM&)0P48Mha= zWwY%iVv^gy;*IINKdnb~z(a$a(a6Uf|3(}HUSM!qLA@C01=>=J<+syOeGO0TjMbKM ziM0OKe`mB|ShxK9b2Tj*!rs*et>v&o)HXcWV`BT-Gq5{ZHv>nuAIG7}YQ|&o235F8 z;Mc8X$Opv16jz-BMw01YEDk@XI@MeWe`0PD+~dEf_)(-D^XJQhIPYNQKj%UPBWx1A zOSS*Tcszkw2y3=HpN^b8l5=nb~_FT>$ zhXA6@>ay4RZhFM)%NMra^1)#-SGrp2g#GT5zWpUg&Qa*w7zl*0CMgF%AiEbqrG?BZMLO-L z=guZWZ0DxSL(VLOt?JHf1?>uxVuP;5yl1#?kA{fTruh%YclHOVM^0C0qS&wtN~Fkl zbT=Z-NeK#(ge2^|T;H44s~A)<+rMvphgY(pw)asFrtoq^^LkAqqA+@G{2g!emfJ(8 z9iPS`EA)MCosBMiii3HA=BFmw3&Wc$s@v1vXP(*~Dy@VQ?Ik{oA+Z9w0+fk2|>!Rt@d%9wG(RAi|o5ZM+(5O#yAY!I+zf zOZ1mh_nS?UAi;O1>E0i~R`oR!VF#?nSvt{%^nW4KKriI>#mqlXTZ)yyXwDJYpruhg zv+$`eFDGy-pKZXs_0>Nh9p`ZLeKa~rB!zjLW1EDje4bp@A%JH(<5XhuVSb*4(}s>{ zJBa(f9z7GR87XLD{Uo6Ft>^rd=Ha!!F0{YZnP37Zm6-Rw^+U`z+&bR0IcmcWY#^EyloR zaCA^H!9}yj#RHTA#hxto-9(OcjONgoQn=ndrV4>d4nok~7S&`cxQn(9^*{#Xz-cJ|u}L zF)N;DWlC~o9n<1BcZ00BbL*xA;%r7y7`vpOTO()q&GPBxpQIJ}-Er%7hjLd&Tg$#o z&yet@GmQh)7sD@SQ6;lI9wo+6fk!!;GV}hQ<*NYd)AJTcc~pWjSone#Q3QdOg^}2n zVG9g|N}3@}5;im97Hy{uNf|~~40C-udUeDC3-{eh6|+?d*2W?%+%RdI8Xij0Rxn3u zsy!YlZO3WTFq%G&+{~JRz1*}wVn~(4)K6Rq4-&3U5l6JexTv7UUg2qQa@9@QV7?llzeOdoCiVrgLbK`lT)OmNmcApU)xFs~plj54LE0C{N1657S6%wp645O5 zcSbtKAjxoF36W%vi>bmJ~EtP&`oOlN! zmE;z~V@^-AuNZ)b>Wy8~5MUVFnAVy%&4}Fs$);_Ckb_tU zA6MiI{<_Qcp$Ma{;F zUzkQ;^4n%bsqMi$(e%q4_c2%XnNE@%ir_jAs~fJl9V{k@CbzOe8W8z~Y-Eg=;gn(x z$ou!=xfLCvCa2f;FyQf)kbfI_Dc)3kKb;po`&0@*J?7Ht@U3ZefrxPm5y?E~bG>+1 z3#Kx3_--sYj=Vk^kWsFisrY#Hm#t%U2~`0qLyB$;PI z!R2G{Kb&D+Z5iA-eB5cU2<5-9FExywvVo>iQ))cq)(iPOgGUhu9OZ7ZEq?P(;o!Qt zVbwaV^5DJ_d2npXPq@}n!dKbS5%HQ++$oI)rZQZf$Sc!1kI7q9h(&#AeK>Qv@QUF> z&uh;FC>N$Co>WaAQG#5zSx+IRBrxY<=e@tjP#^R3}*#s)zhjt9HXCOCGD_glhe5V z6)pJ))h-Z6s-;C@Cj)`58Ol_yF%a2Ide>HD4*i&ZRu6;}^>|^r?wzl%%n&-ESWMdO zas;|~{t&%0h~gvk*H;B-Xs9?RrsZD2ZA49r1GFrp_v$d1UJ+evuxGPGv|bW}f2dY? zWT$BL_&$+SrGh&*HPF13T*Gk_edj_7M(CrehPek)0ww!Dsk1#1 zo0!cG%{f*&=ngfsM+aw}^L1_%>x@Rn+F$>CE|^E;L-baESD@+IicEIzB(~o1u`ET4;LWnlIC3pwI;em@6?WJB=4b*KSQ* z@iBc8I6hXgk~Ow|aH+R0IpHPyAShB!yX+u8!9L3vv!5||3i;J|o7N0}6`Ve7XbgF^ zd5}GRQhZH~o&7RT9zC$#?QdrEvHo>8(x=LtOy+*Sdcdu0L(s@d3gyzb_^5ZacT>mJc7(vK};OntwUolW!)%iHa5 zqC8mJFa;0VKV5fStr@sOJ$s1KML-S(O$^Ur)vszcF?UhY?WRpEY z6Q@RRUCO=*Hu1=jJjs+kTTQ1Q?JqL5i!Df^*%?uVj1E5**}dHkBWr2JnSZdgcfNtO zxMQYAwEb|mW~=OT#?qs+EZaWHxwvwfl=SwhC0C`lGt9r|W;svhTdi29{C`ZnWmg+) zw6$AGkz&E6xD|JIC|cazgA{kSQrw|ffdIwb-HStTcX!v|^5)rl>~YSg{D5TSUURN% z&gHOlwEcD2Ff7O#DW}FQq#~u&^49P8$WDXw!Y*(ym3-x@Ifc#**gW~&fvchC^I?&+ zb43RbO<1hM4Kds0>c}KHRoLWqyHKzgSxbrxY!?NUoqWF|UN}j}rnjjkbU!ZO2zv@L zT=9pdQkG#4J#mhN>rTl~1+g-+t8$AD=-}vWPVgtzMDN66&{xcqMK{>{MH}YusUH@j zE%gpXV}rP|B(vBY_+wAiMUQ8IwBr3{e{c_;vbEaduRn1@ccMoKUsTa}(j$xb>xV!i zJGXz6eJq(dJGx(V!;c2G<@+{|5bco|YBdoolVVigh0m}k%DiQkBKW!aWR4ia#LcKR z%%p!s*IIdG-1XOAkPUubLL8<5mZVbhD^l(a@t-^7D5q@6#22F+DTn!C;oQS}Nt{){ z{kBp&N3h@R$Hn`T4x;GKp;%}qdxx2xr zz=6H0HZ4@dJ0PBblcXtOXj;4F=f|k<`xXq#R);`XGy$mh`vz7PBVtASe8v3fME3|w zmOV8KKRc4H-312xB@OcXRR*{_j6-8Xm-mOWcd=e6Tu=`;O0OMZ=sTGb8VMu^YaFiK z;J~+GlPq(x#kC>5OX78Uz|hQ(-;baM;pC=T7$SECm7hjN}l^?JB zC^qz)5)atA9qLdLu{8Yp&P{GS59d4TG6*ldzVr_#Hx^>HBvwIlVlQDymM_mcmVVzD zy<2RYqM{Ln27^nuuPOCTSqC4b_H1~(r$TY!_KqUQ2ca{N50)EYTi`)XDq)+_(nI}1 zDp37OS^<9tAS6qek2$ScKIV=gp=4^o!9?Pyqjh-I6b2opo$=kj&}{UiJpMbD`ZH_Y#ml}iyc zy&(>DhfYogiOHgfHKYhic!r{_qM!-P0aoh@yg(!Z<$lVJ<)?c75wG+J-Tg_7z)vnP z#Dm50aKg*$WcdRT3e?)(K`X*J49>2y5XK)AO6vM{*O5^!pT?hqxO%losc3YhfWikfIx zy@?UpX!eLeAa%Kx-;$Q8MCkjJvon zfmcN+Fd}fFhtPm+u_nW4`Ksa9_)*F9gE5?{oz6rpV;Wsy zap7f}=yE(NF_nRiZX%c7-Cs!!nVl0esqVv8A4sih<2XK}%sp)p=RE1c`oA_1pYxJjs2by96$-Q5*F ze%%A~rd`wqH0hX5S>_N-_x6AC5yI9h(rg+xPmRbi7Py)E9bMH(u50&mH9Q>C!ct|; z_DGhdNDpL$7$ z%T~L*4$Wy~5q+wR`*QyocnAU3Xs1~t4$D3CixQAV*q7rP0vuFtU-uK9deN`zQ0WQ zc8btfe9RnE;?JfoO3Xegop49+!`UHB5nf=cpY4@(#!?g~V(sa5uud$^JcxSDvq>Di zG)zktQ$fp4t-~@D<=%ffD>iU)!xBv7qji^lL*hrFL|+Y2LK!#Ggc2c)DWzT=Ea*KE z2t)fnTvE@|JtAgTIEl)@*QCwEdi0d^J9;m%`Wzy?9^yWgsWTXEgPHKdFD-03hi3HH zU-`_-vZFP(%wofn9bz6Df3v%!>LesK;673VMJ$8J*Am!f3{B%&jKI=qMGqrh`x{3N zIVKc+^>F;8XaJ89@2UuEd5c>QtiLZfl|&D8HcA4aJUy}-R%fW*0HXY@=HdYR0;M0N z1-sd!J!F7rbqt#LS$Su6(IS>3@IND2Gz?qi5inh@uHzBbN9%^IMT!9Oaw+dgv@Iyk zk1IU@Eb89E%8(3;KjoS=rjfo45bub}oJ|?5vYf5H~g6%E$s0&rXOnh8xe`RYMcZ(!tGA-qzx@;!YxiHYa%) zISw&)9nipO%+e`5g8aQK=&Vy&X&^}o#yv64tXNX*ih!nm$xpvpuh#5FJtw|skjFnGv@$_renS>iVL+Db-EdrVA*MwCXyCXF3u4rpUGM;#i}-x7AP zQT-l&WLH<_s^!@tzR6LLf`{FFy??Z6m(}e5W1p+Z`aAA)m#z}bIng}OnDIk-%wQ&qO(*N{ivK1%T zn$8m9m(X#?`f$;8v-^oD!=fU6FT(O~i3uSDK2G4zj`kk0uS7)?*bo+&me(=lkDyKy zI?JMyi6NB&SN3up@noMo2mZ19i+piif_7F-m&u8!zYX)-V<%#Kc1L%6Q4hk+>F7-+Z|%1ZNE9Ri zSH$OwxUmXvC__0gR5w5Q!UUNpJtAbUv86-$q(Jg5#^~QH7Nh{fG9g~kK0&ziZ0?R+ zz7H+?vApQ<_#K{}bQ(KESeUUE+vi%ziNm+k2Yb6znMd8f**jhA{(k=`Lu?dBJ}?m^w&LWh*lVc~D-+}U=~ zs;atI1^gIBhXGIH`k~1QDHBltnB35y{N|ewV`Hc5nT=52=W8jMB}%6zTiT6Qj5tEO z(+$C!-kYX2?eUA=vXL+LB6@T4_KzfA?2nG&ISVT=*JvmoZ1m3$1U<*+Y8Up-e)qF7 z7aD!#?-%!B3@O2V9JYJYCw#VhWO?p?g8Pwst1&$hV>RxY%R7iFX?T*aZO0K~3y-&= z42nTgIx{r9NmXKmK!^VEexg<|r$t4RCXA&)PVCRN$PvUxE4XX;@h3GF4##~|ru z>puo!DA-FUw3Abj(Zx#>%a=|xH-m*rh$VIt38hBjwjaX0k&qQ)MjkwR&HgrI|OPvAIuw6JrEJnb%l0a%f+wM zbCl8)6&u0OHlbNWD|x(x@!p|0lpbR^r9VB9tGDnKckq&T%SG;qu^4cGJ=nN%F-6us za%fjmAezh zIY%{tF)annqk$EmNLozs_pLc<0Y+csEp7Aw_h|M1^3fqetL{TnE(9mtuV!P&dP2o8 zNtsY}!Y)5x3(6HlDm?hqSQ<8j2i)6|Xm+M^2D+eblkVV4yCO2_z)_R%b@lzQ&9f>g zK>#D0*nDdHs6i;iLj7L7oe?he1Bi0{^687BgKG-4xlJ&$AgD%}Pt)u4R~wRA+HpMB zlEnRR_?e$$Lop#1U<>7?cB@cHhVO@jVQ~wi?*tcFhfeKCJ5FN;-cr*ko0o9B9$7Y9 z$aFx4VXDr03R;fSZ)wmk3K@0a(2SOKiP=B19s`HT1%B>R-XcZb0V{^?vZWC5tiw{S zVL2EPpE9_eSqznG-I!a}NImiK0{@hho5qvC>JW%ZbwWq^8CLo+%zt|kPxE=ecxKAY z3Z;^PdUouCgv2aGHVeqA9W1Qo>llv0FToYJ2-nzxI}+S|e!jmuC&ZoF!kw7LOP?h0 z_{6V5Z{GYU4!&94jH~mlNact^4!z~P?50v^NXV{{B;di_$T)x_v;Z~m6gwZUvhua2 zOPX(*(Db7MM4FzHZXD;@;{N=fwA}x---3G;@^`i0uMt7h$iu{`uq1Y-50d-g#PdSp z2KC;3F})#N*yMotNE`oEE#6B4FmJpvI&;{q2>?A;lpYWHNlM4LbpF+{dp>SWVJ=Le z)N4Aev28XO8J+L?`eT0Gu4F@MD@|c^{NR43uNCcwQexDKLCdl|oh(L9SfXw53FhCi zQUwRcQ=)*?$Z+`a)u(Jh?riz>ppT1CEiEg(eP4Rn}IPEja z;83~Hmn`WxUl4ROW+Xvw=;2LsZW8sK?`}vsuypn7Axu{RaVmMWuk6@U^taTcx9g!y zDt0Z71Q-kHGbGI~FAuSGL0zMOE00Ij25{-`?8IDd*c3Ux*JfC<(vKScx>wZ{!-0KF z_I;abStq~V-+0Zdg!*{sr*GtmJ?B3T&K#==v-1@D2N)CbKjnw>C&mKRAKX9;?$_0k zG~Pjzt&n9Js9&VKdzf^bwkQ$1Oce>@vPyvFQLe1C=ZjgxRQD)Qfbq$XHmnNCwZNd* za?iC&kW&fLkek?VVnDvqkh9YseOu7b510HIspL9qsFanO6L7s2>>Z|s|1nrXe$jD> zxa)-&jWDZ(?m`a8Rm+a;JCRmlBmUoPr*m-LHEo%qos;S)J$q&o(Oh7@Bi{Yzwydo; zq)>VLz8;Z2s#!1mzd*sVY2YL!bC4%AZSQdWnIYigaAKcGxOI|rJ^rANa!djH{B)uQ zfch_aNB2LeoHii&0_?vmsf4I-<#KOFb?xi^bmU;I&x9?)qKE|k$1o~Rqzb9mkkb<-^7 z8g~C>J0Dphjz;XSL{6X;*V~s-M5b7!4lzB(L&J1mYI$mJ!V@|hRv*VtLPqfSD5-9# zY|-tP)s77(IoDmRdSf1Gih&I~^|Vn!apjD{qJ|h%Px>?(+3_vdFOyRVH3uC^%~A16{RLre+h zJZkJ7rSl%Rmok0cxNpu$dSXbKt9rh%XR)4fNPW*ap|f8NU9EKV@=-20@|7($)!+Qf z7iO0I`msoFiiU@<)v(+sVLB>BjC=7*@M~fuwELx5`QAOZn1^8eO6V_YS_PYP-cbd~ z%U(qcbUiZB79G9;?V)3k(mmNjN>RZ9*AB3vhCOsj$115M^593*Q67{5dN`Pb_e8mt z(|PKZ@MF#Bd_u=M1lEPPM8&!N6~(Qo%K+PaA%y$Dbo_9~pJDi-r1i z;_A-L|IqjF3H}kgRk zW%JIcH|ZaTZX=W2)Ww1i>|OWx0slWuYP2bAR*7e~xy|oEoBGJm&qT6Bti82e!{Cvm zqDE}_wlTvjQI=La*iv&;(^So6!}L;D9*L9TfYK%tjkNrI^Ui1WDDaQ;Yn}wdIM?4N z_1YYRrT$O3Vfz@Vz4J8IZSz615$>-mtEx8NvUpE2pgawXg)2zO>}4rz=C!#%k#w50 zZ`-tl*;%QQJB~5sxcNRf*Z$B_C3&P9B|~inmD@LAl&|CVvku)W4o6JL#EL#^m+t_( zA6Wdo8@yaj+(xD(+7vP6P@;47r_j1wxRyB-D?j3_nWqm6-tv8n^Hn#JgTg%j+O`eS zuU?C-7o+&~j8m^bye1;%1!2CoVP#5}ut*LOI?AN#gNPLdRZ4ZJ#OZ%&NX-W6K!qt* zDE`{_7-wHFrOli?17oxKEpg0}MK#epTLAf!K zF9ohNX8+P_Xy@vH>c8RRYc=?oqQv~Z{YDNAS28lAxZ=NZvUDfZ|H?JI@OK_9p%xgP zfK#YL(q*;<>!GWe^V3Q`k>d51 z>j_5&@rw3Vt3xrS9Aq1SfsVCgu1t3P+AY%aFA7!2ST$);1*TY)2eabCa6i0o zGyurReB6IoM=>k@X6GCtCIgXc>018rX==~n+Je8$bmk)bL7fXKvttuHA}`sMrU^SP zU0R>;2IVKmIkk_I!{l!ct=LG!y_#C|pNVQ%k~N-fLj`K}f;0Ig!PGD5gjf?W3!BkXBR- zI$y&02lA(?akf_pS56BpTT(oZDg3YY>UEn5 z3Y{`~F2Di~myS~=K^~r)T$>B!n|G9P>o}j~qn-||D{+cDy^E{j#zzJZWLlcFYo%U} zwN7|~hh6r+`{5kwV*{lml|=NXo*yd~f|d_M5$ta#okw1t_%obudOzlyZ~Ak$nPevG zJ@Kw7h_bW=o}~j>cqZTf$}O#-Xt!_6k7e*Rt(trA$qFjuLIh)0GhUx`4?vVaPm65F zS?QzDN`d0_`n1lUZA~0cQGiPRvqIsP%=aID4@U{VWfbexH<($b%68<}OiP_w;UTw` z8ts`w7+JcJMr}C=HE(&`ze#WYdpMy$-hqJ>A27MZRKn;IQZ+FZD_SB$K3Ie^M5yln z1%Pg{2Xw<9`JmyYVzW*GsMJw4V71|%T;Kdd5?#nM#Qt%ZUA2EC4pmq;d8@&T;K)bR z{Ku}11M3n<0MMHZE}|C6Goy~!EeGGwzW$TaB&nIiFxE8BAd@&pyMT6aSk@9Hun&R5 z%!62LlmE*ULz@xOt~F(K`fekP8PfmcC*T&p&3TUd{(Nbto;*?Rk zVI`NN?iOA$cIu=rFmUj6I8KzhBb*)ggs2_Yt!qSGI&Ji z$?4KV+co6fbRA-*%d^9wRnyjA;7ZEyI$DzX1YvGq4@6)`sX5}Xg~lWHC*v#`1`ih!;t**_|BQ3XO_T)gg8g_xj= zu44jxN+PBmp6V8hLjQauTr+dvl8LwJoa)=mP7sDtl_A4Vox!hbT>0^LCF3wBih1wb_wotelQ zRw$TXcA7LUa0n}BjmzKt#}OdL)$3Zi1KQ}2r=)h}#ZBk*7=vAmm{miJ1)J}g;cvgn zd`5H&j{61@rpZ1v&X?Hq<1H|=#M{r#xf9YNrWDXRv$QRB9}Ld%rH^bq4mW+YBu!q) zG}A~JuNvi^FyUWeV;df8FxTcI{+=DLbL%=k#WJ}{l77M7!2GlF9tUR*LhVgcNOC=b zdp4;iRlJOulf(vyCT7HkA9bPncO@S`?Hb^GPm?hRUxU zCuiO3Z&e5)70eAL+M3}2Ti6B*M$hBJy(&~+Ple^uQ8(;Tjg>+k-aVZNm<2A$%u@BD z04%TOeidjTLu7P=h0)FojeFEWq`&FraVqF+TAfa5L!3b8s@0%ULoIX8S~A``n3B^< zRg9>9RGU|8&lM@(k9gvT8OJe_vjgjuw)!=i-g6V`FTFBf{J+FSq3xtYl<`GMag54I z3TY@If8 zN9GeWsh&ziJ)gG5S{qwqLa7_ZUl0UokZm$!1sr4y*MJLZ1C?to$ zyU&)`lj59K-P}xB8D-0V*$RhuVH!MVjlMEqHjY5^-j)MCj0GvR;b`mI#VN9yAix}l zb(R%5UxJ-k2~4)kt*CrIl>Fp|T$W6dzi1de+6cdFvGmQPy!9A8REpeKioC7(uSmb| zX1~lsU^?H5svb#2o~AZxeKr%bUv@>_qx}6}Zp2BSPP1>!-)}b_J>LFIysl0>pI^KM zY%pClbiLpE?eM(jcy8VZ-{1J3RlR!bWCuV$8kBsuv5f8^0h@WGui0Bmw=N>;&L9bAJ2cRmzyL02>wF<()8t@oQcL65Y-=wpaecEi zwQZg!9!Z{7Z6FNi{h#~9XkGWEEVJUDCftiuZ*$nywZ(`LnZ~w(yT#V~s)3v@!39ls zk^ANCv;46lOYN`x}?$%THHFLk%7v zygaRb>NI0*Zw)WLg>`xar?#txD<0PlM;G$2UO!;P9Blc4NEEWzS~fn(eTs(5ws|UZ z2=7D7YR?vY*_$aA$2ngQ+HuTg(!++DJ>+arfZQ*^x_duZQMnH+gn9%PK z`n~OSy)cN}AO!4RykvI0?vTFUO+2@gKB@VGm7WjYS^~CsUQ^#6-yZ|~EB1_nw-;WY zs-ET@Ua>B2-j#GWFCZ66d-a9w01m1Y^6vwsR@88F0gs+G}H^NsDB4s!> z_w+(o@^6_CAX%E<{0BxakP;5Pn#OOawz|PCcHU+)P5JFc-xxW=zy@TH_SR)P9fV$M ziJ2gZJw}`s44K4WE;$VS>X7%lE@Z?RccPSgPQf5CGg_!IGSLOU2=ltW1~l>eqVle7 zPzQq2P@7?TVMXIPUR^=5zT)jE*}hP*!nTJj2%;g0u661?m;SoX!r31xIA~+HZ3@AQ ziM%A6OG>~$cf`=?s;dQ6FRjQW8JG%ta`86C6>2)jN$`+pw9C|8rwz_GB+0EH9#g!k z1NsfOxAbg%QlhHRV!qnetjz@-syVL?FrVqB&TpO0IB%}=kKWj4iY|Klh%J^kTpol* zG1u5}Dy7uUVD(5+WocVa$i=Zvq!%eFesND=m@vz9IloGzRMRy52e&_I`5WmdOTBR` zKwYS})uAQ1sc z1J}f!jVaq^wq-CA)iq7FO#Q7LfeIaRc71%Qx1x(*gnT1?(McBC8rOMVw;W!&vmev` z`=y9I9!&%+WWVn_yyekcWC>oH8$Bt#$4|VBRy{-p?6kdadysng-(Ngtc3tAV5B=UA1_;eoJzpZaTCu{Z{eA7CW%Lx{2hDdI*0aOG1w(XPMcTLQsB)nlF( zf;xO}|5Vhnc7odzIlke?xx|nBA>!FHRuV~tmbFD#SDIt3kkdrqQmOeAJ6AK&hIL=( zhmSsOE?a|xt~vY9>Yb~Hr4;>P6nB-G4$VHu#TxTT4c_Dri@Oh~q>XDZCZ1Fi?aUtZ zaSvge=nQSBqa@t;Ue=$FGOJ}-Vkf{U(YJ!Pkj~NbhS*4^sGOStnHHavh!R7GoP5(g zL$e>X75_3Sf&XmytK|feS02sw)?w2kU(-n|3U+I`>O$spq`)_(X{?{ZhgJGS3PD(g zJRC++HlYdC(({m^?B-Dt*D@9>i!J=D>DIz@jee)`I%DalTC5kmk(z!O2 zjMPhw2P?DR_mrr0_6m$iZWxvLw`Np~6_=T5P*ZsLpyi4#ih7P<%q3t7tF%NfL2`SN zs}enBg85JrOh6GF9yJ-?T3{!<*pDqJ@S0=v#dtru*~$L%X647>Mps?Lx9`IDGgF)` zKcqNGr*_l7U-Jsuc?*O}zzA-#r$84Gnl~KyNZfOC=G2zY_zI^NXjpfQyUQ zwJR=9{jNuzcR)5oCmXW!e$GNs6yDBNw*z>pC%v`Gz6;uZNt$q_VUkaKNMP5Gm3cg3 zCm!6+4{`ch#l+q&yv0tt;zW-?`$fU*gO{-4l2`XlN*Ee1iNt!h$9{-LOV*p5AF`yO zln_jK`&XDqW=>~z;Os#-rA6lg5-P1Q1_F+>sjvUF?N}|4P+4_ihGaOFhA$i&U@mWp znpek_&Gi{D3@tkznX6C>ttF55j2sH&uZ(DxW zZn3#Tm-$#ctJosXDPq?h9c)ZrjP;?`@aZ2kU~2R`T8e1opIqbE@jyL0YiU!F1r@*V z^r$OU5l~hWb>g1(^30quNISN=MOp!CdbnaLsC~<@B0}rdShG}+Y#>UF;An>QIvIXX zVCaz>wX9uBJb6=C4KS|xn1dcRZ4!|c+AXxSig8(Cw+q>h3}#JMJ)UvLi(up-kANQ7Vz|b zt=DDoRA1C!^u+RRBRDbmKAQbD*Y(73!e^WF0}6Wm%L#QJ(M2X7N!?KRNE!qA~f~- zFs22H2KA5d4h2ey)NbEXW|vXiB$%5=GZyb0ONq(Bf+PB(Pqd*GI{x4G__zCh;@~Vh zaf$9HyP}nPTs9$Ps?WDZ#O=y{WiMu?1Xld9|2qS3NNp!#@Ms?mMxMd1+ z1D#-+%#CQFWSu?~`C1imEs5dD^{>0U|QvnlWXwj%rns6s;( zov@@m;1K7_))+^z$5yvt%(MQ9g<|tzrc4cRIAIxKXFltC#mmM1j;%TTP%(dX!`wH4 zvtkA;qp>peX?FWrnvjbzK0recefSwm6|8Z{1i4_aYJ&4u^a%iUTgvnO{5 z8z^!4sPds;MOaz_m)5r}9{q5HN}je|oo}#0>aj*hV6}j7>c)GME4=>OHj=uaz4wtx9L?XmB_BX~a$+gFxlWnOdOTN$X zz1OLWjUSWo`C@xgY2`Uag6N%G%0ttt;OGb3B?5sN#f*GZkj+I2fb4-90BkG=7dfzs zc|@+v*H}ErH)HYhn8!th`Y6ydi<8U(AT30(F=9g?Zy+A z(c^iLogZ0c*R|Fg(tDjRTjz7>OJvoHx#ts^eyTwE+pv;9`>y!wJD6oGu7mU;kn|zc z;o)iHZRK6d(+*Ot@aTZ}etqxnaLJVk`?r<@tyU_EVvvqpKXidOrak%u8eYHj>;|T> zRH#=A4)7D8a(i-qaX&$`y_{*@Ch&_H8+OS8#?wfC5+K8$C%D9@I9GlopjCp64*SFT zz@WHA`KK(QX0UOSiXmtmeOQ7JMTJl}p`>}@|Ga>;3z8znM%`5fCu7Byn$5@q6upZr znb7`$^HT99Aj=NXM9k2#=YwW;YamXXrOqccCdF4{-!=RLJ~uF_J|iA;$F+Rz8<>Wq zLQl@z%)s=#xxRb;NR~0H>bJC2@JGmKJ!G-x-R(l+F~iHgynqDm8kG2Bxmtf2%22Gd znrC#7(B3B#ElABs0Ty1qYp!1#cA5I-NGb;E7R`LnY0_B#f%0b1m7@z}wyXY?uY=U3OKtBTS-SS!$1yx{-#nw# z``p=0EnReh;1S!&(Qk-CkE_y)mp^G>^Fym@xNOY-W$K{&9t+F`O_X?h#ukVg4JjnK zJ@qLNhM+A;ncB?5n%LZ4(p`(;zr$^|UERNAZCu&BRT$lK_z#FY>G_W=lmkvHulpaM ze?D6J51ki}Sr?B}B2NylPa+R)S0@6f{{Id7rxS0dBJT%pAfFqA9jyRJz)i%(ORPta z$OGW5m-Ouy%L|MDOu*fF9$N+E*6e-*-E`kL?%)58X*m4KC4y|D8a+0zu`zk$Y(lV? zi|%~Iy}$5v3VlHsS#HRcCdy2xDs1WO>C5h>$PZM554TV#Wn7HerGnwgOcR$tmno~b zaJl%#QC)3SrX@!_iw*ZHiaN92Exd^ry(C|%yaIfw4z+XIaRSx^ne8~^2U#@Ml5Z_2 zi@Ns;f-R7u0O~k~tde1I5?wsbCicuS^;lFwzK&Wd^%56%+IY-RQ=BSUlcxQ$j48KIdxn|1S?2o>jvNB`>4Fc%Cyuw zT5M4ej&kHlIVscVpt3f6JPI5)0U-xFEOcP)aquwPs^$K_Vx|F)&`i-xu%qQ$IoEvgx#V?_j!lcG3@%5am%7%6DCxA2ShTR3AEg+UB8p| zT)acYqFlfci~kegDLQPOzY~8afb@AWJ3PDdDC2EXkQVtKY)cO&b1 z)O$muDHDO5ZQPx&IXtE~yj8uGy>Gh+AGo{?vpg;lIb=D(yh+`2;Fc22PhVNFZEGe_ z5(a{1wFZ-Jl7`u#XDj{CC^$NPaQ1BXv3Afe898h4`)rr&=;LDoUz#yE(rb4&ZH0pA zC{l|+EvRW92TGw8Dy`+aM;OJT8#290By{3`a<`CWO}a1G`Qe45Wzf*o3T%f31Z%fP z<`e0nC*~jfM`~}LXY>7;09(SGgqqy)f(a2~4JOGgxagp)HCW!nr_U_AX{%Vhi})Xs zY>QAV>`Hd@2)#2L4A(5C-N4fWpdVMo1m^#%i!KFtRhRmwjM49$4yQKd;Dz@1gN2Jqg zwhvGYvfcgtk`6?-UhJs6+UNXxo2eQJXN0MTXFNjMK#!CEe^xSa5m67ZVwmJqN@`aUH6=054c_aU;?d7Z*5=a48{lUItdHqKWysSY^s zhi&JQAn8UQv#sRB!cdS^o5BQMN7}UGmG*K(rs&i7_a`BAqHaps?CzG{JwZ9H<4;#n zaVHD0(z+a9*ruM6lMUeZGrD)4aCqjiXX9k*s0qPCkfe&MgvjXyE%Y`3j} zrOG|Mt{T!!bk`Zd<|8w(na-Yl{J-|@u3d(~ay{HiabFW9>eC0l9_6n!oKFd19V%KW zGfQSo!28hMpwy=1%u~tbWR0d^4gaPbdRlZWVWnb=V~5UlS9V|TkOp(Kqp(V|WLBjT zkpCF)Dfg}22zG!*%V%Iqm>{B|2+N5}+Ac6Aut{Bo;e zLTL#ky386U*tlPE5w=rCqfN{Q4kEGGzW~S3y59%N+b)kWBU;m)V}1NOc`@qHnX@|{x1E3Ew`2Ilp?D5_a@I0{kaNhN>W(3jbIzD(=4Y1&!QI_w_#mZfVb|dXD_3-8|ZNOdhM|hV;GV2K7QHk ze=5+S_bjmuk1SbwW2#I_;AMgS)Ze2W+@$Is1AYcxQYGdWXUDnfSP zzL3F$l0P4icn>z!b;d`Ze&VVuuF zf_T_Kue)+P_K5WJQ&Whp`&(sl?d(VZXh274e#g5Mm>It6f?cBlZqAEG0rx1xs+bZG zf(Qu(8%(;fa6$T38cImlF4^n*7|ZSZu<4AxhY8dBt|`d}#fqP;xfHt*$8Eo=jpQ(< z1>>XVcXSrOEW4D>A`!>r)+H<6=t|1Awj~_4bK?9b;r(Uf7l-5>;lfpTplDQY_DAFT ziKoep;HDEd=DcsxVAG1CVqwrPZI*Egu`H!)`LU{>IYb06+?gNf!`$N-5j<~sc}~vP zdBc2RJ4Jpc{3Y6IrGBjWk5LiZ>#bF~mRah0O}Bd4FVQ3b+&ojLp$W6zUeDoHIgS^_ zVvPoNn+I6$Slx5CEIpZ;rzkP5XPmUaH_uM~*SCr`F|QAB*bJ0Ij}&*>|3i1nODSt; zs~He#RS|Jjx*-&{d-mVWt49B-%AeJ+NfdruHAP%Ai&nP@G^tqb0tbps8h{f;ByQ^a zv;6cfaM>`tCZD@oxg_8VF{jc9K5D5&?oJXE`0IrAL5)vl;ra`~j$FdrJD5?9U1SUQ zeEx0>5hoTB~I;%4{qGVi~ld)z>Ob8BZDpv;Y}lT4soXSDgS%hreS z2D#82tgnxjfkp(FxlHepEE_hDxQ9C{hlYxcrPJA8V`Ra+11h`P#B5gJPK^YQ%h&o( zW$f*y((KZn38`0fRf!be8&O8-Cgw00N>7&wDV{d~AMEd=d%+o3^=#pMw?x zweE^27}I)ByWB7QAJr@RYH(lIM)Wh$j49f*CWa^o47E%jU19lj!Psh?u}Nb}@xunD z(6RU*k_WLzWVy^O1v)BTgrx=PXUDi-t+px!+QG1?_dO4R;4ZF4i%Z2%ZfwN1U<0uRgzk;TcsnVposaC`#<FvYHzW!AHE!s8jD(mS7wsyxt8I9UhbODRcEOUh>&KQU!e z$WC9c{8bawos$c?kT zZU41#3$yTNLeJ*yhA!fp%~`UbM2|s%77M&OMh?SD9|uc-B5&xh4E%V4iEFlcb_m|+GoH75>QOVJf3S)ww`CHH#T9^=(>SS#A}Dxh6C zwFB#g(Xa0fPiEDb*7N78sP48p#ps`#Lo3qPBg~QjYx>3!K`} zP~haisaXzFEvJ;ooywRtFlV z@cWk9@$n13rz_PeA80Z|!R^}OuCd%I$&Y6|GQxbNm z$DfvS88E5Lj-%plo@0hDe9YqxLPm-0EI~LI{(e6l?PJ!Qo#G+i2A>E?o&f66ChT%sgse4l6aMdz)Wlf?B~hN1;Q zI@G{^<}Md&SRc)j8Um}`5Ilo1f!hy z2*y|%{=g^u=HJibKP1Op0@+leuKMO*4oO6eknW$(=q@{ozm12WdLSR7oL z5<$$&ibyvz+;wuGk~+Iw72V%||MCokY$a?^YFL`r0twSJR(#0H=v4D@T!4w`Ln{Ug z|Gc@tlSvGTWmwCD8O8UxE^!ql@D%&1EZ+*~U9&N2UKbG>ZpzZy!yt$acfVWA`vs=} zCYdV80Q!fFiTJbzliXNLyRHgHCC8-?V~@GfEqpxYzaknYI{iuG0gmsk025|PhfFJV z+$oKk0Mj*2Zfk8en+iz7R11Uz;HaPp|3En|#i_lPma zwypqd@;M|~hqD9cH!S!s;p}kk)ELeg1A+1|T|e0x+eA1EV;l}mshv5V1KULdb2dBR zyym;BSVo|7XHJl~g@ZC;#mT?@?=G{0DDhXR8{kGGazj&js4c$31?o3oPV+5R&9JE< ztPy=j57-)&TZgW}s$NQIPOutyu^53wF5majylFn&?HtBgA)l%!H%kJAxBh+-524?W zzG7|)BGCz0(ysFPqpAu!rwED%GfKyzg#c;d8Iwm*1Oe}97Nq5Wys zCx#c&@-=Ux1IuRhf}ulaILaQHk!X3ShLlDu-aN*^HKj5Aj_#UsOZx}01eCpd-jCTJ zHS(UpF1>iZg!$F|ez;eMFtHtE5hPw7chaBi=N_F%#MXGYA{Y<2D~ECA2{g$>GG3X6 z8Vkq)A;pxy8xM8@Moo$sovVL&oU7oo5~q^naD0-#mlIrFy6^48wPJ`!#H%Zn&eRYE z=eLA7dV{5keTOBl)^nW>A8!5608Uz|j2c05;ae4ufWD$QdRSU0Fb+mZm*8~5-n>MRr4LDc2tKO4EVO3`$C(_(Yl15>vV1P)N!#zl(dWb(L6`upEYr75t@+snIRMdloh zQcbJxG?fPHQk-c;e;r&0>)AN~2N`B|Ky#f~T=KSE&@T1d2P^-~lN^;6`+A_~yW*6P zF~yaL$H$J*B<)3iG;|ohxf2y;Oa^FoVkA0 z4&c{HsjFs9`#sBMO+0}myE)d$&V2q7z-654iG_$`Kx~O1xeDFmG!3}MeWc+YYqKI$ zXbbd<87dFu{Oixt0ya$itq?vUHy+?eW@ooc1xcknXqOKF<2FIO~Ux6xOA7e%2%lV$SYC2SHc z0ux3TFF$ihcwf&4#19r_)fDx6EWe+Wm4R+y)tQJ+M&ibveW4|-; z&!OmWQ+kKd<`%Q_`rpOCe3a9|H5b4mkA6iS|EnOk&;QeUHXRm%Zp}jrlRj?N-tyEzJck?XYxl$P<15w zS$MT<@nTE-b<%Y3-Wf1#y_fM#SoaeA! zo+^<;I*&%dzRC%U~F z5OKrL+Sv<}PalNC_uby~gcPSIG0i3)1{Op*o8H0=z2RgTzNpn6%3fO-r(klh2vL|) zG1uEihw+iMce9%K&eh5VzTUfVsk8765fWX*b0B?ObOT|f|iAry+R;ZqU#)aEd zxOq(f!C%^JnW7LghXg?-*?~Gl!O1@!)(myrBinpCv&^K*WGm5WhzgJp1a<5#tK=dI zC$)M(Vvpxcd(_R2i~=0r7Y<_-_wjrh`7DVM_gB;mo=&k<*S&@TUZdpygv^qK$+@=q z7Ph|Q05)}3Z*Ig@Ut-OR}>*>|Vh0vU{>u;X78 zMOiAd@C+aJx1{_+0PhqLE8FXEz7Z0sNQxF6K(BG;nS6F#C>e(pLV3`Y+M@|^ggJ*f; zRDHwxX5EtX_o=so2?IfHvPk3VbnMt(OlZp^)eUY+M0eFDZsx}I1W7iXIVmBXB8aBu zM8k;7&_WN3Dhsl-$CzPOT2cF3(fHYF@ZawmxN|^xReBp9j+=>5$8ljeN8aSJvXriw zcl%H5=9eK(UpS`qA^N zY7=)o;sen#0kB>>CzGDR%zOC<@dsX<7okJOk#($XXo6$Ab)vD3+xbBnT^xC@x{DU& zD4Sc_?P5--vn!K3va90M$@N&4msb+hYfOqxr`$Pow?d#3kEh zXH@VTjUe^l=2=;hE=iliXQSDJi zgG1w>SP?-(`{L_6@IU0l{DKc0!(dwA`DGnl>i21=Ek;BQPs$%5R~8Lfe2%rxqBHqG z2{!zzeOODeRvJmZE5A$g(a|^%tL632&kR)8f%;)aJNQ6qbp~u&?bKQYNXRjbzdfqf z*T5LBzcImGSG+n0ffi(mVgw2@$vfJJHg=xT;CMKg)+@hTDaTO3|B^nQ)mYD z?KLWr)Eo__6@gM&LsI9HeVro*phvp&4u;$cgL0B*_9)i2*7_=9x!ts#g*IKR1iX**$!zy8a;kc6~nf$gq)j@w6+B4><~ zBnmSMK6GPrlmpLWqscL^vHo7h$K;%8k%>Vr=Y{}9En9!{Kin(K znx*}%aiHDB(e`>Dk9#kX;Yf>rcf?2r>2E(LTfoYCXAfINuChZt`Xq9wm7RI-na)1vB}`bNJVz9UOy@ZVS&Pqw;ES9Yx>)9iLl66G3A&9nxLGmc5f zJh`OUuEbTIj^mti#lv-1x1q9|5Yk$HVMhv+O9DqlSCe4S`xr_ofP1xEbMR;Xbb7K) zVt}LE1%Tu$(=~fz8XK*Z_uk)I4`0+#3hXRrPuD_2mhPpY4mQM6Z@t|Zz8}AVgQ`C2 z3}&{%c8Wg-G}0-C@KFFi8sh-1{bL+Pln5j3{bT}$I9uteIQ_hnB^`wv5x(g77mEC_8+Koq2X?{pI`a?%QlCB4x-DPeN9U({Hv@JJYArGdJN8 zpuLNfV5*#x$kA$!PUtF=8f(LIot(t`{R~dH$7p7pz7k=0cHecpA$*opgilm&&87qiJCxvCf<7v*&IWOA++Sg4ZF+O9(mU-U`uiiJd8RieU;Ibp(l`7WN zjrDMqm*=@S&bDd^(9%}HO}PDsRX#u#{)csc09m(?bz29Xn)D9mjBd}CHcS){;ZWz1 zpB1y%5wfFtjjsOGJb%9Q`r-DwRB=+1fU9KDxv?ckF}?x|W^9}BAMeidRH%0ahN)k` zXQ~1NGe;qs+iEbIr}A)J4jaVLwJB8q=2f+TBhHV_#D>(MQ|64xDL20*^OC=7@WPtr zBS>39tla52T$<)*KWU}XWmBCk;m_ta(34s z=Tbgtx4iVv;-}sGzCQdw;sL^Gz8tL-&gO$aGwV9p1ej2Q!Zg+ik_BhK~ICYFhUu{9t z;GTx_60XIYKZe6!fUWoS_U=XOlfL$X*%@C+FYLz&K{~7^sBAZ>|awwA&c(FZgOY& zLBcKZoO5i8KG)%5R;z~lW7t9Q=akys@h~qq(psWJS&?Q8N2GoE5WQUPtI+VxoRGql zMc;OHn~+(uFtj3Okjc_!suG|X>zNy!pziv=@m+uvfW?t_9LEs5{hplqgy0cvIWnG1 zeRa0b#ovvq-s{H@KwYq%X&Ii9I^nlRHxPrZ5;iguVy$aG%W=O$e5AfadS&A4(w7>% zwLkQ=q+mO%){UPFjz9(WbkX6^yXnL2R`?5k&Z;F#zsA8YVs4Fge{hu?wx~_+ObU(o zj4as7Yh^C0fazEa{Yx1D#RZu;p$e{ zm_XJ|(7?)qoENmj4Rdfc zi+xY@7<2ezCj!=_8*LcOMsGT+8r+jmc;uk~5ZIrSYwpG{B$54n-7n(-(>^mlz0)g zFod(XHx%)}Nr6eakGuM-A{4E=W{6AvP5L z#2H~594NHsYkp9d^y{ehIc?^DLgb1y51%g9>Ix@-fkpY1dQ+;a_hIm0EO00Cw&7{v z-;-V4Djnas@7-5RaMswKL9dmzpCBu`B86 zfB!p$G=rR0@?sVgQeS3Rx>8SEkQYjIe=Uh ztd}#kFqV3@k7Q2%zJ`GKHF`P!N~9e}V^s|j4zr(1M@dzVn}Y{0dQ)ea0*=5lbG-}? z*Pz<&a9{HFJ(pU5+y6Q}_9ZxXyh%lt$rYyd_?=3!LXH%IinNXIYb#NT_Ce11+s3=&mNwpE4P*Rj~qx5~x$ z_lLXX@Fr(9&`Yfxl@z_B`DBSi21P32FQh)TdOE)BPRFLZJrxbBxLmtt2enu7ihAeIDMBB1yO#7N58;=4|{i(GHwLm{nuk`<8^qix83 zVdUqI%8r{d>tDa~9&i>?AVM|G$TnOy@Z~6uX3jP&i`x5MlmS@6Ay^0c@225i3C=H0 zU*Y4-w_#T72eoN&xPDXo9NI2k54^=CJ4|=YAlys0D_mzFVUxC@bopi!jbWO)ZMr~o z(NZ;bYg;y~8<<>p#Eo8=7c(y2A9_`r+4v6=XOU_&MmHRO#7phI=QPwfey^Rq-t%0K zdb8hp!N2L!{yOu*Z(nsNf9~^KM*K4DI@kWv>^f`mx+DA`*}4G=U1_)8#lNDeK0dTx z3$#;b$nK-h5f5>k*-$2hZsP$N`WDZ* zqlfTK-}Bt-`s(Yg>&okk*X#W2C6WK*!s`~%YmV@JqwCH29p7tcYqF!K&w9ca5{dkGOgiRAhmOLVIQkah|yOKA3AJSDM4Etqg!bp;!Gi{YdE(IG_;WlRdm^Q0!mUMR3 z1X$i9{&DzDkZ^f;@VbELMo4%+Cx8AJ@6f&$TBHn)uN_$(s-tXR^)E5o29jV0q+*V! z-&(=UD~-%bZ%qi#Ut0g21>lPPY7^0D^AOjCTN2LZ+BUmnsF}=WUcssrHKsZS{afx= zjj#ef+r=6+5e_aOIUxrXOLDjw^3~Vr4=>CClalW5)yqHE7c&D>CZB=^JAtX2P3CU~k1vAA zQ2EA8Aueq1k@T28Z>jQSD(1Ovh6ITO&X-CDON?0a>KUIs)CX z`Gf8V#d{MEqbc^+hDa9}WDUVtgOCF}ryAsz)ySW|dvkSh*5xp(e`XjQu^w`u$kkYi)?{k&)-=RF-pu^}xTsU7AO05&mq8;P-eL+~K$V z6)5+8hVO)o%486ukyr<3OY5UnrK8U+y87ZEv!hm_g8i3DkM>3T45Pu36U@;8c;Yn1 zFF#zu*-G*O8n#^65qNtbdp>SzV%WQ+_%1xP#M%UQ;h(JEnDxHUvuU61)*z4#b)TfA z=SKHMdNn;GQ;Ni{Dk945K^4AY+dJd-{!I4U;i$U|(n8IdBuI#)k5kyxoQvLY=WYYd zD??@48B0;-=6>fop8g_^d7>+-$e!veZ@3 zoy?5sr9)DxvC}IohO1;692}Q+?Lk?yBO{rl(J&|n4LPVr=;xaLTM~=2Uk2t^$B(jb zyCDC9TKuk`;Yb`xuFOJ^iB`5~i>Q3$>xoS1tT<`FRB*Dgb~a+nHwg$9F~{-92m-1& zG)kRLRwyq~nMqNJIq~|bhHZA&VK{ewZ^9wMwXw67T&L(j@46!<#E$(zOiUE&rxZ<3 z#}EM<#*$Pj#~D`9hbMxg!vU^M0cP%<#0wURv0I9l57(^lL0}t^-%}WImqQ`K=Q~t_ zSWZ%`_^h@&-WpMHXX8G20e{%Ut+7C z6S{WIy}rIavp&hYcEmr52?H=Sj7B_^#PoI!-oMYsVc7q)1*p=uJb=l){_J|j>{?R` z8>KL4#sD!Nduos1hv$kyyRuNwo#iZ3*T^!M{U-m_vRW2uKdl{(X!opyvr9 zg>M-SW<~GA*^8bX@XeG1z-2=Q`iE=sRltOU!pegnkttlSUhvx?g90e_1=5nr=0OVH zCK661lr?xFlN2N$0CR&}OLtT6B}5SsB&J3^42%mTfD#LwvnvFyP%S*EOq~wYCSp5M z@D+HM(=2BYgL~Z?wWw7u05gD;cHqBlc)s4ox%&H3qC6W$H!`A#@$C*^xB=oa+y3tc$8rh z%}=328M0H?wn8f0r_1T>$o+bHA^3uL$jL&UqS03oY#8BAkh0KC0tKZ?6~5yuogQz0 z<@nzQFtw9syK4={gi|0~1toA?*S&8jniwbCTksOPv-6Qbye-sV&x50~g_2Ql^E?D$ z@#0As28HoRc1g0uAu$fTgI$KT$s#YiK{i?)InNn_M5Sv%YB+cE9@tZtcG_nK;H4K= z!|<_(^Nxg9pV3jF8F2JSyvs|?2sBMger47fxyE4CK*;e)&L#@f=R;zdyyV4E4yX5z z{ZSDKdqFYjB0d}phG0*^w45Sl>nA2Oju${Dmtji&&2mT_`+S0u%ZME&F>XMCmL~)G z-VsfPNZsv+5k5$S6k#f!(7(KIBAp!_#o%Ki4dH?Eb zX9k6r!<4}CF|lZ5G;A=P|G}2PR@%par9YSPx-0^Z9ry;h@lJ>4K>P6{g`B`4JwrmT zbWfDDl!jbOVo1HkAIbFT+_+H)SR`8yXfK5jEkO{7jo-x*Gd*FyKF|cNMPV9%FPL9PiRfQ7l*r=FSzhtm}ytulr zQDEjxx%aU&M-sx-&u0OZU&+uYvG~s%PyQ(!%UbfYhKw^0IM~nu>g$&_H z-qpmqw)H)e!TKc;z1$J{^J(feY2veY2oQ{C{6RhqVr9kPo%z6t1i3g>w7+=*mXot- zxqPxha4tn*1xLtKie-h!10~M+aD7btq7idlKu8jG>cM)a0>xLu9MTw-`M`8nMK(LL z?iSr5^5xiuWs`is_C3Bss?4M9l1c>8(?|@J>+=KHXZ_Pn_hNmg2S@6E{t}m1QC$ah%gf>y)a%aOxf%9TK6GHcGhxVodwqwjjpO@fzAWBce0qpMWvIFuw` zFrBS;eStw&uQypJni|>+o8o_xPM}iU7NDEZqvu}2(PyGPqWBR{Xd~B+1XEys8 zg(-Dz&jRtC$_U_j{3FQD^rL>&YCy3=tu(~8wa|SGmRrN@8jjs%7{aj+W8E&Y+&;G>@IkaQ85~q`t68K zsIf-ze{#fPg!KMOoI+Ow#lNg;iDk8rMQl}OG!B0}+6?dwDIZDOOE${QBTvnHrTBv< zh}im>oM2iJ%!Ub@%Le$q3Kr~^%7}IXs$dDJIhkie58E4;6F>7bR6WLsmJ>47leSlY zjm$`+$04jnPBRY;n*Woj0^`rNambuCWvmJn!{53N5mFe9vvd|8DH*lgpZq|pr#(e1 zYhD^dgVkm{(>Qmi>`lvv45pbpBXJ4DP1R#vpzkSUT43D3qgX*j~i*jri|JQaBF za50qc*9@w`C7WE2x5}4tTxV{fB?-Af4tEF6S+IU;o|ImD7zVrc$W_r}bu@(Nqev;pi28 z*X?O-fz%A@_4`|KUS#V@A+AiadZ&Afo#EKUN}coRe2LN1PM>%uP%1(d1>{r6*lBS#G$Lm8V2CZ7N&CW)f^Hf%sPLJ;kf8_hgoE$%x zYH#z`=bKgQO`yqvW}A~DN1F8LS!^xihEk?o-c+)8BS&ax9Fn@)N$ zwtHl7*jyrYSahcE&v~21=`-ufQTON*Q)V+Trp)?ljh2&EbA{3?z;+{^Qn7UZaKej5 zyVLF^6ur)9!t>&*ZYATtvw(-CCd-XOc@CFp?>Jnx@I)N$$D>KjN$%ZXlFMV;H&x>d zdEhhJgCDCUf0JlpWv<*-3X+ob6Xj0CUz3@QQ}jPy&%XEJiA)8;Nzb7t-)X%9Lc z&j7a?|;C*|DR&lyj(1`KRx<8}J)&I9YFTx`-oIHk-udtu|XY<$A#RKE-hx zguGCy7(uYs=4|~DSX8Oiq4rW6IFsjDtkF_SUcN;Z5k*Gd`FuM!#V5|H56-UPwE<0@ zsiUIQ>Tr@5MOO=WNJT$XYw0Z>f+Y0BDnp$!L3=w)vntjov0IrhGmPZA9lj5vPNLUl zG`WtBArW`YSLHZJ5>?dtTHHCMIYzixYqGF43f(vuMW@p+S*bm^JDSR3d1;E``!v`a zj3AiGo1s`xRMyf>R?3&y5ek5@T&}j=$rZgjocOZn_J&3!J8_Yj$%6;UXFnihSdAS= z5KqA6I^eQSt}V7JPEEi{F?7bKK!%TQS~9NA_dH8=kOBYK$HLn ztY2Z8H*0=Da&yLrHC|E3sFdk);3sY_(y}@Mg1-~cTTTT&1U_S|^FTIX9JKH#)tKYd zn*$`37Kw{BBLi0{8L^q98Q??4KgLRx=gx~D!RP-wm__gx8#f`Tia~tXRM0$~SDb&f zc+}cU5{Y=E`*VzV9Kq-ASk_3T^c@~+Z11}g_6H;OOPbDlp(cO$E=pnL_#fsAlC<21 ze*|40$;_I3I$o~7OmYL(pSu0FJnv{19UHBS65!Qg&}e>Fn4!nr5!Bc_Q?g-8+Vc(I~Pubob*y!pj>R+2n(qwoRzD9iZ^ z4AJvuK-siV45HN8V#@~+lOp;RH#5ug6}m9k`+<{lHJFrFsZwENoMsxe{Z`P^y>KP35w!-^p`xBhvII81zMOGWOBQ$f6^xr<4j=ly}#8> zS>bz_-X4h+W$_t}W2qu5u$C$hSs=}BRkayC;9#ySGpR8MUeiUqe=k;OnWd)hQYm_r zeD_hPD9})4W`tI0x@%p3xoR_(&Pu_E-_^e}n#lAxq!gz0YO;G}io0ud!salU_WHbg zeuY2thGgD6pzC-iSyD1m2s6Cz|L@9ete;qQjD zw}d$IM2V=f*NH+rVfxG&Nk_P9$pLGBP%Vk=h$?InzY$GmrqK&4n1=YO3`!0`@?-v) zCjp;?-0l{B^;7UUkFrI8Itd2whUJDE)QUBgubdY-TH6R`ZQ+E(-1vMI>>r&>W^0`L9w zrLy+N#F91!l>!5nH7IsD0&TXhbBD3hIg)s>c9+iyatQfs>eFf&y{6|&0T1pyoqZ@~ zrW@UBMN~}d<$6sR|L=xh37dqT!7D^sWJxX`56ZXuoVN!_+T9k>+81jLZO^-5&9wM# z78N^N9&tOE-&jr8U7nk^SS)^Lsb|#l9)>V$n1+?A+$3Lhe!K!3q&VHOW@-A93@vN^ z>u!(4{`#5oby2+GH1?_!<68?PVq6RY&Fx;>p~ct7w5oRCOCo@0-ztm656gnSK#veY z>;u!a#)e?Zxx2@UwHtH@L~InnfuBhl^Icm*T5QA0ZKtEuov(TlJ>OoCv7OJCN3u$^ zt2P~%H2bdH*PH0Sm=f>?Yq#ttO~nN zwc%LrRo)$&UEeO69zU;n>2cB8C>BcehX^~r23ZPb48bMyu)yp2<+_xbfA`P_!r#^k zdSVB(oX>_4x;_hv>r&z3O?Oo#!2^^#0FGI%uapkAVnWZJQ4e&HNYn{{=`-y-p!3sb2>qNb*N_gZ7o;Jx zuYr=Nl2iSzV{yR}I-rYF&`s=!5q4dS#<18_uX_!yMMJE|RFt9sro=>ccYuXRN}-jT zJU$j6G?SbFI}$9a|I23|Yy{lM48$Qk zFOs*Srs^Xg5vnMZazaXQnRu+vg5&)bgF$NIn{^^3&A-UuijVjdUZ4;dC$A8YC$W@Z zW+03x95G|8BnI;-g$r3G(&d5`|X0$16o)R zK9z|3tuF?VsMdk#En)_vBSJy~N4B0Ze3S@ZOflDiNL?nh!$)PGXd*6h3)*HDG|~u()x0&X(!?Bk7QwG2%X23HQ!1Xu@^d1c6ZMKty^*F> zHP;|zN!v^Rs?*~=gL+nVYqP`cXd;c@`I2Yu8*7_Fl~$hlQswh_sbZN`=fNVXT&bH< zm{QjLMbJ|iI`!+e@RPobUY5p7$PghjVmLPUV;|z1bjmIFSFyoJEEz6K1#VxfpdaMZ z*Lj^x+UGW+hVupMFZ^$li3DEfGw&~V+{ovik1I!MS++_OYD11)hph#LTM3<^7`(5O z+*iNpR&5l;bbTwx(fzutlzJ^ zvf3|Saj!bAd_ckYj;^%aWS&|b&vXt3tC9IfljyCNDl>k3s7pt1B+{t18b8s61g4(q zn!0)3&xk)WX1QGk92z$=%xl?SJh({bi8^nrkkn3ne(>)_zL(cc3OUpb=PHb081H}u z#`^d{q`iq_(mr0bUG~47Emm}VL~@_aTiUFi$Pl<6>ne!Oshw|ja1;9$aVBX3B=b4{ zwX^Kjn~T8JG{Tz>PBOmfLPzrp!>(T$Gws-X)wX+s3cjVZ*>?Br+AL=4?b0p!ua9r> z)@p55VD@wGZm%oG6LE#GAN#0sXf!GygjkHbZ(ls`$;gX)QC~|e@;9HlLHLhZK?LJz z^z>qIg++k13U(3^oIhJ$Uwv+u9bek)>+kD#^pzSdFk7^0^~b`98&@WCC<=LDqU1NO zsb0>TD|to{sn5OpBVD}j!+Y$T4(e4}S6F=8AJkRN?SF$JV+w%1P^VY~&hv~vG-_4r z>*}6dxqVMSl5{!?^i>*6uPA0F;xESgDAZ!TC(0(!v%vNA%WNT2olFRj`r@Q9m{vP^ zrYm}IfJ}&eI}37-#{J+2uMSNvScjm_516M{y3#@BK|Q8v##emN(T*HOvO81zk zQaR45C=3{ZjgVkA@&Ff>LPLm1W5}evmN}8xd7Ae{x>%xBZ#|}oT&zjREh?WmjIcrP z8j;a~KPK8JAL>YUp!CEs@{}g8pA_LEL>N!o#o&ZIUD?5G*iTR=42n{Tz}o2@jbO4F zraud9(u|+54+1ifr0Ql}=oD%&Q(brmQW=+c_s;S^$M(BpA3)16kk- z@$JSe{#%t^@p4VayZCJ2Gi3o7C<4Wxl{Qy;oK_G-0URDh2-~(Tw^6K0Ty6b|v$t_R zpJhymK(>;EyNg#+s_(XvdfHbK;DdV%aS{CcRqxm^vBvuC@{f+4>xF z$nNU{?&R4beB5}FHsHn=c5(=E(#)2OL%sE?(@n+~ZDXs;>&q}nn^U*fe|Qkk`LNjf zaJJ>X*Kj-stojReN9yX07CY!9oW8dbKl*eVRBQtra}~=H$CBu;rc1(h;3~H|H(K8R zojP^aTb_rVBXHIo4~;6G>0tW@07c6G6JWubi^d0wh`A@=u za2Y(Vx5x4%dHhr+ul`0EHT+-cprTqH1O{hJRM9Fg2Qi2l4TS^Ona| zlI`9sZ_2#kEL_}EBVU`Pzq7kxoM=3iqS<`0Jf^05%E%?toc`cs26v{@DdqF)9ew-r zu2f)Fh|hm#0rcVu@c3L7v7Co;{9;2a9=*VshUIv2&E4S?u(ne=p3X;VnXVt-x4+z` zI3Vne=~Kn?@A&W8PVA6~S*)}Vtam+Kw!yj1$mE48YdKCl-=GrzlJ=~%+Gt(UcKi8y zUrJqdyim48=w%_#D8g3JaxkXsaI$JSvpX={;hE(LIKr1lCVkM%1I|2FmuvKI!V9F5 zxNby72fLnc%RI;e4jBrYqk_DNNn#6auRq@|_dbA+J$8LOa?7V$x4$fo(erMV{2e-; z1I|6(-zvGK2XS|{y|P^5*}L7=ZIfK_C0)M!Hb3}IBDOT9TNTIHEY&<-7TBqz6~jHX zGFUsd)EekoufMskD`?TR+=oV_{o!Ziu$k_T;yW?NNq!Dur0?AC_3?vDF;r{hiL*)#j^Ha*ZnDaX;*&pK`Z=`zh4!6q+kq_DS1e(n7QsYB z1jf*BGRCM|AT+aKt7Ln@(6xH`t|dJ(Q@1BcT>V=WlP%UsxH$q!CaU7679%%I)%IK> z4JCo=-|{$I#hQ-a?Y2Mlr}Pzm8l+ZZy3{jbA7uAov-b4;1ds`y#XiQ?yBd&X(UB0k z*GqS2N!vk7k;8#f)jb$irzRIiBetk%ha+OQ2aX29d3{^Crfp#wd7Yqz{yzh24@r;|pnp8E-$zeD#r`=5+dC%KUoAH^OK zJTYBBWp3Av{*+d))p#~jHG#dNFqMjy@?A{P{6xU-{RZCOL_??1%#qY|IGI6$Ve66h z3`BUjyTrYXr}eS3kDpTUHnAXcQvGja=qx{6NOOYUz#!ndY3e0nh=KFAiaRcWXIPHG z<7@@(GKPZ6cA~vrl1PqTmh4&AT02K@-rb-pnNGk8y0V!5*AxUu-?Zsm(cT%EJ@@%_ z-`wx>t2W@xAF#RTUZ8#Cf7xdy5WMR65~RYFF3Xq14MZgD;ipxu)KFkxEFsxsNm)qa z-I|pxm@rW@_Jf$&3T=OAOsn8sE=|s8n|VPQ!PK&CIj@y^DSG0+-Y$$tvrr_P=-uq# zPvs4o&jZe@ON9DQkIDF4 zYJV9l)?=~2YN(YFn<+JfRX_MOH`iiME<0=d0VzUkvFABErGZK&i^K1!mx`jE^#Ad6 zRzY!a!L}ZBaCdhP9-KjfLvV-SPH+kCGPt|D2G`*35IneBAh_$D|J-}-3olen4ON@n z-D`cTH|MuO&l?q7WJBM(#q;A`&PhLVjm;wJ6_KHWa=Mf5Vt8+m22nJ-ztjJd z1%8k1JJ_AKaPZ8We9R}f7g$vm&?YS#pa7EC7PYOc^0`}>?H0AW`Y6jMqb)nea}cT= zaO;C`ddbW3dQBzb@sK!95K`fa2ZUMTw3lh`<+x|74neNN{+?D<QiX!D~8rQRCArI zp-|A|WyeK1qW5@AC{~f3zLZDo2Na{qN^SJ&BTMketcF@C`53W?eiJ2a(Hu@xPHfIc zg$1S`{+E_0_yESB{0K-Qnl%M7y@GuP_md^>Moqgz zQff=4jJx*$TopJP{^22)>FW^ z`s|jd*5BjX&5p65A~3a5dvyI!9$a{u40KQh@{olC~G?YEa(w@|mfKdVgBXT)?~K)T2zC3X;( z!-v4T*1Pps;t+MBr}t7*vHSU-zN?{0{|{d#(C;=C>iPTG<0K4x(QWE28O+1dVbhgb zBM#;`-f#Loo1U2kU&5|&D>Z5r?PI^uC2Tf$av{hgM<p0Bni)p2&Q)e3Uk zEHJofRm_0M2YZwjUH{Fw>Q;-~OmbZoHQO%x@a}pyn#y5D;q<-8r?Rwwh%uaQQRcJU zv~DcE28QdMXR;gbJ|bcZ59)W=LrSxXiNAXmkI;+oY4zdb?Ab~qz0%V+uFfb8C{w1P z^K??%JXb9D#mUcBzl}_NBmb^5=GN*r|4lRW>$8N|5(aHD#x5VlNrSDZ4^QQ_E-PpU zxF}>o+&_EoE0qiMX;4T@k=_FDJbN#HN!{5zRx>82uWw3To-{JFU6txhq|~an_>?%~ zs(z$?GAvc=dH-8h+x>o@<}Hl;V3DVN5I3jNR}ABr(BOuKDG0SPL{ zvWVmINANayOivv2jxVV69{~{4OSRMYgz&;d`?cD&B@k^FGfX+tS_1^r@RI_;V=VNa z!X*?+B^2;RNUoRattKT!M05Wn)CLTh5qaEc-L#F!Ce-01AO*nf9N%%!&65~wD@e_z zM@pK?WW^4dVU)#CSqy3q1q(zDCnFo5h0AhefQRFGh#vrQz-)amdZY;_t*JTN;cz+% z1JjUIG+#cHla!RHE{C9GW!oWFM^X7g+%N+jHCjM9z4N4cB2O}!W47^FPTkiz$01St zpK5Tm`zFbOju09nkUC#hpNK{g>Yij*v zIPji-`vvaq0n!;yT|We+GOE#~wLZq3i!!Af;QLG#J9fDLySA^<=}7h0q40+qINOWE zhblkeN%xQUfjAbhZ+Qc7_Tl~Z8|Gckug;_LWcam)!!cieI%`7BcG`FS`my0@Ib<5k znEeZh_AQ98B>V693zy}zsCuN;T9b8>4*jz$WQ?Ty=OmE1>bvm^_4SVWW)7UPh?cj86X1uN9uAASrUJhULu#uJ)+;}b(hXzn#p5FwBfMSl)?FQ z`Tl01cO#m!+I*EF791{>O!8yh$)|!fM=;Bu@SaJ%Ej|io=*?-*FU4=xB!e-b$<8)w zr$jDA2;#|&ym2a)c-=h_|H>N*Jsxq*nF%B4M}oznZ~fpET&mCu_iu2#!jjYwjoPs2 zwuq@4*|Q-Pv^dj_Lv%TucG>9?$G0v$U9KG=SFToVN5beIu5dQU(~|whtzRVN=Ciav z+2MZP|465b0XpJN-|0u57U@~@9E!qcR&R6cgNCDUB`rjQik@%-5U2B&c;kHIAOZ*z z^CL&2RF^@KbwC7W0d$fM_QN_-`fZoXLl2I)5_XfK{b@>KNsI1g0WJ8|)7&O^R()IRd$C7)e1dOSf=M4Pl!q@08)(G4E0HL_#Cku?E5CJ-kr zmg>K`Q_?ij-N=39v(Ob9Mwd1XKs=U=+9q)Q5 z^%SMV;`hiSXDezd^5its`y7Q?*PO!+F*jUQ<$$d z$<6xHP!?Youwh6~JLV zNkHRoZlI7poTvLCBbcWHmTxs%zdE|kL2Px}{PxE&Mrrp}+&5fi&C!FOHX1|ZMTU(Q zZkP40a(jkrt#)_k1gu&W`iN-z5PvCt_mN-#;h>8Ght9(ZwTwAu4ctaEclh5>TE!Ly z(M)s>##0oe83$^KY1fk5WRRG&D#8A%tRq!s z5m*5n*a&Mdg(GVa_U6rtY6QF=dk(*2bzs9rS)JZ}apV5ffJ>mH4eQF$#9OOcaUDq3 z=n=7tsS&)wCd&EbURjV!?XsP`njl*qVHC*y{ErFFS^sOcML0kI8mD5h`FIBu3{}rB$<3jRbZ{3b-1n zRR8(io?I`=uP187_i9(Nu*F6gquTU>2uh<`dv*@52nnMoXRCkQsoq2~3ZE@-#(upn zL4);Q?Dq0cwFw+%gYTo;GkJc0d)!W!7!A99RC&N@RhCRIF04J6MO=Ms5K^9g#2ImoUC#3;#W68%g9+bq7vEw`FGT8+L2|CPmxiE&&vLia= z@pt^_@;>p85zq;S@zL3LfuaO`;W98OO=2TsGODU;fyjB@A2MsSIr+jy2-7+Od$lmE zTcQ(G_X&48UFLY>&?}U@#seHb?5sMSP zcHHdrEPDB=*ZlncLO!`)%{5dzoWXC~@+I}nC#Q+7X1Z=RcVR{}D0V~X4c9GVZ)w@) zP3L3gyz?gZxE{|k=dKq9oe=)BwW~n4pv`UiLR>FDdS&_q?l_K?>!B4|it6tZ7biA? zm~av*JQb!p(xKdZjY!yoSyT90v_xIGaE--h`lIiGL;eL0d_TICfg{QJXexj2mJMnq ze&usnPU$z8W%Lzxd8>-xt`vNEF&T=icM+SU=U7dofZ6r!zhp2?D^`&;B)d6vo($1u zIWO?Ry29vW_zFe-jb#)Dl?>@AfzZ>I@PTH?tk)Vwgl;~T>A<>36pL7o`4XBrIk^bc z)TEl>g(RN9mY+g_7AiDoR$C@k(Ll~obRvCHmL4N1Fna9qO7v z0BDBr?+I?`k~|Lllzxo`-NNn+*nQ(59EF&67;3bvq!=88VzK$-)@=(e!%zurDp)O!)+aw+w3zt)7{UR31Qf2ivCeAwQTg>CybI6~Mf}s-#L+)R33fLQ7a0;Utszs)0bpAd??{j&bX;Y0mq91S zxo8{ya@Hw$B1&zD5WWEqyGly-FNR)rN+5=)vy;vY)B=$3A&@RrH@fl;2@y@ z>KB^iSgbirpG(n+AuLt6jYO-T)w~QPduAamY#b@1i9YgT@DpWmVGzL?!Zv;W|a!A2oIWxZ zqS=J5Y%fja=>o6WcM*yhG?>Zm(M`FZ{fRKxZ$HXCK%@-gu6N^bFAzvl!e6qbd}L(N zZSpvp&iSLGTW{i-)wmWE1)pA}I*C>7M}Mt+KJ>Pd&A*$5h|6Zc(%^GCL<*EkW11q* z5_EtPV2*HysIjLbelvh0@U@D{Sd_DwK?ZxJ@yn+fk=P_i$YuFbmI;8ywvo za1}Tk=A+CC3X@ocF5k_|xk(xt1R&m;tspO>j17G$s4EHaWHlk!S*94nM_8o+HptMI zy2+|PWhxo4$qopUOcx^V{wBji{D~|}ANvHRFg}Tg9azHeXGopkC7UD! zBfX$)&9x7f#2LMg3B|=ED#D| zXfZ)XCE*@455}}p{-w_N;ft>{crsfZhM6zlq41exs8oBfhpkn+9t(hz^V`anCxQC6 zD4D!;p6(fiS7#1nYivr7Lb>)8mUdi3EYX2SiQGg8JC;3#;T)K+y3)*6L_05R!SL)) zPxjldqTQh=TY7UzoYm^+#}d0ChP!3B4UG!bp>bShh++Q*ni2NCzx~l;xDb7Fh^WD$ z(jXxbJjKu(Q*Bb_@$Kv7SM|j2c8IsnZl^tO?tGx7U*;2kA@hsll;^xIN&2(*Ez+s# zHg`5o;7r#iQimg%EGCu0wn8R~B?|Q`EZRrY9;K%ZpH9a(;;wy=!6mt>h zPNSXv<9M;JrQ=pUtS-wz6&LWWct=#h3o|^eHY*KZt5Q$uH6W;8tcZcn7l>(KD6QX>ByYbof30qd{?~Il1-3Gb2w6cv5;+j;^ zQDG4Ov;i<8G}^{!5EzLAFj5ire?pb?&d?UR$Y!N#SAu!YdMOS8Rn>y4#RsYjtkzNY zzk5xjDg1Yypof8lF^~DP>AtJ!ZX?QY)Zgt>B6Y}F|4!GW7$I7bf25QskAlbWwQs4d zuec(G1F4&s1=#wxfn(dKupH05Mdwjj1K-A}=g&b{Lophh4`-uTH$B=tz*EMPdfL zL|+^xAiC72F|H%f%4QTMIZHI0ItWbU^N5v}3zuD~51*Iv0gS{D*rlT5H6wJiMgLjJ zDKqjY7aPadQ0AyM9WR-+Jn4}Zx2j1q^i1(zDOV}_cz?KpSUIrd_Vd4RKW?rLpO5$A zca01=mQC$2Oh{r1;<7L0hp z{FtO_@`m@Z0-A><0Y%vmHM*yxp70j?oK*-r?s}=n=ZyFAFDL%lpj+qUg~=T@TDR%? z=veIM;!kbG5lC%0MjRn8s|X*sgyw6|gME6=a`?IfW9Ts3Fhpp{J;Ema&dqce6-%Pd zVpy+QX_BM<7yJvjiNI9uAcH8!?`sf#@|@J|dsRPAoHKp$^ta04{@>O`r7Ow9XJoxU zfh%rwhh82!>e7bRz;Fu@rGWP{lpP_1OVAqR87wDfTK z2>(c;O@e~aIs(R{fo}Y2q{CpvX0NusWjoc`4W7ANh|JT45y6j|m_6-Cz>@?xd(S4D zq7CJ5JJU4i$3RK4(A}6|=6gUhyfjFCarzVhd2)Qj3=93|`sWJ}RMaH5ld6P8tU$DQ z@rO9bv9pAMB!C#nlmrZhEgsqBAS^;H;)q|Or)m^c7{(yr18RRqY7mcW3;6{^u@qe( z5tEUI6CjU5h_5dQz1-Q3@NEZJcKu8l@g=%0C-H_D9?ZKI6eki5 zbas}T3RH3B{mM9lR%$}iD%H=!$7U12&s$WD@LNcn(<$k;I)PNX>5;$n&A#3{y?_sV z&dx}}SKz@n)o4TZdO9#sHrdg9{MX3qli#j>F)b6SB3Zsn#yKJ-IEK-bQ?fW4q3qqE zO`wh-Nad9LE<+uCCSb(`CbNSq3h{3GRzTL7+b~g|GF*=V(Dgni?2Li}C9Y?jt3{9( zR(+-M2%!cad##g@4!-AIi48sGzw;pOKxCfor+YoWqrX)(L>#XjjcZNA`Qs9PrOc0K zjWa%%eJ_tkdz5bv^kkm<9FHzqgI9fL7o!=USlb^>$oMsW>YATt>)aep=RUtUn2tnu z?}VM2cMcr_`W6OIb!nA%hT9JGh+~hR_Tr{q16SQ_1i8bDAjI!US?;ypn`~cyXm!uk z1GNJzqA$Pt*kf7wd$9J)g#O3#vKt9h93R!pk1fA0qxW^|dA}FWvtuno&siz?e0~s9 zn8f%9Xd5>z4)jbQ0wIX04gF4r7T9i#+8mO@e>h0RlJJk1(IHV_EZbl>;cApV5sW2& zWQe(Z^WSI|S{C3`%5~^|ckUA^B1`fk5r?_s=fbH6nFXfQa6C^ zeY^9qt)wOXcSha^r{HK??8{Saq;Re6(fz+w%Hpiq+byqkKW0!JL^D7K9e)yCG=;U| zhuK;mfs#A~p-@pRSjwmut1ixe6mk)Z0#{$zJCn zYz^~Hl1URS25Fp1q7+$%u_oOOcBFOFS)@_&UQF-Nd+3^47$PgVdn62sC&M(gt)%Ce zGQ-Y-2Nh-p)aB@8S&TmSj1u1=xJ?=|ndqAxE}%(BK=hKOZ`W)XkX;Btwq5QW2O7iF(7|Ibr-D=pcTts@bn-(8APLv#FtOyP1j75o36E&g^d#oP$UU2wF z5W1?At_jWvd4pI0AQ6LCS&G6YZT*sOB0v(_#Ozh&on-f>mtomVkdugj0b@$tAIh6` za?-Pj0wX;qt3JgT-0|;soOv(G z1(?Et-p(>CbuxPBG6f5iTz*TS&-4x4?VipLPY zWmX9ZB{-}$jWsX3#x=|b!6Dm2rt9tAzCMBpSzREn`yJrJ8N`OeCeYDub4;@0!Qb(f zI4LLdUgq8Bsiyt#Z<3;bHKYp*MZy*MTV7NMgoI4~&6xVDFm#`MM)b7UQJg zLZn_zEURMloMD;gUjP9r9$`FCi54!FANJU~U!70p4j>>xrCzI|_rttb{PUM&TZirg z_l7kB;BW2$_4=!#IFa^Vl177#;|;|ef%8A@jXy3x7F%jcg6+0xtR9n)$z0_6*mOEg zyW`F0cm4!x2aQ*&XJhG~)zvzuBFar<)m)F=o=>{jI)fTtAe31G^wW*6pwD2-PrVo> z_nzm<dKxK4u*!gA7-*q;Ousfu;7S%TM;otH_&24Qe$BmkjFs+yQbas0Z-AC9; zzPtA1p(YKcL9ITcU%~~wf_8|B%Oo&)azAZcDI&%o)aWGIsCZKBPP*M8k~ zlH)+H)y56W79Eo4c*HmKqAj$03v>JEe#u4O^yhsZtvgLgGb~p-Dqd+kN?*T7H)S&L z8VhZ!Za45RI-2Qug(g6UBM5}bpLfp=Cgb!thCu1dZ3%Wi^B}ZIp8wL!sQKOB=4RWx zKkaJLrpj%9a_1mw>7+!tDP7f#3#FUK-*6BPE!?ngW zpR0rA7WfN8<5M-B!kEtP34u0j`jkE^K!c6*97FNP;elX_sbAp+X^2xB^066+ zpcoD9KS%TYt}p*Dcxu1iR&QFy`u~E_XTIvg657|&{e=^^@Q?$IchMTX?@9|BO&8|{ z;Ty^QUuTb({sZ*3gV7;dO^Wnd?LX@rLxM=5mJKdh-=)$Cwj}=>^AzPyLQj-Z#fXr7IVX^~)f4ag`bUoqHq7)mOp_;Nh&->Hbo|YjZyB|RL;9VDgdRm)5ts9#~N*CymTZq8d zMp0MmeXQe{Q)lE;>%DfD#b@S=(VMkw9y^EG^XWXO8@p|H|Ko!27yoD3B~#GNnTw(C z8>*($9QQ!&p5M1RN8%KL-N6zryKlrFWWeTjY8iZV$g2; zkzwauY8I6{A)WsGn+?J@L%+K_NNRk~3fbB@nRTr=Lqk?)<2FH>?88UYUkxU5Q%1FN z28iZ!(S?p8|LpsGa**~d(jTtgYz&L7>rn{Q0)cChSv$=pkI7M2W^*7Nh5c~&`;E-# zaNiTXlak;Zl>TRq%z4tIrFp|ko8(K9>DqkqT~FwL(~#t-Vu8OkmAG|UjC3VbF$ z1&i#Ba+CtTHV*;;K7U2QG=M5>u?EbAW=-w2qVn^AX;5_PIRvdRo%Wh#loYg@;3_3+ zFN4O;Vxof)<5wJ*%E~P#gCw*Lh~u%Zh`b7H2j#!UNXn(=iH~nrqrln?>cUMc>YV*u z#2i(_AcIz86n%AlmDmcWHwMXn*maw8|4L;u^t11{f7o~sJv3}^sPVixe0vz9heZB3 zPuZxauEZyOPye+-(r#GqYBrnZiNp3shI}T24?+UQj||l+`o-B8NC<&1$-Ta}NTnVP z{#%d^@!}0H`&bvkYNu#@6TU7>fPwcR``~W*3Lo{^0zsk*fZ|E7nEmJ16hAhd zLP&@$L~VRncj=LJZn{Z0&Z3nQuhTjy2$Llv;)M+NjWZcx3XXzFxDWpxtc!-Hqur|XTh7B8Q@>ouB}ebDA}XTyjhdk#G#NAYdin* zC;Vya<(-ELwWKA(7N4iZjoMScDYusg+at4qN~B&B%zoRLqOtF5*}l`q9Hxo)tBsa% zC#acN1Mf{GY`RUwBRM}ZOAB)g4| zmPXq6qsqR81m4M@cL1PW&Vi;!6jbbM#R$X*Wcr;|Z9P9YI6{e8DV#xTvAfaNEQGsU z=P#so$1h@17Fc>d6DgV}b}&?QFq&D*uTBy5a#87%*J3J=Jy5W{mHe4QQU6`>qmxbm z_W8XlDvCcCcB@g71jsAM6jX^u3ho~AZk88;8+W`b`-G3v)wmY!OI?X!Yd zjHJNqPf4PQSr+9$6Qur4DTqu+9vJhQ8CA`~Ik+E+%P`H1S)kK7ryCI&rxh-~PR-(W zjhp=-1X2aB8{ycKmwwHZ=sDgRT!API+P@$~Yr9K03L%d*WW$?0TWrgg&&$S4Pjts!s$S)Cxk0!7 z3KA|;I{eV{-Raj?VaMOw=%7c(AIEGMk$fos<7v&RhgLWiMQ*e*?D;TFW^mQ2?3F^& zeUN6^G9y=^yVEm`<=ZtR`}g2gwmgiQH3@&w{qP<^pZojgnrHms=43`=>DN3wy|f|O zzHXAgS+Qzx`fpQw-uGuMM@=2~ndf)J3$@A+4e9zoLCp%H*xD{tznzq+ICNeeuHK!V z$!B`XNx*Vw)!!5WoA)+_z4Ls}L!t7}zi<3ntZ%j<^?!AO$UwTCW@Be7E*3MHpM3@& zAQA2JPdk#%fJr@5eYn7c0+`7huA^+1HN&+o@2+RJH`7?6x5F&quc@>Y2nb@Jx2BJ; zyzf!ibyl;xc_Xp7nDoRw#8+=^Tr)+J8U5gt*vJ5eIfaEtw}VG%-cl+b4_>Ah~3vL9-f@n;~!## zjL~FVcqDY0VMj5%ThC$_psz%{pz}6UTS(-iQe9SUHl5#gDVmV0=lS{cU_z5`9aiGr z3S>UMz+;)`J-uRgx>V(R2KmF6%KaX9q$eT1;$bvlk5UfL_djWCKfX(se5Nq0OO|vh+nSU)mHnQ#s<9_T#S%T;dq=Vs5H9H)ds!vDh=g?u{EUF2bP}t2;Mc6}^1ELRp!cV-$pr;-IS_H52ztl5c zLxrsHpP02u9C9?{f8mPwQhcL$Q^7^YZCDkSB^l8qbe{Pa!X~!hN`i7WKnQ-3Z{hQ1hJHMZ2`v# z&h9WzXa`oNU1B|Gp`7B$76L2~NM3s2nIqF5aGaPB2& zp38R0IaVcz3ZPaEBF#IhbBKmV4Iz?(EnP9#K2QxB|7$VUG{0cp28Y!tZC@jOts~f{VPn43=D?X z=IoyPdzFrT`(4X>o^Ni9Vtqz(+YPqD8cZg9P}rcSJrNp;G4{Uwo>0yN3FmxLusN-j z2bIVH)b4G--P?9NQs)3^o$mqrs`l?`HT=96ZK5Rw1erYc!PcdK!f(^ufpk z#ROH4a{O;5X!+Zs zY?9`9gfM^_qJlD!heD(e%oGK^kl^h~3MPD_L`C<@{Rt9y6N43Vef!*)Rwp@VmmxLY z7pSROy*1$#Awfe<6@oIc-$iKRC7k41v@+{WTO-l$6|UAVTFhxII;(BgH^Lncf67e! zo{i8aPt?y}WOr|@JQ&cq8dAPQH5$c&^^9AD|s~p%MhW_b*Pu=O1#Kh{oU|$`kyh(Pu zt0a$Vq(JXzD)Giq|DF2dM!@DUI=s+d)Fig+nBRNxjz&&;&tPrPnaa=x8HFttA3&%o z#0)8zB;EqgP&oe|Avx0Qa9oLy?Gy#6(i$P=Cbs=_ldu>CA-I&jQ-P&4nsN%g>IJIT z`1}R{_9i#;`s|AryF5IoxrD ze8jO;*Y9LY;agr-78DWzCT8u1$*qO*^vqx#6dVX+FtU;^aj7~Q?-%U;Sw+o2onjO8 zUh*&Z-{vrII^KQzX>ISTbX$>1O`%~A!~bg)p4t#ZCMlo)%HQS{eRvxmI&w(S6ApVO zG%Ulfg_h1(8qEOpGl7Y>?1B)(rB#qzY_-f?FOW2afE1PLdom>e8+Ym_Wao|Zu(z~y zMi5oTD&L>ii(K#b7)UZ6k|?@A-{>HvpPl!1DIMSK<^I9h%>73&B%n3?P}3Z^C5jVfWZ92pUQ5lf!4(72S*+TZh~y zz^Jk$HT!Rq$S*DjR5F=@l~nveRB{YUvkc|{5Of}pEigk=dytze?9)k?kS$zXiaym9 zS7aokOtg8_a{$2r)hsqV#Z%(Jvx&R=NcE(1z^mjsKE`HH?;L>z7SHf3d`jko`e^J~ zf^FMTkLYtK`(|SMZ)Aw_F+d){v%11LJKTo?sH&piUPp|))w~wl{X{k^3-oz_C%OyJ zY3(KC1P%SeVSl#dY#?H#hTR8TkO5?(-TaU$RLNWr{Yj=hY65hsiek(pcnG#(Ztw%T zacNTmsxSr=VkOD0jUM2!sHiZmcW2=!1MU|%jt$k(TrQ8=KzY>f0LMH8Ro`P4b+>+9 zbmss?DW>xsAs`QA$qu>McZ#lS6PO8((loS;jIc)>o5R_V5>8f14Xdw;-f))jOr)jC zjst(o_QS5*#0)eX2h#F!5g`s^{vQK^FF~>LXUHI!KgKip;WIUNI_4I^}`k6qYPiH?OL#ff=WEz#h$|7ers@cEqW_fB=OM$UV!WD zJuQLUOMl^P@9yOTN)4Bv71xj9iv#;~ntq9rB3>s3fad#tceyra;Iuqzx8uuwP70GI z-`*tG=>iCG>+`?9dE(EI#W){%S~)NRKJXFYt-;jFNwewhjT6XlKWkG zgQMpv_qaV(1awnxIGIr$q&6|lN>SP~+zq_v)FvHGR?oNkHq0+lR6#zM)fD$KMlhqj zP*19pZ5pX?iBItXJNeGdLA`dh{#g+(o5l@SeT`9P>Y)#Fedp%dYmMb`<1M)5B;4%y zAP>%(uV7Pp^)9M4+2z$<#yq7UJ~48xRD`rp_$PK2YA&IEA=)txk* zNp*@w*w0i66`6~qXz|2}Ifz|-3hSeF*na*N@Q3F6HrlF$I868(GVo__@HAl_Ch=-zQDO^ZlE%u`e*4r#`!N0JKXHHeZ` z0bP4+VOyhJWn>_RO=~jEF2JLdC$%$b%ib~$C&AKPd8t^5-wD}OdR5ohv6a2XVCtmWjs>9GZU_++nl!MNhltbX;7cuTyTU#fF#RS z;8!wdtehJ}^j*|4ID=H>T-3~Aqd38jF1EWYfqEpkYEH|vR5yE7+ftm!2yqS-ZJjm# z(arp2J@E3iSP}QPBPbf->4whsd7IzjlsFhP-omhezoc`dF)@j$uWSCjFEZjG`w2yG7S8ruw`Amm;wx z0H^Ha(IQ_|*A(Zb-|3w=)`DIffbHBU$3IWdbohl+l(>bod%;-yYL?MwY1JB!paxJ$ zNEkACUuQt<9XusCFGL=Mmk^w$bVD10$XO?4C?7gksh&GO@ zcx%ifZ#pRR`w>r~F?YU(E)xHN`sX6n@_DFjgaBqrxqv}`@B{oe^?~BxE`4u2W0YQ2 zJY-BSH8>=@cx#;ecP?y`akN6E<2ITi1cZBd(sw=gV**|w2bssD1!7o=Xvq+1R498< zB3Qd@bfI5gzL_t@B#!^4FRn~YN-mC(jzdd;C0CZB8kamg5p{^afofyZ4BVu6Ku++1 zoX__-*7K54C5A{WZQ4m+X{dZkX_qoGV5@`G`ERifQk+Jrpz~g81Wpv##AAg&yeBb} zG)HovOI?*ZUXM%P{y~^_^!;^D*F@)A$uLks5&MhKTSI?3C%dH;@hnEJrJxB`PB74S zGhfX2{T#9;^1SDx03GzI3=r|>EgxCZ5_nZt0;?(8L_ah@=P`$;ijKB?Tqu4*3 zza!kje<8@K!|2M_PL9uKCTsfw|2O@k!PU!I_Vf#UG-_c|Ptxvxs|}o1v09wN)eBg& zm2jsw^u|y$*Hxk2iG))w8`!xzwA$#0+cUv=O;(WZbVRk?t2d-e=nPd}2q>?xnab%B z3Fb78EJnz?uKtWz$t8rnsUz<-pi~?)_VmWUHJmBGJG7+qC-p~r*R*zkS&YwUr^B-j zX^kkYYPtvHo%a14cQ@F731s@};K<&AGCE>Aq(n5j;|2A-0Fl0Mm}6Au2w>{q#R4ri z0zxWTAk6NU_=*3n>d6vE^>N~-A2Hs(;+D7#iLl#DZvLd-pGGHquR-8B z%dV7`LCm|6BbXZS6_2{0Fz&N6VJwLBzF@KGe{&z3 zqxHTo_OqL;`PPxj`t5$>2eodq^^4fWW~Qg>CVliBN}$$-=u*Mc`fUDV`H;T1OQl{T zQ%=^+U(!)f0WI*T8}Mh;D;VQ1c96&q!JuPp0ZdPqvc=xY7f}t>e^Xp!0aB7K7HZ!Y zxPRQKgnbilASz#@s7Iz?{{dn>6~(zqujLqV?WPl6@=7Gv~s z#JB1g&7>i56eo3x72ze_NizN9aA(>QDOh+&ol(qE^t#08qU+N6X-5Q*C$|uaU|BIk z6>e}nFfmn_qpDs!DKa@1`Zga*~m&r9`z~dT$el9Pn=}~ z(+^KHf}T)Ou<^4^2_&Qa;k^dei_!9C`@!K%kagTy zxMumEid=(yMm+#B4J*HsG9D=;v%YsxZcNxoZ#lb2!ZYe3xKK{6o#m(6IcQcO-YYh$ z%Y_HAy&tGL>x)@l{9uC1%AV7apUE_0MW8kRQ!Tk`SIWtT%Xd2F=$_=-VP=Di3e(Qk7Y~OSaugoobwp(gR29~yOqg?Ny zK)q7AXz&)j>-B*PNeZ1@vIw*$?Fr>nPOu0Naw~6SpKt4PgzheyI23H>^}kwx zYQeu*!cRG3uV!;QkJw|QPQm=Jn+{NC?e)}&S4Ff^FLM4nZR3vV+<7k4`A@QK`;>8` z6=1Gx3wunm+7P$8cHw|y^2nDEra-qp1~Oj;Ww&v>ZuN4clc{<3x^WAX-W+a!!o*e+WmOnZZyTnQu)1m*}jhJK`w*~%^Ba1)b zTBXpXbzH^4M$zHj3*+jODN-K6VFzi`KoJ%tx^;w(ma-_p26-k1tFj#c-&F`{zHT8G6{otU6DNhAj2_l8k+Li6DCak0@j z=(q5EBxvVLb9!zMGa#GzR{E0AJIRWE+ecL)D1Tr3h{yB0^(v7oIQ#syBOH>wI9 z{~J8%iyF$;d?twkXBEbL;M{pf!5p!(Gy1E^YGLwrZr7?2pI;s4sLRVB3$PZ{r z(&X?PjxB7osm39_Lkx0wpQjXhWDeh?Q2q&C^zEziW2{`MYrkvWjyWN1UR%yes)ux- z#84inn|Z-_U46$+rA-RR^_ozAb_Ch9Mv`AAFonEcRZgS#VU~Q~_%(tk+j+~iH{d*I zvkqzI0BSi}nkF@YckoZyO!FpaSD9T7dG^NiPSwZK)J1_>J))_z+2uG@GLp%n)#p*s zX(Mn;=w=$KOT@4}N~5Qt0-9;KHvdS|4~uaCh+Sg*55JJqhv>bk&G{LD0_gAACX9%L66^$%Q-<%*x(|o|>wVEDd$2#D`>VFjgM$SPT!u4~M4t=4o8#LP+@X<8o z%dqj>bo%aWvGTe$7AShkTh*fJru4ad#Ado*Eg^Tv+*rkR-GUr@fTRR5*dCGHmUdM% zT9lc>jwDHp0ChwIoF#{l&;;JdpaOb#qPX@hJDv{3^H~0t+x_GyVU9$Et9>~@Cf!&}?=}5;IFRVa@3;so{OEsb$Qix#y=Y&`S&tfC@7n`mF6G55+kehcM zA0mS7kDnZ0u7NFvn^a_>LY|ADK@qlyCQn%VTRc!F@Ya{g^;*DT7C0W`qlG$&2asWo zSHxGov~OJ0kirvzy?Kl^HGmsGMQz~A$l%mdpR>rIvBq8pWbSbaRU0#eOJN(ki3Wl5 z*5$qM>@@V}=x9qYI36)gg;lT7?kVw#1pI6moyLGh-U%~_J{V2b(d(Diy8|Mm{U0Zi zcsp91*=Y6rvS8V}ne!~ZLX9tHEj|)r0WBoQlXWj2u|8n8NbVVu_k|P~4ZKiOaRn@Iwl#musY3WYsPLXcu?k?%>?nb0RQo2DJ>F)0C?&i$@;@o-XW<7haIlp+v zK<<5rFS*8W*pfu(=T-apNov=AS$Z?9WCD`*+Yc-8L|506Wt60;QMgNc*q6`7J zsdH*F`$jQfvf{rD<|aN`_8eGBeYaaq@KZ(W#`9+MqBkeS`4i4B%UND%gMj1;6+G;D z{`?J`p*BxrWRkR$iP&P3$BU?mHHY3I6d}Uv*u*NgB$`J3ct>ZgCY8<9%BpBJ zi>qW1t)OzjrLcpDa)8^yv44k9CA(#&h)!)FlW2xEQ@2k&@~uVBTGP#;0TPw7$fRo@ zG6zm9IMY7Uo0qkzSi1Y$D&=%^5s39R$y&)OyYYvwW|t{kHHwgbeM6Srlzea~G?ZtX z7;&1(EPbfh-M`^n+LtbE~KpTMbzKZ1$uNH+P&DCfI0`k*)Bx{H$9 z@&1^#@_W~IsG~$OF%4#NYEs;W2Suel>IzCUTcEd7LD3-y{tjzDKh^er3r~ z*X|Vs-YrC)w)EJ1`LOKm8B$#knbi(|(SozP7P{%RkdekKG9fU)W z1%BaMVK%={&+|??fB9hv+8@KL5??399hQId{HpYOqDEz1MM6o$vl5fiGjKA8V?VF! z3vqN-WKs0D?sV*Wuw?{m9|3WoE50uID)1^mO(Sw(@m`;n1U_9jQ9UZWUwcPx z$Gt#pU|RdhMT#^tpxaZ=OI3Xboqysc35Q25Pt5e42Y3A6JRCMM*)JAYSEFVzKRgbR zivw4QI2Tz+$8-mT8FyLJJlv(V?@EFdg*7u`{PuZ9<@y_*_G(p z`d{NWYOyENeKha`59{(L2rm(qMpMc)`Z?%#LBMxQ?UShB4kp?Al8k=~POPXH#Q?MPDnS=Vm4Ert7&;Haxn;Wvtw(Q$L-L*$l^)$ zPT^cLhsyH@y&pp($JkY~Vi=)%H|iIQDS`YR?{j<|AAJ$@AsQ_FB^gGij<|`C^Q$;| zP9z^5+dn^{dp}-=UA#E0Yr+eEF?WB#4XG)izkbpGc=^im+ezmJ=Zj#rm`}TXI$Bmj9mj7( z{l7yydS+Mts->}{r638@AygFkg8%m)Q!$s0Xl{Nv62e^aoiVqlz9VFfh)-AZ@Yztg zeM)Yu7SrdFSfSxZ=H*u_3WpeKc{#C^?lLu;5I z#dj+4B>#*IH7>=C4K%>)%C^aNkA=r_%RJS+;PUe;-~MM24%XxE+uP&tafWOPPdw`|uW!QgzV9NTLSo;Wrr$Zn8%*jipMTVbRHWJ&$Igp6 zuU(PQ(xZ;e0Z`AUYe< zlW`98?-R(Q+p|8cgy<1!ZUa_vZb&Con9(RiYu$%dK`5Xvp~XLW;=%mMgu|tutI|Al z8dV5?vsT(6-z6bBJf)6HMy@xla4hx{i-L3=|Jx)}Ez-1T7W*hpHU9#x@JM&Ae;;zU zXZ*XuD9g{kvfs<#$+7-Qz0KPdp?D7UM@>JtkDbFc`YZWq&(oH)S|K$3xtj1d(*ZO8 z^Az!Omd^VdY{p!e-oE|GHmkSpwpZ*$;te~c>1CCH7Mtgj01|=P$d;3tfH%Txga_=` zjw6C+3|?^Sq()OMg67)vi-arZ=K3R#;;2wY9MJ7m)!~PCDQ7oCFnly@w8l>9ZqGZM z)fA60_$ud6;k=%jWVcoM=^~_>NbNl3K7eFMhK$ONq3L5s%6KCynr*NY{_;em!kcB(F>H0nhVFa=}WR{Nq-4!&zA(u4)_YdvKXydi1m!YrEauh_e3t?|3h;Q|Sq3c|8pQcrf}SmnPe zqkKpW`y}FF7Q`p$rl#)k2@M>?*M)KKg>D;fBjGg0C4H=9>k{*mZWi=K4P$=XGlfSzRTviO+XJQg+o-vU`uivd9T7x` zy#xGY_I@|_$k4CUn#-!5C?860pk`Wnw60RgOOUF=Q;+o2+)H7FvN`(qd=tAxbuK<# zFwI!mqBu_*7(Mu=2h@5l;5wE%Dywn8aTHSh>!DJTFmNayypq3$hIYv2`9Uj|cDWZ# zV4VU(#G4Sx_}vd(k%B!2I|jo&x{-G?Y^-%v∋+!_KlbY@U-Abvjid2PmP6J{JZ! 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๗!EE33~U$JBN&=#d81!cU-*>&Rsm5 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

qJ0-tsE3|sBe!|YaokpUK#PMjwv?a;9z)a4$ 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 Date: Mon, 24 Aug 2026 02:09:02 +0530 Subject: [PATCH 25/75] test(ui): cover empty review titles --- src/ui/patch-display.test.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ui/patch-display.test.ts b/src/ui/patch-display.test.ts index 9b49f53e9..7b5822566 100644 --- a/src/ui/patch-display.test.ts +++ b/src/ui/patch-display.test.ts @@ -7,6 +7,10 @@ import { } from "./patch-display.js"; test("review titles describe a uniform or mixed file set", () => { + assert.equal(getPatchDisplayParts( + { files: [] }, + { emptyTitle: "Changes ready" }, + ).title, "Changes ready"); assert.equal(getPatchDisplayParts({ files: [{ path: "a.ts", type: "new" }], }).title, "Added 1 file"); From 11fdcf966f1b7ac3e6d63a7a906853019be2e9f4 Mon Sep 17 00:00:00 2001 From: Waishnav <86405648+Waishnav@users.noreply.github.com> Date: Mon, 24 Aug 2026 01:28:14 +0530 Subject: [PATCH 26/75] feat(config): define versioned JSONC schema --- package-lock.json | 11 +- package.json | 5 +- schema/v1/devspace.schema.json | 301 ++++++++++++++++++++++++++++++ scripts/generate-config-schema.ts | 10 + src/config-schema.test.ts | 27 +++ src/config-schema.ts | 97 ++++++++++ 6 files changed, 448 insertions(+), 3 deletions(-) create mode 100644 schema/v1/devspace.schema.json create mode 100644 scripts/generate-config-schema.ts create mode 100644 src/config-schema.test.ts create mode 100644 src/config-schema.ts diff --git a/package-lock.json b/package-lock.json index 5b5c247a4..c0563885c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,6 +25,7 @@ "diff": "^8.0.3", "drizzle-orm": "^0.45.2", "express": "^5.2.1", + "jsonc-parser": "^3.3.1", "lucide": "^1.24.0", "react": "^19.2.6", "react-dom": "^19.2.6", @@ -782,7 +783,7 @@ "typebox": "1.1.38" }, "bin": { - "pi-ai": "dist/cli.js" + "pi-ai": "./dist/cli.js" }, "engines": { "node": ">=22.19.0" @@ -1087,7 +1088,7 @@ } }, "node_modules/@earendil-works/pi-coding-agent/node_modules/@protobufjs/float": { - "version": "1.0.3", + "version": "1.0.2", "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", "license": "BSD-3-Clause" @@ -4450,6 +4451,12 @@ "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==", "license": "BSD-2-Clause" }, + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "license": "MIT" + }, "node_modules/lightningcss": { "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", diff --git a/package.json b/package.json index 301edf4c4..dfa5cf557 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "dist", "docs", "examples", + "schema", "scripts", "skills", "README.md" @@ -28,9 +29,10 @@ "build:app": "vite build", "dev": "node scripts/dev-server.mjs", "postinstall": "node scripts/fix-node-pty-permissions.mjs", + "schema:config": "tsx scripts/generate-config-schema.ts", "start": "node dist/cli.js serve", "test": "tsx src/user-config.test.ts && tsx src/config.test.ts && tsx src/onboarding.test.ts && tsx src/cli-workspace.test.ts && tsx src/request-meta.test.ts && tsx src/incoming-artifacts.test.ts && tsx src/artifact-download.test.ts && tsx src/ui/card-types.test.ts && tsx src/ui/patch-display.test.ts && tsx src/apply-patch.test.ts && tsx src/process-platform.test.ts && tsx src/process-sessions.test.ts && tsx src/mcp-sessions.test.ts && tsx src/server-shutdown.test.ts && tsx src/local-agent-config.test.ts && tsx src/local-agent-catalog.test.ts && tsx src/local-agent-presentation.test.ts && tsx src/local-agent-runtime.test.ts && tsx src/local-agent-daemon-lifecycle.test.ts && tsx src/local-agent-daemon-protocol.test.ts && tsx src/local-agent-daemon.test.ts && tsx src/local-agent-codex.test.ts && tsx src/local-agent-opencode.test.ts && tsx src/local-agent-acp.test.ts && tsx src/local-agent-grok.test.ts && tsx src/local-agent-pi-sandbox.test.ts && tsx src/local-agent-pi.test.ts && tsx src/local-agent-claude.test.ts && tsx src/local-agent-adapters.test.ts && tsx src/local-agent-availability.test.ts && tsx src/local-agent-profiles.test.ts && tsx src/local-agent-targets.test.ts && tsx src/local-agent-store.test.ts && tsx src/local-agent-manager.test.ts && tsx src/roots.test.ts && tsx src/skills.test.ts && tsx src/workspaces.test.ts && tsx src/workspace-conversation.test.ts && tsx src/review-checkpoints.test.ts && tsx src/server.test.ts && tsx src/oauth-store.test.ts && tsx src/cli.test.ts", - "typecheck": "tsc -p tsconfig.json --noEmit" + "typecheck": "tsx src/config-schema.test.ts && tsc -p tsconfig.json --noEmit" }, "keywords": [], "author": "", @@ -51,6 +53,7 @@ "diff": "^8.0.3", "drizzle-orm": "^0.45.2", "express": "^5.2.1", + "jsonc-parser": "^3.3.1", "lucide": "^1.24.0", "react": "^19.2.6", "react-dom": "^19.2.6", diff --git a/schema/v1/devspace.schema.json b/schema/v1/devspace.schema.json new file mode 100644 index 000000000..e7c18466e --- /dev/null +++ b/schema/v1/devspace.schema.json @@ -0,0 +1,301 @@ +{ + "$id": "https://raw.githubusercontent.com/Waishnav/devspace/main/schema/v1/devspace.schema.json", + "title": "DevSpace configuration", + "description": "Versioned configuration for a local DevSpace MCP server.", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "$schema": { + "default": "https://raw.githubusercontent.com/Waishnav/devspace/main/schema/v1/devspace.schema.json", + "type": "string", + "format": "uri" + }, + "configVersion": { + "type": "number", + "const": 1 + }, + "server": { + "default": {}, + "type": "object", + "properties": { + "host": { + "default": "127.0.0.1", + "type": "string", + "minLength": 1 + }, + "port": { + "default": 7676, + "type": "integer", + "minimum": 1, + "maximum": 65535 + }, + "publicBaseUrl": { + "default": null, + "anyOf": [ + { + "type": "string", + "format": "uri" + }, + { + "type": "null" + } + ] + }, + "allowedHosts": { + "default": [], + "type": "array", + "items": { + "type": "string", + "minLength": 1 + } + }, + "trustProxy": { + "default": false, + "type": "boolean" + } + }, + "additionalProperties": false + }, + "workspaces": { + "default": {}, + "type": "object", + "properties": { + "allowedRoots": { + "default": [], + "type": "array", + "items": { + "type": "string", + "minLength": 1 + } + }, + "worktreeRoot": { + "default": "~/.devspace/worktrees", + "type": "string", + "minLength": 1 + } + }, + "additionalProperties": false + }, + "storage": { + "default": {}, + "type": "object", + "properties": { + "stateDir": { + "default": "~/.local/share/devspace", + "type": "string", + "minLength": 1 + } + }, + "additionalProperties": false + }, + "tools": { + "default": {}, + "type": "object", + "properties": { + "mode": { + "default": "codex", + "type": "string", + "enum": [ + "claude", + "codex" + ] + } + }, + "additionalProperties": false + }, + "ui": { + "default": {}, + "type": "object", + "properties": { + "enabled": { + "default": true, + "type": "boolean" + } + }, + "additionalProperties": false + }, + "artifacts": { + "default": {}, + "type": "object", + "properties": { + "enabled": { + "default": false, + "type": "boolean" + }, + "maxFileBytes": { + "default": 104857600, + "type": "integer", + "exclusiveMinimum": 0, + "maximum": 9007199254740991 + } + }, + "additionalProperties": false + }, + "skills": { + "default": {}, + "type": "object", + "properties": { + "enabled": { + "default": true, + "type": "boolean" + }, + "paths": { + "default": [], + "type": "array", + "items": { + "type": "string", + "minLength": 1 + } + }, + "agentDir": { + "default": "~/.codex", + "type": "string", + "minLength": 1 + } + }, + "additionalProperties": false + }, + "subagents": { + "default": { + "enabled": false, + "providers": [] + }, + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "providers": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "enum": [ + "codex", + "claude", + "opencode", + "pi", + "cursor", + "copilot", + "grok" + ] + }, + "enabled": { + "type": "boolean" + }, + "model": { + "type": "string", + "minLength": 1 + }, + "effort": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "id", + "enabled" + ], + "additionalProperties": false + } + } + }, + "required": [ + "enabled", + "providers" + ], + "additionalProperties": false + }, + "logging": { + "default": {}, + "type": "object", + "properties": { + "level": { + "default": "info", + "type": "string", + "enum": [ + "silent", + "error", + "warn", + "info", + "debug" + ] + }, + "format": { + "default": "json", + "type": "string", + "enum": [ + "json", + "pretty" + ] + }, + "requests": { + "default": true, + "type": "boolean" + }, + "assets": { + "default": false, + "type": "boolean" + }, + "toolCalls": { + "default": true, + "type": "boolean" + }, + "shellCommands": { + "default": false, + "type": "boolean" + } + }, + "additionalProperties": false + }, + "oauth": { + "default": {}, + "type": "object", + "properties": { + "accessTokenTtlSeconds": { + "default": 3600, + "type": "integer", + "exclusiveMinimum": 0, + "maximum": 9007199254740991 + }, + "refreshTokenTtlSeconds": { + "default": 2592000, + "type": "integer", + "exclusiveMinimum": 0, + "maximum": 9007199254740991 + }, + "scopes": { + "default": [ + "devspace" + ], + "minItems": 1, + "type": "array", + "items": { + "type": "string", + "minLength": 1 + } + }, + "allowedRedirectHosts": { + "default": [ + "chatgpt.com", + "localhost", + "127.0.0.1" + ], + "minItems": 1, + "type": "array", + "items": { + "type": "string", + "minLength": 1 + } + } + }, + "additionalProperties": false + } + }, + "required": [ + "configVersion" + ], + "additionalProperties": false +} diff --git a/scripts/generate-config-schema.ts b/scripts/generate-config-schema.ts new file mode 100644 index 000000000..2e56f4a8e --- /dev/null +++ b/scripts/generate-config-schema.ts @@ -0,0 +1,10 @@ +import { mkdirSync, writeFileSync } from "node:fs"; +import { dirname, resolve } from "node:path"; +import { + devspaceConfigJsonSchema, +} from "../src/config-schema.js"; + +const outputPath = resolve("schema/v1/devspace.schema.json"); + +mkdirSync(dirname(outputPath), { recursive: true }); +writeFileSync(outputPath, `${JSON.stringify(devspaceConfigJsonSchema(), null, 2)}\n`); diff --git a/src/config-schema.test.ts b/src/config-schema.test.ts new file mode 100644 index 000000000..f76c9ac60 --- /dev/null +++ b/src/config-schema.test.ts @@ -0,0 +1,27 @@ +import assert from "node:assert/strict"; +import { readFileSync } from "node:fs"; +import { resolve } from "node:path"; +import { + defaultDevspaceConfig, + devspaceConfigJsonSchema, + devspaceConfigSchema, +} from "./config-schema.js"; + +const defaults = defaultDevspaceConfig(); +assert.equal(defaults.configVersion, 1); +assert.equal(defaults.tools.mode, "codex"); +assert.equal(defaults.ui.enabled, true); + +assert.throws( + () => devspaceConfigSchema.parse({ configVersion: 1, typo: true }), + /Unrecognized key/, +); + +const generatedSchema = `${JSON.stringify(devspaceConfigJsonSchema(), null, 2)}\n`; +const committedSchema = readFileSync( + resolve("schema/v1/devspace.schema.json"), + "utf8", +); +assert.equal(committedSchema, generatedSchema, "run `npm run schema:config` after changing config-schema.ts"); + +console.log("config schema tests passed"); diff --git a/src/config-schema.ts b/src/config-schema.ts new file mode 100644 index 000000000..c30bb3612 --- /dev/null +++ b/src/config-schema.ts @@ -0,0 +1,97 @@ +import * as z from "zod/v4"; +import { subagentsConfigSchema } from "./local-agent-config.js"; + +export const DEVSPACE_CONFIG_VERSION = 1 as const; +export const DEVSPACE_CONFIG_SCHEMA_URL = + "https://raw.githubusercontent.com/Waishnav/devspace/main/schema/v1/devspace.schema.json"; + +const serverConfigSchema = z.object({ + host: z.string().trim().min(1).default("127.0.0.1"), + port: z.number().int().min(1).max(65_535).default(7676), + publicBaseUrl: z.string().url().nullable().default(null), + allowedHosts: z.array(z.string().trim().min(1)).default([]), + trustProxy: z.boolean().default(false), +}).strict().prefault({}); + +const workspacesConfigSchema = z.object({ + allowedRoots: z.array(z.string().trim().min(1)).default([]), + worktreeRoot: z.string().trim().min(1).default("~/.devspace/worktrees"), +}).strict().prefault({}); + +const storageConfigSchema = z.object({ + stateDir: z.string().trim().min(1).default("~/.local/share/devspace"), +}).strict().prefault({}); + +const toolsConfigSchema = z.object({ + mode: z.enum(["claude", "codex"]).default("codex"), +}).strict().prefault({}); + +const uiConfigSchema = z.object({ + enabled: z.boolean().default(true), +}).strict().prefault({}); + +const artifactsConfigSchema = z.object({ + enabled: z.boolean().default(false), + maxFileBytes: z.number().int().positive().default(100 * 1024 * 1024), +}).strict().prefault({}); + +const skillsConfigSchema = z.object({ + enabled: z.boolean().default(true), + paths: z.array(z.string().trim().min(1)).default([]), + agentDir: z.string().trim().min(1).default("~/.codex"), +}).strict().prefault({}); + +const loggingConfigSchema = z.object({ + level: z.enum(["silent", "error", "warn", "info", "debug"]).default("info"), + format: z.enum(["json", "pretty"]).default("json"), + requests: z.boolean().default(true), + assets: z.boolean().default(false), + toolCalls: z.boolean().default(true), + shellCommands: z.boolean().default(false), +}).strict().prefault({}); + +const oauthConfigSchema = z.object({ + accessTokenTtlSeconds: z.number().int().positive().default(60 * 60), + refreshTokenTtlSeconds: z.number().int().positive().default(30 * 24 * 60 * 60), + scopes: z.array(z.string().trim().min(1)).min(1).default(["devspace"]), + allowedRedirectHosts: z.array(z.string().trim().min(1)).min(1).default([ + "chatgpt.com", + "localhost", + "127.0.0.1", + ]), +}).strict().prefault({}); + +export const devspaceConfigSchema = z.object({ + $schema: z.string().url().default(DEVSPACE_CONFIG_SCHEMA_URL), + configVersion: z.literal(DEVSPACE_CONFIG_VERSION), + server: serverConfigSchema, + workspaces: workspacesConfigSchema, + storage: storageConfigSchema, + tools: toolsConfigSchema, + ui: uiConfigSchema, + artifacts: artifactsConfigSchema, + skills: skillsConfigSchema, + subagents: subagentsConfigSchema.default({ enabled: false, providers: [] }), + logging: loggingConfigSchema, + oauth: oauthConfigSchema, +}).strict(); + +export type DevspaceConfig = z.output; +export type DevspaceConfigInput = z.input; +export type ToolMode = DevspaceConfig["tools"]["mode"]; + +export function defaultDevspaceConfig(): DevspaceConfig { + return devspaceConfigSchema.parse({ configVersion: DEVSPACE_CONFIG_VERSION }); +} + +export function devspaceConfigJsonSchema(): object { + return { + $id: DEVSPACE_CONFIG_SCHEMA_URL, + title: "DevSpace configuration", + description: "Versioned configuration for a local DevSpace MCP server.", + ...z.toJSONSchema(devspaceConfigSchema, { + target: "draft-2020-12", + io: "input", + }), + }; +} From 7743f320e6e2cc4a1ae0ace3cb6dc82ff0ff2d77 Mon Sep 17 00:00:00 2001 From: Waishnav <86405648+Waishnav@users.noreply.github.com> Date: Mon, 24 Aug 2026 01:34:01 +0530 Subject: [PATCH 27/75] feat(config): migrate legacy JSON once --- src/cli.ts | 47 +++--- src/config-migration.ts | 96 ++++++++++++ src/config.test.ts | 269 ++++++++++++++------------------- src/config.ts | 248 +++++++----------------------- src/local-agent-client.ts | 6 +- src/local-agent-config.test.ts | 15 +- src/local-agent-config.ts | 26 +--- src/user-config.test.ts | 110 +++++++++++--- src/user-config.ts | 178 +++++++++++++++++----- 9 files changed, 534 insertions(+), 461 deletions(-) create mode 100644 src/config-migration.ts diff --git a/src/cli.ts b/src/cli.ts index 7cf723f8c..c745e791e 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -8,7 +8,6 @@ import { getShellConfig } from "@earendil-works/pi-coding-agent"; import { satisfies } from "semver"; import { loadConfig } from "./config.js"; import { resolveCliWorkspaceContext } from "./cli-workspace.js"; -import { resolveSubagentsConfig } from "./local-agent-config.js"; import { getLocalAgentProviderAvailabilitySnapshot, } from "./local-agent-availability.js"; @@ -46,6 +45,7 @@ import { import { generateOwnerToken, loadDevspaceFiles, + setDevspaceConfigValue, writeDevspaceAuth, writeDevspaceConfig, type DevspaceUserConfig, @@ -143,7 +143,7 @@ async function runInit({ force }: { force: boolean }): Promise { hint: "Use DevSpace from Codex, Claude Code, OpenCode, Pi, and similar tools.", }, ], - initialValues: files.config.publicBaseUrl ? ["chatgpt"] : ["coding-agents"], + initialValues: files.config.server.publicBaseUrl ? ["chatgpt"] : ["coding-agents"], required: true, }); if (prompts.isCancel(destinationAnswer)) throw new SetupCancelledError(); @@ -153,7 +153,7 @@ async function runInit({ force }: { force: boolean }): Promise { let allowedRoots: string[] | undefined; if (useChatGpt) { - const defaultRoots = files.config.allowedRoots?.join(", ") || process.cwd(); + const defaultRoots = files.config.workspaces.allowedRoots.join(", ") || process.cwd(); const rootsAnswer = await textPrompt({ message: `Which project folders can DevSpace access? Press Enter to use ${defaultRoots}`, placeholder: defaultRoots, @@ -166,7 +166,7 @@ async function runInit({ force }: { force: boolean }): Promise { .filter(Boolean); } - const port = isValidPort(files.config.port) ? files.config.port : 7676; + const port = files.config.server.port; let publicBaseUrl: string | null = null; if (useChatGpt) { @@ -180,16 +180,16 @@ async function runInit({ force }: { force: boolean }): Promise { "Connect ChatGPT", ); publicBaseUrl = normalizePublicBaseUrl(await textPrompt({ - message: files.config.publicBaseUrl - ? `What public URL will ChatGPT connect to? Press Enter to keep ${files.config.publicBaseUrl}` + message: files.config.server.publicBaseUrl + ? `What public URL will ChatGPT connect to? Press Enter to keep ${files.config.server.publicBaseUrl}` : "What public URL will ChatGPT connect to?", - placeholder: files.config.publicBaseUrl ?? "https://your-tunnel-host.example.com", - defaultValue: files.config.publicBaseUrl ?? "", + placeholder: files.config.server.publicBaseUrl ?? "https://your-tunnel-host.example.com", + defaultValue: files.config.server.publicBaseUrl ?? "", validate: validateRequiredPublicBaseUrl, })); } - const currentSubagents = resolveSubagentsConfig(files.config.subagents, {}); + const currentSubagents = files.config.subagents; const availability = getLocalAgentProviderAvailabilitySnapshot(); const configuredProviders = currentSubagents.providers .filter((provider) => provider.enabled) @@ -220,10 +220,16 @@ async function runInit({ force }: { force: boolean }): Promise { const config: DevspaceUserConfig = { ...files.config, - host: files.config.host ?? "127.0.0.1", - port, - ...(allowedRoots ? { allowedRoots } : {}), - publicBaseUrl, + server: { + ...files.config.server, + host: files.config.server.host, + port, + publicBaseUrl, + }, + workspaces: { + ...files.config.workspaces, + ...(allowedRoots ? { allowedRoots } : {}), + }, subagents, }; const auth = { @@ -295,7 +301,7 @@ async function serve(): Promise { console.log(`allowed roots: ${config.allowedRoots.join(", ")}`); console.log(`allowed hosts: ${config.allowedHosts.join(", ")}`); if (config.allowedHosts.includes("*")) { - console.warn("warning: Host header allowlist is disabled because DEVSPACE_ALLOWED_HOSTS=*"); + console.warn("warning: Host header allowlist is disabled because server.allowedHosts contains '*'"); } console.log("auth: Owner password approval required"); console.log(`logging: ${config.logging.level} ${config.logging.format}`); @@ -369,10 +375,10 @@ function runConfigCommand(args: string[]): void { throw new Error("Missing publicBaseUrl value."); } - writeDevspaceConfig({ - ...files.config, - publicBaseUrl: normalizeOptionalPublicBaseUrl(value), - }); + setDevspaceConfigValue( + ["server", "publicBaseUrl"], + normalizeOptionalPublicBaseUrl(value), + ); console.log(`Updated ${files.configPath}`); } @@ -384,7 +390,7 @@ function printHelp(): void { "Usage:", " devspace Run first-time setup if needed, then start the server", " devspace serve Start the server", - " devspace init Create or update ~/.devspace/config.json and auth.json", + " devspace init Create or update ~/.devspace/config.jsonc and auth.json", " devspace doctor Show config, runtime, and native dependency status", " devspace config get Print persisted config", " devspace config set publicBaseUrl ", @@ -396,7 +402,8 @@ function printHelp(): void { " devspace -v, --version Print the installed version", "", "For temporary tunnels:", - " DEVSPACE_PUBLIC_BASE_URL=https://example.trycloudflare.com devspace serve", + " devspace config set publicBaseUrl https://example.trycloudflare.com", + " devspace serve", ].join("\n"), ); } diff --git a/src/config-migration.ts b/src/config-migration.ts new file mode 100644 index 000000000..f24851adb --- /dev/null +++ b/src/config-migration.ts @@ -0,0 +1,96 @@ +import * as z from "zod/v4"; +import { + DEVSPACE_CONFIG_VERSION, + devspaceConfigSchema, + type DevspaceConfig, +} from "./config-schema.js"; +import { storedSubagentsConfigSchema } from "./local-agent-config.js"; +import { LOCAL_AGENT_PROVIDERS } from "./local-agent-profiles.js"; + +const legacyConfigSchema = z.object({ + host: z.string().optional(), + port: z.number().optional(), + allowedRoots: z.array(z.string()).optional(), + publicBaseUrl: z.string().nullable().optional(), + allowedHosts: z.array(z.string()).optional(), + stateDir: z.string().optional(), + worktreeRoot: z.string().optional(), + artifactsEnabled: z.boolean().optional(), + artifactMaxFileBytes: z.number().optional(), + agentDir: z.string().optional(), + subagents: storedSubagentsConfigSchema.optional(), + tools: z.object({ + mode: z.enum(["claude", "codex"]).optional(), + }).strict().optional(), + ui: z.object({ + enabled: z.boolean().optional(), + }).strict().optional(), +}).passthrough(); + +const LEGACY_CONFIG_KEYS = new Set([ + "host", + "port", + "allowedRoots", + "publicBaseUrl", + "allowedHosts", + "stateDir", + "worktreeRoot", + "artifactsEnabled", + "artifactMaxFileBytes", + "agentDir", + "subagents", + "tools", + "ui", +]); + +export function migrateLegacyConfig(value: unknown): DevspaceConfig { + const legacy = legacyConfigSchema.parse(value); + const unsupportedKeys = Object.keys(legacy).filter((key) => !LEGACY_CONFIG_KEYS.has(key)); + if (unsupportedKeys.length > 0) { + throw new Error( + `Unsupported legacy configuration keys: ${unsupportedKeys.sort().join(", ")}`, + ); + } + + return devspaceConfigSchema.parse({ + configVersion: DEVSPACE_CONFIG_VERSION, + server: definedEntries({ + host: legacy.host, + port: legacy.port, + publicBaseUrl: legacy.publicBaseUrl, + allowedHosts: legacy.allowedHosts, + }), + workspaces: definedEntries({ + allowedRoots: legacy.allowedRoots, + worktreeRoot: legacy.worktreeRoot, + }), + storage: definedEntries({ stateDir: legacy.stateDir }), + tools: definedEntries({ mode: legacy.tools?.mode }), + ui: definedEntries({ enabled: legacy.ui?.enabled }), + artifacts: definedEntries({ + enabled: legacy.artifactsEnabled, + maxFileBytes: legacy.artifactMaxFileBytes, + }), + skills: definedEntries({ agentDir: legacy.agentDir }), + subagents: migrateLegacySubagents(legacy.subagents), + }); +} + +function definedEntries>(value: T): Partial { + return Object.fromEntries( + Object.entries(value).filter((entry) => entry[1] !== undefined), + ) as Partial; +} + +function migrateLegacySubagents( + value: z.infer | undefined, +): unknown { + if (value === undefined) return undefined; + if (typeof value !== "boolean") return value; + return { + enabled: value, + providers: value + ? LOCAL_AGENT_PROVIDERS.map((id) => ({ id, enabled: true })) + : [], + }; +} diff --git a/src/config.test.ts b/src/config.test.ts index bb464d844..c266b1e8d 100644 --- a/src/config.test.ts +++ b/src/config.test.ts @@ -1,167 +1,126 @@ import assert from "node:assert/strict"; -import { mkdtempSync, writeFileSync } from "node:fs"; +import { mkdtempSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; -import { join } from "node:path"; +import { join, resolve } from "node:path"; import { loadConfig } from "./config.js"; +import { writeDevspaceAuth, writeDevspaceConfig } from "./user-config.js"; -const emptyConfigDir = mkdtempSync(join(tmpdir(), "devspace-empty-config-test-")); -const baseEnv = { - DEVSPACE_CONFIG_DIR: emptyConfigDir, - DEVSPACE_ALLOWED_ROOTS: process.cwd(), +const configDir = mkdtempSync(join(tmpdir(), "devspace-config-test-")); +const env = { + DEVSPACE_CONFIG_DIR: configDir, DEVSPACE_OAUTH_OWNER_TOKEN: "test-owner-token-that-is-long-enough", }; -assert.equal(loadConfig(baseEnv).uiEnabled, true); -assert.equal(loadConfig(baseEnv).toolMode, "codex"); -assert.equal(loadConfig(baseEnv).skillsEnabled, true); -assert.equal(loadConfig(baseEnv).devspaceSkillsDir, join(emptyConfigDir, "skills")); -assert.equal(loadConfig(baseEnv).devspaceAgentsDir, join(emptyConfigDir, "agents")); -assert.deepEqual(loadConfig(baseEnv).subagents, { enabled: false, providers: [] }); -assert.equal(loadConfig(baseEnv).artifactsEnabled, false); -assert.equal(loadConfig(baseEnv).artifactMaxFileBytes, 100 * 1024 * 1024); -assert.equal(loadConfig({ ...baseEnv, DEVSPACE_ARTIFACTS: "1" }).artifactsEnabled, true); -assert.equal( - loadConfig({ ...baseEnv, DEVSPACE_ARTIFACT_MAX_FILE_BYTES: "123" }).artifactMaxFileBytes, - 123, -); -assert.equal(loadConfig({ ...baseEnv, DEVSPACE_SKILLS: "0" }).skillsEnabled, false); -assert.equal(loadConfig({ ...baseEnv, DEVSPACE_SKILLS: "1" }).skillsEnabled, true); -assert.deepEqual(loadConfig({ ...baseEnv, DEVSPACE_SUBAGENTS: "1" }).subagents, { - enabled: true, - providers: [], -}); -assert.deepEqual(loadConfig(baseEnv).logging, { - level: "info", - format: "json", - requests: true, - assets: false, - toolCalls: true, - shellCommands: false, - trustProxy: false, -}); - -assert.equal(loadConfig({ ...baseEnv, DEVSPACE_LOG_LEVEL: "silent" }).logging.level, "silent"); -assert.equal(loadConfig({ ...baseEnv, DEVSPACE_LOG_LEVEL: "error" }).logging.level, "error"); -assert.equal(loadConfig({ ...baseEnv, DEVSPACE_LOG_LEVEL: "warn" }).logging.level, "warn"); -assert.equal(loadConfig({ ...baseEnv, DEVSPACE_LOG_LEVEL: "info" }).logging.level, "info"); -assert.equal(loadConfig({ ...baseEnv, DEVSPACE_LOG_LEVEL: "debug" }).logging.level, "debug"); - -assert.equal(loadConfig({ ...baseEnv, DEVSPACE_LOG_FORMAT: "json" }).logging.format, "json"); -assert.equal(loadConfig({ ...baseEnv, DEVSPACE_LOG_FORMAT: "pretty" }).logging.format, "pretty"); - -assert.equal(loadConfig({ ...baseEnv, DEVSPACE_LOG_REQUESTS: "0" }).logging.requests, false); -assert.equal(loadConfig({ ...baseEnv, DEVSPACE_LOG_ASSETS: "1" }).logging.assets, true); -assert.equal(loadConfig({ ...baseEnv, DEVSPACE_LOG_TOOL_CALLS: "0" }).logging.toolCalls, false); -assert.equal(loadConfig({ ...baseEnv, DEVSPACE_LOG_SHELL_COMMANDS: "1" }).logging.shellCommands, true); -assert.equal(loadConfig({ ...baseEnv, DEVSPACE_TRUST_PROXY: "1" }).logging.trustProxy, true); - -assert.throws( - () => loadConfig({ ...baseEnv, DEVSPACE_LOG_LEVEL: "trace" }), - /Invalid DEVSPACE_LOG_LEVEL: trace/, -); - -assert.throws( - () => loadConfig({ ...baseEnv, DEVSPACE_LOG_FORMAT: "color" }), - /Invalid DEVSPACE_LOG_FORMAT: color/, -); - -assert.equal(loadConfig(baseEnv).oauth.ownerToken, "test-owner-token-that-is-long-enough"); -assert.deepEqual(loadConfig(baseEnv).oauth.scopes, ["devspace"]); -assert.deepEqual(loadConfig(baseEnv).oauth.allowedRedirectHosts, [ - "chatgpt.com", - "localhost", - "127.0.0.1", -]); -assert.equal(loadConfig(baseEnv).oauth.accessTokenTtlSeconds, 3600); -assert.equal(loadConfig(baseEnv).oauth.refreshTokenTtlSeconds, 2592000); +try { + const defaults = loadConfig(env); + assert.equal(defaults.host, "127.0.0.1"); + assert.equal(defaults.port, 7676); + assert.equal(defaults.publicBaseUrl, "http://127.0.0.1:7676"); + assert.deepEqual(defaults.allowedRoots, [process.cwd()]); + assert.deepEqual(defaults.allowedHosts, ["localhost", "127.0.0.1", "::1"]); + assert.equal(defaults.toolMode, "codex"); + assert.equal(defaults.uiEnabled, true); + assert.equal(defaults.skillsEnabled, true); + assert.equal(defaults.artifactsEnabled, false); + assert.deepEqual(defaults.subagents, { enabled: false, providers: [] }); + assert.deepEqual(defaults.logging, { + level: "info", + format: "json", + requests: true, + assets: false, + toolCalls: true, + shellCommands: false, + trustProxy: false, + }); -assert.deepEqual( - loadConfig({ ...baseEnv, DEVSPACE_OAUTH_SCOPES: "devspace,admin" }).oauth.scopes, - ["devspace", "admin"], -); -assert.deepEqual( - loadConfig({ ...baseEnv, DEVSPACE_OAUTH_ALLOWED_REDIRECT_HOSTS: "chatgpt.com,example.com" }).oauth - .allowedRedirectHosts, - ["chatgpt.com", "example.com"], -); -assert.equal( - loadConfig({ ...baseEnv, DEVSPACE_OAUTH_ACCESS_TOKEN_TTL_SECONDS: "120" }).oauth - .accessTokenTtlSeconds, - 120, -); -assert.equal( - loadConfig({ ...baseEnv, DEVSPACE_OAUTH_REFRESH_TOKEN_TTL_SECONDS: "240" }).oauth - .refreshTokenTtlSeconds, - 240, -); - -assert.throws( - () => loadConfig({ DEVSPACE_CONFIG_DIR: emptyConfigDir, DEVSPACE_ALLOWED_ROOTS: process.cwd() }), - /DEVSPACE_OAUTH_OWNER_TOKEN is required/, -); -assert.throws( - () => loadConfig({ ...baseEnv, DEVSPACE_OAUTH_OWNER_TOKEN: "too-short" }), - /DEVSPACE_OAUTH_OWNER_TOKEN must be at least 16 characters long/, -); -assert.throws( - () => loadConfig({ ...baseEnv, DEVSPACE_OAUTH_ACCESS_TOKEN_TTL_SECONDS: "0" }), - /Invalid DEVSPACE_OAUTH_ACCESS_TOKEN_TTL_SECONDS: 0/, -); -assert.throws( - () => loadConfig({ ...baseEnv, DEVSPACE_ARTIFACT_MAX_FILE_BYTES: "0" }), - /Invalid DEVSPACE_ARTIFACT_MAX_FILE_BYTES: 0/, -); + writeDevspaceConfig({ + configVersion: 1, + server: { + host: "0.0.0.0", + port: 8787, + publicBaseUrl: "https://devspace.example.com/", + allowedHosts: ["example.internal"], + trustProxy: true, + }, + workspaces: { + allowedRoots: ["~/work"], + worktreeRoot: "~/trees", + }, + storage: { stateDir: "~/state" }, + tools: { mode: "claude" }, + ui: { enabled: false }, + artifacts: { enabled: true, maxFileBytes: 321 }, + skills: { enabled: false, paths: ["~/skills"], agentDir: "~/agent" }, + subagents: { + enabled: true, + providers: [{ id: "codex", enabled: true }], + }, + logging: { + level: "debug", + format: "pretty", + requests: false, + assets: true, + toolCalls: false, + shellCommands: true, + }, + oauth: { + accessTokenTtlSeconds: 120, + refreshTokenTtlSeconds: 240, + scopes: ["devspace", "admin"], + allowedRedirectHosts: ["chatgpt.com", "example.com"], + }, + }, env); + writeDevspaceAuth({ ownerToken: "persisted-owner-token-long-enough" }, env); -assert.equal(loadConfig(baseEnv).publicBaseUrl, "http://127.0.0.1:7676"); -assert.deepEqual(loadConfig(baseEnv).allowedHosts, ["localhost", "127.0.0.1", "::1"]); + const configured = loadConfig({ DEVSPACE_CONFIG_DIR: configDir }); + assert.equal(configured.host, "0.0.0.0"); + assert.equal(configured.port, 8787); + assert.equal(configured.publicBaseUrl, "https://devspace.example.com"); + assert.deepEqual(configured.allowedRoots, [resolve("~/work".replace("~", process.env.HOME!))]); + assert.deepEqual(configured.allowedHosts, [ + "localhost", + "127.0.0.1", + "::1", + "0.0.0.0", + "devspace.example.com", + "example.internal", + ]); + assert.equal(configured.toolMode, "claude"); + assert.equal(configured.uiEnabled, false); + assert.equal(configured.stateDir, resolve(process.env.HOME!, "state")); + assert.equal(configured.worktreeRoot, resolve(process.env.HOME!, "trees")); + assert.equal(configured.artifactsEnabled, true); + assert.equal(configured.artifactMaxFileBytes, 321); + assert.equal(configured.skillsEnabled, false); + assert.deepEqual(configured.skillPaths, [resolve(process.env.HOME!, "skills")]); + assert.equal(configured.agentDir, resolve(process.env.HOME!, "agent")); + assert.equal(configured.subagents.enabled, true); + assert.equal(configured.oauth.ownerToken, "persisted-owner-token-long-enough"); + assert.equal(configured.oauth.accessTokenTtlSeconds, 120); + assert.deepEqual(configured.oauth.scopes, ["devspace", "admin"]); + assert.deepEqual(configured.logging, { + level: "debug", + format: "pretty", + requests: false, + assets: true, + toolCalls: false, + shellCommands: true, + trustProxy: true, + }); -assert.equal( - loadConfig({ ...baseEnv, DEVSPACE_PUBLIC_BASE_URL: "https://abc.trycloudflare.com/" }).publicBaseUrl, - "https://abc.trycloudflare.com", -); -assert.deepEqual( - loadConfig({ ...baseEnv, DEVSPACE_PUBLIC_BASE_URL: "https://abc.trycloudflare.com/" }).allowedHosts, - ["localhost", "127.0.0.1", "::1", "abc.trycloudflare.com"], -); -assert.deepEqual( - loadConfig({ ...baseEnv, DEVSPACE_ALLOWED_HOSTS: "*" }).allowedHosts, - ["*"], -); + assert.equal(loadConfig(env).oauth.ownerToken, env.DEVSPACE_OAUTH_OWNER_TOKEN); +} finally { + rmSync(configDir, { recursive: true, force: true }); +} -const configDir = mkdtempSync(join(tmpdir(), "devspace-config-test-")); -writeFileSync( - join(configDir, "config.json"), - JSON.stringify({ - port: 8787, - allowedRoots: [process.cwd()], - publicBaseUrl: "https://devspace.example.com", - subagents: true, - artifactsEnabled: true, - artifactMaxFileBytes: 321, - tools: { mode: "claude" }, - ui: { enabled: false }, - }), -); -writeFileSync( - join(configDir, "auth.json"), - JSON.stringify({ - ownerToken: "persisted-owner-token-long-enough", - }), -); +const missingAuthDir = mkdtempSync(join(tmpdir(), "devspace-config-no-auth-test-")); +try { + assert.throws( + () => loadConfig({ DEVSPACE_CONFIG_DIR: missingAuthDir }), + /OAuth owner token is required/, + ); +} finally { + rmSync(missingAuthDir, { recursive: true, force: true }); +} -const fileConfig = loadConfig({ DEVSPACE_CONFIG_DIR: configDir }); -assert.equal(fileConfig.port, 8787); -assert.equal(fileConfig.oauth.ownerToken, "persisted-owner-token-long-enough"); -assert.equal(fileConfig.publicBaseUrl, "https://devspace.example.com"); -assert.equal(fileConfig.subagents.enabled, true); -assert.equal(fileConfig.subagents.providers.length, 7); -assert.equal(fileConfig.artifactsEnabled, true); -assert.equal(fileConfig.artifactMaxFileBytes, 321); -assert.equal(fileConfig.toolMode, "claude"); -assert.equal(fileConfig.uiEnabled, false); -assert.deepEqual(fileConfig.allowedHosts, [ - "localhost", - "127.0.0.1", - "::1", - "devspace.example.com", -]); +console.log("config tests passed"); diff --git a/src/config.ts b/src/config.ts index e42365574..c0e8f943a 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,15 +1,12 @@ -import { homedir } from "node:os"; -import { join, resolve } from "node:path"; +import { resolve } from "node:path"; +import type { ToolMode } from "./config-schema.js"; import { expandHomePath } from "./roots.js"; -import type { LoggingConfig, LogFormat, LogLevel } from "./logger.js"; +import type { LoggingConfig } from "./logger.js"; import type { OAuthConfig } from "./oauth-provider.js"; import { devspaceAgentsDir, devspaceSkillsDir, loadDevspaceFiles } from "./user-config.js"; -import { resolveSubagentsConfig, type SubagentsConfig } from "./local-agent-config.js"; +import type { SubagentsConfig } from "./local-agent-config.js"; -export type ToolMode = "claude" | "codex"; -const DEFAULT_OAUTH_ACCESS_TOKEN_TTL_SECONDS = 60 * 60; -const DEFAULT_OAUTH_REFRESH_TOKEN_TTL_SECONDS = 30 * 24 * 60 * 60; -const DEFAULT_ARTIFACT_MAX_FILE_BYTES = 100 * 1024 * 1024; +export type { ToolMode } from "./config-schema.js"; export interface ServerConfig { host: string; @@ -33,168 +30,13 @@ export interface ServerConfig { logging: LoggingConfig; } -function parsePort(value: string | number | undefined): number { - if (value === undefined || value === "") return 7676; - - const port = Number(value); - if (!Number.isInteger(port) || port < 1 || port > 65535) { - throw new Error(`Invalid PORT: ${value}`); - } - - return port; -} - -function parseAllowedRoots(value: string | string[] | undefined): string[] { - if (Array.isArray(value)) { - const roots = value.map((entry) => entry.trim()).filter(Boolean); - return (roots.length > 0 ? roots : [process.cwd()]).map((root) => resolve(expandHomePath(root))); - } - - const rawRoots = - value - ?.split(",") - .map((entry) => entry.trim()) - .filter(Boolean) ?? []; - - const roots = rawRoots.length > 0 ? rawRoots : [process.cwd()]; - return roots.map((root) => resolve(expandHomePath(root))); -} - -function parseAllowedHosts(value: string | string[] | undefined, derivedHosts: string[]): string[] { - if (Array.isArray(value)) { - return normalizeAllowedHosts(value, derivedHosts); - } - - const rawHosts = - value - ?.split(",") - .map((entry) => entry.trim()) - .filter(Boolean) ?? []; - - return normalizeAllowedHosts(rawHosts, derivedHosts); -} - -function normalizeAllowedHosts(rawHosts: string[], derivedHosts: string[]): string[] { - const hosts = rawHosts.length > 0 ? rawHosts : derivedHosts; - if (hosts.includes("*")) return ["*"]; - return Array.from(new Set(hosts.map((host) => host.trim()).filter(Boolean))); -} - -function parseBoolean(value: string | undefined): boolean { - return ["1", "true", "yes", "on"].includes(value?.toLowerCase() ?? ""); -} - -function parseLogLevel(value: string | undefined): LogLevel { - if (!value || value === "info") return "info"; - if (["silent", "error", "warn", "debug"].includes(value)) return value as LogLevel; - - throw new Error(`Invalid DEVSPACE_LOG_LEVEL: ${value}`); -} - -function parseLogFormat(value: string | undefined): LogFormat { - if (!value || value === "json") return "json"; - if (value === "pretty") return "pretty"; - - throw new Error(`Invalid DEVSPACE_LOG_FORMAT: ${value}`); -} - -function parsePathList(value: string | undefined): string[] { - return ( - value - ?.split(",") - .map((entry) => entry.trim()) - .filter(Boolean) ?? [] - ); -} - -function parseStringList(value: string | undefined, fallback: string[]): string[] { - const entries = value - ?.split(",") - .map((entry) => entry.trim()) - .filter(Boolean); - - return entries && entries.length > 0 ? entries : fallback; -} - -function parsePositiveInteger( - value: string | undefined, - fallback: number, - name: string, - max = Number.MAX_SAFE_INTEGER, -): number { - if (!value) return fallback; - - const parsed = Number(value); - if (!Number.isInteger(parsed) || parsed < 1 || parsed > max) { - throw new Error(`Invalid ${name}: ${value}`); - } - - return parsed; -} - -function parseLoggingConfig(env: NodeJS.ProcessEnv): LoggingConfig { - return { - level: parseLogLevel(env.DEVSPACE_LOG_LEVEL), - format: parseLogFormat(env.DEVSPACE_LOG_FORMAT), - requests: env.DEVSPACE_LOG_REQUESTS === undefined ? true : parseBoolean(env.DEVSPACE_LOG_REQUESTS), - assets: parseBoolean(env.DEVSPACE_LOG_ASSETS), - toolCalls: env.DEVSPACE_LOG_TOOL_CALLS === undefined ? true : parseBoolean(env.DEVSPACE_LOG_TOOL_CALLS), - shellCommands: parseBoolean(env.DEVSPACE_LOG_SHELL_COMMANDS), - trustProxy: parseBoolean(env.DEVSPACE_TRUST_PROXY), - }; -} - -function parseRequiredSecret(value: string | undefined, name: string): string { - const secret = value?.trim(); - if (!secret) { - throw new Error(`${name} is required for DevSpace OAuth. Run: devspace init`); - } - if (secret.length < 16) { - throw new Error(`${name} must be at least 16 characters long.`); - } - return secret; -} - -function parseOAuthConfig(env: NodeJS.ProcessEnv, ownerToken: string | undefined): OAuthConfig { - return { - ownerToken: parseRequiredSecret(env.DEVSPACE_OAUTH_OWNER_TOKEN ?? ownerToken, "DEVSPACE_OAUTH_OWNER_TOKEN"), - accessTokenTtlSeconds: parsePositiveInteger( - env.DEVSPACE_OAUTH_ACCESS_TOKEN_TTL_SECONDS, - DEFAULT_OAUTH_ACCESS_TOKEN_TTL_SECONDS, - "DEVSPACE_OAUTH_ACCESS_TOKEN_TTL_SECONDS", - ), - refreshTokenTtlSeconds: parsePositiveInteger( - env.DEVSPACE_OAUTH_REFRESH_TOKEN_TTL_SECONDS, - DEFAULT_OAUTH_REFRESH_TOKEN_TTL_SECONDS, - "DEVSPACE_OAUTH_REFRESH_TOKEN_TTL_SECONDS", - ), - scopes: parseStringList(env.DEVSPACE_OAUTH_SCOPES, ["devspace"]), - allowedRedirectHosts: parseStringList(env.DEVSPACE_OAUTH_ALLOWED_REDIRECT_HOSTS, [ - "chatgpt.com", - "localhost", - "127.0.0.1", - ]), - }; -} - -function defaultStateDir(): string { - return join(homedir(), ".local", "share", "devspace"); -} - -function defaultWorktreeRoot(): string { - return join(homedir(), ".devspace", "worktrees"); -} - -function defaultAgentDir(): string { - return join(homedir(), ".codex"); -} - export function loadConfig(env: NodeJS.ProcessEnv = process.env): ServerConfig { const files = loadDevspaceFiles(env); - const host = env.HOST ?? files.config.host ?? "127.0.0.1"; - const port = parsePort(env.PORT ?? files.config.port); + const stored = files.config; + const host = stored.server.host; + const port = stored.server.port; const publicBaseUrl = parsePublicBaseUrl( - env.DEVSPACE_PUBLIC_BASE_URL ?? files.config.publicBaseUrl ?? localPublicBaseUrl(host, port), + stored.server.publicBaseUrl ?? localPublicBaseUrl(host, port), ); const derivedAllowedHosts = [ "localhost", @@ -202,41 +44,65 @@ export function loadConfig(env: NodeJS.ProcessEnv = process.env): ServerConfig { "::1", host, new URL(publicBaseUrl).hostname, - ...(files.config.allowedHosts ?? []), + ...stored.server.allowedHosts, ]; return { host, port, - oauth: parseOAuthConfig(env, files.auth.ownerToken), - allowedRoots: parseAllowedRoots(env.DEVSPACE_ALLOWED_ROOTS ?? files.config.allowedRoots), - allowedHosts: parseAllowedHosts(env.DEVSPACE_ALLOWED_HOSTS, derivedAllowedHosts), + oauth: { + ownerToken: parseRequiredSecret( + env.DEVSPACE_OAUTH_OWNER_TOKEN ?? files.auth.ownerToken, + ), + accessTokenTtlSeconds: stored.oauth.accessTokenTtlSeconds, + refreshTokenTtlSeconds: stored.oauth.refreshTokenTtlSeconds, + scopes: stored.oauth.scopes, + allowedRedirectHosts: stored.oauth.allowedRedirectHosts, + }, + allowedRoots: normalizePaths(stored.workspaces.allowedRoots, [process.cwd()]), + allowedHosts: normalizeAllowedHosts(derivedAllowedHosts), publicBaseUrl, - toolMode: files.config.tools?.mode ?? "codex", - uiEnabled: files.config.ui?.enabled ?? true, - stateDir: resolve(expandHomePath(env.DEVSPACE_STATE_DIR ?? files.config.stateDir ?? defaultStateDir())), - worktreeRoot: resolve(expandHomePath(env.DEVSPACE_WORKTREE_ROOT ?? files.config.worktreeRoot ?? defaultWorktreeRoot())), - artifactsEnabled: - env.DEVSPACE_ARTIFACTS === undefined - ? files.config.artifactsEnabled === true - : parseBoolean(env.DEVSPACE_ARTIFACTS), - artifactMaxFileBytes: parsePositiveInteger( - env.DEVSPACE_ARTIFACT_MAX_FILE_BYTES ?? numberConfigValue(files.config.artifactMaxFileBytes), - DEFAULT_ARTIFACT_MAX_FILE_BYTES, - "DEVSPACE_ARTIFACT_MAX_FILE_BYTES", - ), - skillsEnabled: env.DEVSPACE_SKILLS === undefined ? true : parseBoolean(env.DEVSPACE_SKILLS), - skillPaths: parsePathList(env.DEVSPACE_SKILL_PATHS), + toolMode: stored.tools.mode, + uiEnabled: stored.ui.enabled, + stateDir: normalizePath(stored.storage.stateDir), + worktreeRoot: normalizePath(stored.workspaces.worktreeRoot), + artifactsEnabled: stored.artifacts.enabled, + artifactMaxFileBytes: stored.artifacts.maxFileBytes, + skillsEnabled: stored.skills.enabled, + skillPaths: normalizePaths(stored.skills.paths), devspaceSkillsDir: devspaceSkillsDir(env), devspaceAgentsDir: devspaceAgentsDir(env), - subagents: resolveSubagentsConfig(files.config.subagents, env), - agentDir: resolve(expandHomePath(env.DEVSPACE_AGENT_DIR ?? files.config.agentDir ?? defaultAgentDir())), - logging: parseLoggingConfig(env), + subagents: stored.subagents, + agentDir: normalizePath(stored.skills.agentDir), + logging: { + ...stored.logging, + trustProxy: stored.server.trustProxy, + }, }; } -function numberConfigValue(value: number | undefined): string | undefined { - return value === undefined ? undefined : String(value); +function normalizePaths(paths: string[], fallback: string[] = []): string[] { + return (paths.length > 0 ? paths : fallback).map(normalizePath); +} + +function normalizePath(path: string): string { + return resolve(expandHomePath(path)); +} + +function normalizeAllowedHosts(hosts: string[]): string[] { + if (hosts.includes("*")) return ["*"]; + return Array.from(new Set(hosts.map((host) => host.trim()).filter(Boolean))); +} + +function parseRequiredSecret(value: string | undefined): string { + const secret = value?.trim(); + if (!secret) { + throw new Error("OAuth owner token is required. Run: devspace init"); + } + if (secret.length < 16) { + throw new Error("OAuth owner token must be at least 16 characters long."); + } + return secret; } function parsePublicBaseUrl(value: string): string { diff --git a/src/local-agent-client.ts b/src/local-agent-client.ts index eb47ebafa..157826eb5 100644 --- a/src/local-agent-client.ts +++ b/src/local-agent-client.ts @@ -84,7 +84,7 @@ export class LocalAgentClient { this.endpoint = options.endpoint ?? this.paths.endpoint; this.startupTimeoutMs = options.startupTimeoutMs ?? DEFAULT_STARTUP_TIMEOUT_MS; this.requestTimeoutMs = options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS; - this.spawnDaemon = options.spawnDaemon ?? (() => spawnLocalAgentDaemon(options.stateDir)); + this.spawnDaemon = options.spawnDaemon ?? (() => spawnLocalAgentDaemon()); } async run( @@ -408,13 +408,13 @@ export function createLocalAgentClient(config: Pick): return new LocalAgentClient({ stateDir: config.stateDir }); } -export function spawnLocalAgentDaemon(stateDir: string, env: NodeJS.ProcessEnv = process.env): void { +export function spawnLocalAgentDaemon(env: NodeJS.ProcessEnv = process.env): void { const entrypoint = resolveDaemonEntrypoint(); const child = spawn(process.execPath, [...daemonExecArgv(process.execArgv), entrypoint], { detached: true, stdio: "ignore", windowsHide: true, - env: { ...env, DEVSPACE_STATE_DIR: stateDir }, + env, }); child.unref(); } diff --git a/src/local-agent-config.test.ts b/src/local-agent-config.test.ts index 713fed653..1751d0639 100644 --- a/src/local-agent-config.test.ts +++ b/src/local-agent-config.test.ts @@ -11,7 +11,7 @@ const config = resolveSubagentsConfig({ { id: "codex", enabled: true, model: " gpt-5.4 ", effort: " high " }, { id: "claude", enabled: false, model: "sonnet" }, ], -}, {}); +}); assert.deepEqual(config, { enabled: true, providers: [ @@ -24,31 +24,26 @@ assert.equal(isSubagentProviderEnabled(config, "claude"), false); assert.equal(isSubagentProviderEnabled(config, "pi"), false); assert.equal(subagentProviderConfig(config, "codex")?.model, "gpt-5.4"); -assert.equal(resolveSubagentsConfig(config, { DEVSPACE_SUBAGENTS: "0" }).enabled, false); -assert.equal(resolveSubagentsConfig({ ...config, enabled: false }, { - DEVSPACE_SUBAGENTS: "1", -}).enabled, true); -assert.equal(resolveSubagentsConfig(undefined, {}).providers.length, 0); -assert.equal(resolveSubagentsConfig(true, {}).providers.length, 7); +assert.equal(resolveSubagentsConfig(undefined).providers.length, 0); assert.throws( () => resolveSubagentsConfig({ enabled: true, providers: [{ id: "codex", enabled: true }, { id: "codex", enabled: false }], - }, {}), + }), /Duplicate subagent provider: codex/, ); assert.throws( () => resolveSubagentsConfig({ enabled: true, providers: [{ id: "unknown", enabled: true }], - }, {}), + }), /Invalid option/, ); assert.throws( () => resolveSubagentsConfig({ enabled: true, providers: [{ id: "codex", enabled: true, effort: " " }], - }, {}), + }), /Too small/, ); diff --git a/src/local-agent-config.ts b/src/local-agent-config.ts index 538355d92..e008788c7 100644 --- a/src/local-agent-config.ts +++ b/src/local-agent-config.ts @@ -39,19 +39,10 @@ export type StoredSubagentsConfig = z.infer; export function resolveSubagentsConfig( value: unknown, - env: NodeJS.ProcessEnv = process.env, ): SubagentsConfig { - const stored = value === undefined + return value === undefined ? { enabled: false, providers: [] } - : typeof value === "boolean" - ? legacySubagentsConfig(value) - : subagentsConfigSchema.parse(value); - return { - ...stored, - enabled: env.DEVSPACE_SUBAGENTS === undefined - ? stored.enabled - : parseBoolean(env.DEVSPACE_SUBAGENTS), - }; + : subagentsConfigSchema.parse(value); } export function subagentProviderConfig( @@ -67,16 +58,3 @@ export function isSubagentProviderEnabled( ): boolean { return config.enabled && subagentProviderConfig(config, provider)?.enabled === true; } - -function legacySubagentsConfig(enabled: boolean): SubagentsConfig { - return { - enabled, - providers: enabled - ? LOCAL_AGENT_PROVIDERS.map((id) => ({ id, enabled: true })) - : [], - }; -} - -function parseBoolean(value: string): boolean { - return ["1", "true", "yes", "on"].includes(value.toLowerCase()); -} diff --git a/src/user-config.test.ts b/src/user-config.test.ts index f13ca46f5..9ff9df6e6 100644 --- a/src/user-config.test.ts +++ b/src/user-config.test.ts @@ -1,41 +1,103 @@ import assert from "node:assert/strict"; -import { mkdtempSync, rmSync, writeFileSync } from "node:fs"; +import { + existsSync, + mkdtempSync, + readFileSync, + rmSync, + writeFileSync, +} from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; -import { loadDevspaceFiles } from "./user-config.js"; +import { + loadDevspaceFiles, + setDevspaceConfigValue, +} from "./user-config.js"; -const configDir = mkdtempSync(join(tmpdir(), "devspace-user-config-test-")); -const env = { DEVSPACE_CONFIG_DIR: configDir }; - -try { +withConfigDir((configDir, env) => { writeFileSync(join(configDir, "config.json"), JSON.stringify({ + host: "0.0.0.0", port: 8787, - subagents: { - enabled: true, - providers: [{ id: "codex", enabled: true }], - }, + allowedRoots: ["/work"], + publicBaseUrl: "https://devspace.example.com", + artifactsEnabled: true, + subagents: true, })); writeFileSync(join(configDir, "auth.json"), JSON.stringify({ ownerToken: "test-owner-token", })); - assert.deepEqual(loadDevspaceFiles(env).config, { - port: 8787, - subagents: { - enabled: true, - providers: [{ id: "codex", enabled: true }], + const files = loadDevspaceFiles(env); + assert.equal(files.migratedLegacyConfig, true); + assert.equal(files.config.server.host, "0.0.0.0"); + assert.equal(files.config.server.port, 8787); + assert.deepEqual(files.config.workspaces.allowedRoots, ["/work"]); + assert.equal(files.config.artifacts.enabled, true); + assert.equal(files.config.subagents.enabled, true); + assert.equal(files.config.tools.mode, "codex"); + assert.equal(files.config.ui.enabled, true); + assert.equal(files.auth.ownerToken, "test-owner-token"); + assert.equal(existsSync(join(configDir, "config.json")), false); + assert.equal(existsSync(join(configDir, "config.jsonc")), true); + assert.equal(existsSync(join(configDir, "config.json.v1.0.bak")), true); + + const nextLoad = loadDevspaceFiles(env); + assert.equal(nextLoad.migratedLegacyConfig, false); +}); + +withConfigDir((configDir, env) => { + writeFileSync(join(configDir, "config.jsonc"), `{ + // This comment must survive config updates. + "configVersion": 1, + "server": { + "port": 8787, }, - }); - assert.equal(loadDevspaceFiles(env).auth.ownerToken, "test-owner-token"); + }\n`); - writeFileSync(join(configDir, "config.json"), JSON.stringify({ port: "8787" })); - assert.throws(() => loadDevspaceFiles(env), /expected number/i); + const files = loadDevspaceFiles(env); + assert.equal(files.config.server.port, 8787); + assert.equal(files.config.tools.mode, "codex"); - writeFileSync(join(configDir, "config.json"), JSON.stringify({ unknownSetting: true })); - assert.equal(loadDevspaceFiles(env).config.unknownSetting, true); + setDevspaceConfigValue(["server", "publicBaseUrl"], "https://new.example.com", env); + const updated = readFileSync(join(configDir, "config.jsonc"), "utf8"); + assert.match(updated, /This comment must survive config updates/); + assert.equal(loadDevspaceFiles(env).config.server.publicBaseUrl, "https://new.example.com"); +}); +withConfigDir((configDir, env) => { + writeFileSync(join(configDir, "config.jsonc"), JSON.stringify({ configVersion: 1 })); writeFileSync(join(configDir, "config.json"), "{"); - assert.throws(() => loadDevspaceFiles(env), /Unable to read .*config\.json/); -} finally { - rmSync(configDir, { recursive: true, force: true }); + assert.equal(loadDevspaceFiles(env).config.server.port, 7676); + assert.equal(existsSync(join(configDir, "config.json")), true); +}); + +withConfigDir((configDir, env) => { + writeFileSync(join(configDir, "config.jsonc"), "{"); + writeFileSync(join(configDir, "config.json"), JSON.stringify({ port: 8787 })); + assert.throws(() => loadDevspaceFiles(env), /Unable to read .*config\.jsonc/); + assert.equal(existsSync(join(configDir, "config.json")), true); +}); + +withConfigDir((configDir, env) => { + writeFileSync(join(configDir, "config.json"), JSON.stringify({ unknownSetting: true })); + assert.throws( + () => loadDevspaceFiles(env), + /Unsupported legacy configuration keys: unknownSetting/, + ); + assert.equal(existsSync(join(configDir, "config.json")), true); + assert.equal(existsSync(join(configDir, "config.jsonc")), false); + assert.equal(existsSync(join(configDir, "config.json.v1.0.bak")), false); +}); + +console.log("user config tests passed"); + +function withConfigDir( + test: (configDir: string, env: NodeJS.ProcessEnv) => void, +): void { + const configDir = mkdtempSync(join(tmpdir(), "devspace-user-config-test-")); + const env = { DEVSPACE_CONFIG_DIR: configDir }; + try { + test(configDir, env); + } finally { + rmSync(configDir, { recursive: true, force: true }); + } } diff --git a/src/user-config.ts b/src/user-config.ts index 00535be27..b1524ee01 100644 --- a/src/user-config.ts +++ b/src/user-config.ts @@ -3,39 +3,34 @@ import { existsSync, mkdirSync, readFileSync, + renameSync, + rmSync, writeFileSync, } from "node:fs"; import { homedir } from "node:os"; -import { join, resolve } from "node:path"; +import { basename, dirname, join, resolve } from "node:path"; +import { + applyEdits, + modify, + parse, + printParseErrorCode, + type ParseError, +} from "jsonc-parser"; import * as z from "zod/v4"; +import { + defaultDevspaceConfig, + devspaceConfigSchema, + type DevspaceConfig, + type DevspaceConfigInput, +} from "./config-schema.js"; +import { migrateLegacyConfig } from "./config-migration.js"; import { expandHomePath } from "./roots.js"; -import { storedSubagentsConfigSchema } from "./local-agent-config.js"; - -const devspaceUserConfigSchema = z.object({ - host: z.string().optional(), - port: z.number().optional(), - allowedRoots: z.array(z.string()).optional(), - publicBaseUrl: z.string().nullable().optional(), - allowedHosts: z.array(z.string()).optional(), - stateDir: z.string().optional(), - worktreeRoot: z.string().optional(), - artifactsEnabled: z.boolean().optional(), - artifactMaxFileBytes: z.number().optional(), - agentDir: z.string().optional(), - subagents: storedSubagentsConfigSchema.optional(), - tools: z.object({ - mode: z.enum(["claude", "codex"]).optional(), - }).strict().optional(), - ui: z.object({ - enabled: z.boolean().optional(), - }).strict().optional(), -}).passthrough(); const devspaceAuthConfigSchema = z.object({ ownerToken: z.string().optional(), }).passthrough(); -export type DevspaceUserConfig = z.infer; +export type DevspaceUserConfig = DevspaceConfig; export type DevspaceAuthConfig = z.infer; export interface DevspaceFiles { @@ -44,8 +39,9 @@ export interface DevspaceFiles { authPath: string; configExists: boolean; authExists: boolean; - config: DevspaceUserConfig; + config: DevspaceConfig; auth: DevspaceAuthConfig; + migratedLegacyConfig: boolean; } export function devspaceConfigDir(env: NodeJS.ProcessEnv = process.env): string { @@ -53,9 +49,17 @@ export function devspaceConfigDir(env: NodeJS.ProcessEnv = process.env): string } export function devspaceConfigPath(env: NodeJS.ProcessEnv = process.env): string { + return join(devspaceConfigDir(env), "config.jsonc"); +} + +export function devspaceLegacyConfigPath(env: NodeJS.ProcessEnv = process.env): string { return join(devspaceConfigDir(env), "config.json"); } +export function devspaceLegacyConfigBackupPath(env: NodeJS.ProcessEnv = process.env): string { + return join(devspaceConfigDir(env), "config.json.v1.0.bak"); +} + export function devspaceAuthPath(env: NodeJS.ProcessEnv = process.env): string { return join(devspaceConfigDir(env), "auth.json"); } @@ -70,8 +74,12 @@ export function devspaceAgentsDir(env: NodeJS.ProcessEnv = process.env): string export function loadDevspaceFiles(env: NodeJS.ProcessEnv = process.env): DevspaceFiles { const dir = devspaceConfigDir(env); - const configPath = join(dir, "config.json"); - const authPath = join(dir, "auth.json"); + const configPath = devspaceConfigPath(env); + const legacyConfigPath = devspaceLegacyConfigPath(env); + const authPath = devspaceAuthPath(env); + const migratedLegacyConfig = !existsSync(configPath) && existsSync(legacyConfigPath) + ? migrateLegacyConfigFile(legacyConfigPath, configPath, devspaceLegacyConfigBackupPath(env)) + : false; const configExists = existsSync(configPath); const authExists = existsSync(authPath); @@ -81,28 +89,46 @@ export function loadDevspaceFiles(env: NodeJS.ProcessEnv = process.env): Devspac authPath, configExists, authExists, - config: configExists ? readJsonFile(configPath, devspaceUserConfigSchema) : {}, + config: configExists ? readJsoncConfig(configPath) : defaultDevspaceConfig(), auth: authExists ? readJsonFile(authPath, devspaceAuthConfigSchema) : {}, + migratedLegacyConfig, }; } export function writeDevspaceConfig( - config: DevspaceUserConfig, + config: DevspaceConfigInput, env: NodeJS.ProcessEnv = process.env, ): string { const filePath = devspaceConfigPath(env); - mkdirSync(devspaceConfigDir(env), { recursive: true }); - writeJsonFile(filePath, config, 0o600); + const parsed = devspaceConfigSchema.parse(config); + atomicWrite(filePath, serializeConfig(parsed), 0o600); return filePath; } +export function setDevspaceConfigValue( + path: (string | number)[], + value: unknown, + env: NodeJS.ProcessEnv = process.env, +): string { + const files = loadDevspaceFiles(env); + const source = files.configExists + ? readFileSync(files.configPath, "utf8") + : serializeConfig(files.config); + const updated = applyEdits(source, modify(source, path, value, { + formattingOptions: { insertSpaces: true, tabSize: 2, eol: "\n" }, + })); + parseJsoncConfig(updated, files.configPath); + atomicWrite(files.configPath, updated.endsWith("\n") ? updated : `${updated}\n`, 0o600); + return files.configPath; +} + export function writeDevspaceAuth( auth: DevspaceAuthConfig, env: NodeJS.ProcessEnv = process.env, ): string { const filePath = devspaceAuthPath(env); mkdirSync(devspaceConfigDir(env), { recursive: true }); - writeJsonFile(filePath, auth, 0o600); + writeJsonFile(filePath, devspaceAuthConfigSchema.parse(auth), 0o600); return filePath; } @@ -110,15 +136,99 @@ export function generateOwnerToken(): string { return randomBytes(32).toString("base64url"); } +function migrateLegacyConfigFile( + legacyPath: string, + configPath: string, + backupPath: string, +): true { + if (existsSync(backupPath)) { + throw new Error(`Unable to migrate ${legacyPath}: backup already exists at ${backupPath}`); + } + + let migrated: DevspaceConfig; + try { + migrated = migrateLegacyConfig(JSON.parse(readFileSync(legacyPath, "utf8")) as unknown); + } catch (error) { + throw fileError("migrate", legacyPath, error); + } + + const temporaryPath = temporaryFilePath(configPath); + try { + mkdirSync(dirname(configPath), { recursive: true }); + writeFileSync(temporaryPath, serializeConfig(migrated), { mode: 0o600, flag: "wx" }); + readJsoncConfig(temporaryPath); + renameSync(temporaryPath, configPath); + renameSync(legacyPath, backupPath); + } catch (error) { + rmSync(temporaryPath, { force: true }); + throw fileError("migrate", legacyPath, error); + } + return true; +} + +function readJsoncConfig(filePath: string): DevspaceConfig { + try { + return parseJsoncConfig(readFileSync(filePath, "utf8"), filePath); + } catch (error) { + if (error instanceof DevspaceConfigFileError) throw error; + throw fileError("read", filePath, error); + } +} + +function parseJsoncConfig(source: string, filePath: string): DevspaceConfig { + const errors: ParseError[] = []; + const value = parse(source, errors, { allowTrailingComma: true }); + if (errors.length > 0) { + const first = errors[0]!; + throw new DevspaceConfigFileError( + `Unable to read ${filePath}: ${printParseErrorCode(first.error)} at offset ${first.offset}`, + ); + } + try { + return devspaceConfigSchema.parse(value); + } catch (error) { + throw fileError("read", filePath, error); + } +} + +function serializeConfig(config: DevspaceConfig): string { + return `${JSON.stringify(config, null, 2)}\n`; +} + +function atomicWrite(filePath: string, source: string, mode: number): void { + mkdirSync(dirname(filePath), { recursive: true }); + const temporaryPath = temporaryFilePath(filePath); + try { + writeFileSync(temporaryPath, source, { mode, flag: "wx" }); + renameSync(temporaryPath, filePath); + } catch (error) { + rmSync(temporaryPath, { force: true }); + throw error; + } +} + +function temporaryFilePath(filePath: string): string { + return join( + dirname(filePath), + `.${basename(filePath)}.${process.pid}.${randomBytes(8).toString("hex")}.tmp`, + ); +} + function readJsonFile(filePath: string, schema: z.ZodType): T { try { return schema.parse(JSON.parse(readFileSync(filePath, "utf8")) as unknown); } catch (error) { - const reason = error instanceof Error ? error.message : String(error); - throw new Error(`Unable to read ${filePath}: ${reason}`); + throw fileError("read", filePath, error); } } function writeJsonFile(filePath: string, value: unknown, mode: number): void { - writeFileSync(filePath, JSON.stringify(value, null, 2) + "\n", { mode }); + writeFileSync(filePath, `${JSON.stringify(value, null, 2)}\n`, { mode }); } + +function fileError(action: "read" | "migrate", filePath: string, error: unknown): Error { + const reason = error instanceof Error ? error.message : String(error); + return new DevspaceConfigFileError(`Unable to ${action} ${filePath}: ${reason}`); +} + +class DevspaceConfigFileError extends Error {} From ebb1dea5f8d53bacaa71a884e7d805ca6981ceb7 Mon Sep 17 00:00:00 2001 From: Waishnav <86405648+Waishnav@users.noreply.github.com> Date: Mon, 24 Aug 2026 01:38:24 +0530 Subject: [PATCH 28/75] test(config): use persisted fixtures --- src/cli.test.ts | 44 ++++++-------------- src/config.test.ts | 2 +- src/config.ts | 2 +- src/local-agent-profiles.test.ts | 21 ++++------ src/server.test.ts | 16 ++++--- src/skills.test.ts | 67 ++++++++++++++---------------- src/test-support/config.test.ts | 43 +++++++++++++++++++ src/workspace-conversation.test.ts | 32 +++++++------- src/workspaces.test.ts | 56 ++++++++++++++----------- 9 files changed, 151 insertions(+), 132 deletions(-) create mode 100644 src/test-support/config.test.ts diff --git a/src/cli.test.ts b/src/cli.test.ts index 9a2022efd..27c91da40 100644 --- a/src/cli.test.ts +++ b/src/cli.test.ts @@ -11,6 +11,7 @@ import { loadConfig } from "./config.js"; import { localAgentDaemonPaths } from "./local-agent-daemon-lifecycle.js"; import { encodeLocalAgentDaemonResponse } from "./local-agent-daemon-protocol.js"; import { LocalAgentStore } from "./local-agent-store.js"; +import { writeTestDevspaceConfig } from "./test-support/config.test.js"; const execFileAsync = promisify(execFile); const require = createRequire(import.meta.url); @@ -38,6 +39,11 @@ try { mkdirSync(stateDir, { recursive: true }); mkdirSync(join(configDir, "agents"), { recursive: true }); mkdirSync(projectRoot, { recursive: true }); + const cliConfigEnv = writeTestDevspaceConfig(configDir, { + workspaces: { allowedRoots: [projectRoot] }, + storage: { stateDir }, + subagents: { enabled: true, providers: [] }, + }); writeFileSync( join(configDir, "agents", "reviewer.md"), [ @@ -138,13 +144,9 @@ try { encoding: "utf8", env: { ...process.env, - DEVSPACE_CONFIG_DIR: configDir, - DEVSPACE_ALLOWED_ROOTS: projectRoot, - DEVSPACE_STATE_DIR: stateDir, + ...cliConfigEnv, DEVSPACE_WORKSPACE_ID: "ws_current", DEVSPACE_WORKSPACE_ROOT: projectRoot, - DEVSPACE_SUBAGENTS: "1", - DEVSPACE_OAUTH_OWNER_TOKEN: "test-owner-token-that-is-long-enough", }, }); @@ -158,13 +160,9 @@ try { encoding: "utf8", env: { ...process.env, - DEVSPACE_CONFIG_DIR: configDir, - DEVSPACE_ALLOWED_ROOTS: projectRoot, - DEVSPACE_STATE_DIR: stateDir, + ...cliConfigEnv, DEVSPACE_WORKSPACE_ID: "ws_current", DEVSPACE_WORKSPACE_ROOT: projectRoot, - DEVSPACE_SUBAGENTS: "1", - DEVSPACE_OAUTH_OWNER_TOKEN: "test-owner-token-that-is-long-enough", }, }, ); @@ -181,11 +179,7 @@ try { encoding: "utf8", env: { ...process.env, - DEVSPACE_CONFIG_DIR: configDir, - DEVSPACE_ALLOWED_ROOTS: stateDir, - DEVSPACE_STATE_DIR: stateDir, - DEVSPACE_SUBAGENTS: "1", - DEVSPACE_OAUTH_OWNER_TOKEN: "test-owner-token-that-is-long-enough", + ...cliConfigEnv, DEVSPACE_WORKSPACE_ID: "", DEVSPACE_WORKSPACE_ROOT: stateDir, }, @@ -205,13 +199,9 @@ try { encoding: "utf8", env: { ...process.env, - DEVSPACE_CONFIG_DIR: configDir, - DEVSPACE_ALLOWED_ROOTS: projectRoot, - DEVSPACE_STATE_DIR: stateDir, + ...cliConfigEnv, DEVSPACE_WORKSPACE_ID: "ws_current", DEVSPACE_WORKSPACE_ROOT: projectRoot, - DEVSPACE_SUBAGENTS: "1", - DEVSPACE_OAUTH_OWNER_TOKEN: "test-owner-token-that-is-long-enough", }, }, ); @@ -247,13 +237,9 @@ try { encoding: "utf8", env: { ...process.env, - DEVSPACE_CONFIG_DIR: configDir, - DEVSPACE_ALLOWED_ROOTS: projectRoot, - DEVSPACE_STATE_DIR: stateDir, + ...cliConfigEnv, DEVSPACE_WORKSPACE_ID: "ws_current", DEVSPACE_WORKSPACE_ROOT: projectRoot, - DEVSPACE_SUBAGENTS: "1", - DEVSPACE_OAUTH_OWNER_TOKEN: "test-owner-token-that-is-long-enough", }, }, ), @@ -268,13 +254,7 @@ try { }); } - assert.equal(loadConfig({ - DEVSPACE_CONFIG_DIR: configDir, - DEVSPACE_ALLOWED_ROOTS: projectRoot, - DEVSPACE_STATE_DIR: stateDir, - DEVSPACE_SUBAGENTS: "1", - DEVSPACE_OAUTH_OWNER_TOKEN: "test-owner-token-that-is-long-enough", - }).subagents.enabled, true); + assert.equal(loadConfig(cliConfigEnv).subagents.enabled, true); } finally { rmSync(root, { recursive: true, force: true }); } diff --git a/src/config.test.ts b/src/config.test.ts index c266b1e8d..e353de53c 100644 --- a/src/config.test.ts +++ b/src/config.test.ts @@ -92,7 +92,7 @@ try { assert.equal(configured.artifactsEnabled, true); assert.equal(configured.artifactMaxFileBytes, 321); assert.equal(configured.skillsEnabled, false); - assert.deepEqual(configured.skillPaths, [resolve(process.env.HOME!, "skills")]); + assert.deepEqual(configured.skillPaths, ["~/skills"]); assert.equal(configured.agentDir, resolve(process.env.HOME!, "agent")); assert.equal(configured.subagents.enabled, true); assert.equal(configured.oauth.ownerToken, "persisted-owner-token-long-enough"); diff --git a/src/config.ts b/src/config.ts index c0e8f943a..496f3bdb8 100644 --- a/src/config.ts +++ b/src/config.ts @@ -69,7 +69,7 @@ export function loadConfig(env: NodeJS.ProcessEnv = process.env): ServerConfig { artifactsEnabled: stored.artifacts.enabled, artifactMaxFileBytes: stored.artifacts.maxFileBytes, skillsEnabled: stored.skills.enabled, - skillPaths: normalizePaths(stored.skills.paths), + skillPaths: stored.skills.paths, devspaceSkillsDir: devspaceSkillsDir(env), devspaceAgentsDir: devspaceAgentsDir(env), subagents: stored.subagents, diff --git a/src/local-agent-profiles.test.ts b/src/local-agent-profiles.test.ts index 6868e140e..d3c5705f1 100644 --- a/src/local-agent-profiles.test.ts +++ b/src/local-agent-profiles.test.ts @@ -4,6 +4,7 @@ import { tmpdir } from "node:os"; import { join } from "node:path"; import { loadConfig } from "./config.js"; import { loadLocalAgentProfiles, summarizeLocalAgentProfile } from "./local-agent-profiles.js"; +import { writeTestDevspaceConfig } from "./test-support/config.test.js"; const root = await mkdtemp(join(tmpdir(), "devspace-agent-profiles-test-")); @@ -57,12 +58,10 @@ try { ].join("\n"), ); - const enabledConfig = loadConfig({ - DEVSPACE_CONFIG_DIR: configDir, - DEVSPACE_ALLOWED_ROOTS: workspaceRoot, - DEVSPACE_SUBAGENTS: "1", - DEVSPACE_OAUTH_OWNER_TOKEN: "test-owner-token-that-is-long-enough", - }); + const enabledConfig = loadConfig(writeTestDevspaceConfig(configDir, { + workspaces: { allowedRoots: [workspaceRoot] }, + subagents: { enabled: true, providers: [] }, + })); const profiles = await loadLocalAgentProfiles(enabledConfig, workspaceRoot); assert.equal(profiles.length, 1); @@ -96,12 +95,10 @@ try { const profilesWithInvalid = await loadLocalAgentProfiles(enabledConfig, workspaceRoot); assert.deepEqual(profilesWithInvalid.map((profile) => profile.name), ["reviewer"]); - const disabledConfig = loadConfig({ - DEVSPACE_CONFIG_DIR: configDir, - DEVSPACE_ALLOWED_ROOTS: workspaceRoot, - DEVSPACE_SUBAGENTS: "0", - DEVSPACE_OAUTH_OWNER_TOKEN: "test-owner-token-that-is-long-enough", - }); + const disabledConfig = loadConfig(writeTestDevspaceConfig(configDir, { + workspaces: { allowedRoots: [workspaceRoot] }, + subagents: { enabled: false, providers: [] }, + })); assert.deepEqual(await loadLocalAgentProfiles(disabledConfig, workspaceRoot), []); } finally { await rm(root, { recursive: true, force: true }); diff --git a/src/server.test.ts b/src/server.test.ts index 4f1215c18..b7d8d3513 100644 --- a/src/server.test.ts +++ b/src/server.test.ts @@ -16,6 +16,7 @@ import { ProcessSessionManager } from "./process-sessions.js"; import { createMcpServer } from "./server.js"; import { SqliteWorkspaceStore } from "./workspace-store.js"; import { WorkspaceRegistry } from "./workspaces.js"; +import { writeTestDevspaceConfig } from "./test-support/config.test.js"; const execFileAsync = promisify(execFile); @@ -373,15 +374,12 @@ async function fixture( const initialProviderAvailability = typeof options.localAgentProviders === "function" ? options.localAgentProviders() : options.localAgentProviders ?? []; - const loadedConfig = loadConfig({ - DEVSPACE_CONFIG_DIR: join(root, ".config"), - DEVSPACE_ALLOWED_ROOTS: root, - DEVSPACE_WORKTREE_ROOT: join(root, ".worktrees"), - DEVSPACE_AGENT_DIR: agentDir, - DEVSPACE_SUBAGENTS: options.localAgentProviders ? "1" : "0", - DEVSPACE_OAUTH_OWNER_TOKEN: "test-owner-token-that-is-long-enough", - PORT: "1", - }); + const loadedConfig = loadConfig(writeTestDevspaceConfig(join(root, ".config"), { + server: { port: 1 }, + workspaces: { allowedRoots: [root], worktreeRoot: join(root, ".worktrees") }, + skills: { agentDir }, + subagents: { enabled: options.localAgentProviders !== undefined, providers: [] }, + })); const modeConfig: ServerConfig = { ...loadedConfig, toolMode: options.toolMode ?? loadedConfig.toolMode, diff --git a/src/skills.test.ts b/src/skills.test.ts index 9db16a103..41556b3ad 100644 --- a/src/skills.test.ts +++ b/src/skills.test.ts @@ -9,6 +9,7 @@ import { loadWorkspaceSkills, resolveSkillReadPath, } from "./skills.js"; +import { writeTestDevspaceConfig } from "./test-support/config.test.js"; const root = await mkdtemp(join(tmpdir(), "devspace-skills-test-")); const originalHome = process.env.HOME; @@ -160,23 +161,22 @@ try { ].join("\n"), ); - const disabledConfig = loadConfig({ - DEVSPACE_ALLOWED_ROOTS: projectRoot, - DEVSPACE_AGENT_DIR: agentDir, - DEVSPACE_SKILL_PATHS: explicitSkills, - DEVSPACE_SKILLS: "0", - DEVSPACE_OAUTH_OWNER_TOKEN: "test-owner-token-that-is-long-enough", - PORT: "1", - }); + const configDir = join(root, ".devspace"); + const disabledConfig = loadConfig(writeTestDevspaceConfig(configDir, { + server: { port: 1 }, + workspaces: { allowedRoots: [projectRoot] }, + skills: { agentDir, paths: [explicitSkills], enabled: false }, + })); assert.deepEqual(loadWorkspaceSkills(disabledConfig, projectRoot).skills, []); - const config = loadConfig({ - DEVSPACE_ALLOWED_ROOTS: projectRoot, - DEVSPACE_AGENT_DIR: agentDir, - DEVSPACE_SKILL_PATHS: [explicitSkills, "~/.claude/skills", "./.claude/skills"].join(","), - DEVSPACE_OAUTH_OWNER_TOKEN: "test-owner-token-that-is-long-enough", - PORT: "1", - }); + const config = loadConfig(writeTestDevspaceConfig(configDir, { + server: { port: 1 }, + workspaces: { allowedRoots: [projectRoot] }, + skills: { + agentDir, + paths: [explicitSkills, "~/.claude/skills", "./.claude/skills"], + }, + })); const loaded = loadWorkspaceSkills(config, projectRoot); assert.equal(loaded.skills.some((skill) => skill.name === "agent-global-skill"), true); assert.equal(loaded.skills.some((skill) => skill.name === "agent-project-skill"), true); @@ -195,13 +195,12 @@ try { false, ); - const experimentalConfig = loadConfig({ - DEVSPACE_ALLOWED_ROOTS: projectRoot, - DEVSPACE_AGENT_DIR: agentDir, - DEVSPACE_SUBAGENTS: "1", - DEVSPACE_OAUTH_OWNER_TOKEN: "test-owner-token-that-is-long-enough", - PORT: "1", - }); + const experimentalConfig = loadConfig(writeTestDevspaceConfig(configDir, { + server: { port: 1 }, + workspaces: { allowedRoots: [projectRoot] }, + skills: { agentDir }, + subagents: { enabled: true, providers: [] }, + })); assert.equal( loadWorkspaceSkills(experimentalConfig, projectRoot).skills.some( (skill) => skill.name === "subagents", @@ -209,25 +208,21 @@ try { true, ); - const duplicateConfig = loadConfig({ - DEVSPACE_ALLOWED_ROOTS: projectRoot, - DEVSPACE_AGENT_DIR: agentDir, - DEVSPACE_SKILL_PATHS: [explicitSkills, "./.agents/skills"].join(","), - DEVSPACE_OAUTH_OWNER_TOKEN: "test-owner-token-that-is-long-enough", - PORT: "1", - }); + const duplicateConfig = loadConfig(writeTestDevspaceConfig(configDir, { + server: { port: 1 }, + workspaces: { allowedRoots: [projectRoot] }, + skills: { agentDir, paths: [explicitSkills, "./.agents/skills"] }, + })); assert.equal( effectiveSkillPaths(duplicateConfig, projectRoot).filter((path) => path === projectAgentsSkills).length, 1, ); - const legacyPiConfig = loadConfig({ - DEVSPACE_ALLOWED_ROOTS: projectRoot, - DEVSPACE_AGENT_DIR: agentDir, - DEVSPACE_SKILL_PATHS: [explicitSkills, join(projectRoot, ".pi", "skills")].join(","), - DEVSPACE_OAUTH_OWNER_TOKEN: "test-owner-token-that-is-long-enough", - PORT: "1", - }); + const legacyPiConfig = loadConfig(writeTestDevspaceConfig(configDir, { + server: { port: 1 }, + workspaces: { allowedRoots: [projectRoot] }, + skills: { agentDir, paths: [explicitSkills, join(projectRoot, ".pi", "skills")] }, + })); assert.equal( loadWorkspaceSkills(legacyPiConfig, projectRoot).skills.some((skill) => skill.name === "project-skill"), true, diff --git a/src/test-support/config.test.ts b/src/test-support/config.test.ts new file mode 100644 index 000000000..2c97bc4e8 --- /dev/null +++ b/src/test-support/config.test.ts @@ -0,0 +1,43 @@ +import { + defaultDevspaceConfig, + type DevspaceConfig, +} from "../config-schema.js"; +import { writeDevspaceConfig } from "../user-config.js"; + +type SectionOverrides = { + server?: Partial; + workspaces?: Partial; + storage?: Partial; + tools?: Partial; + ui?: Partial; + artifacts?: Partial; + skills?: Partial; + subagents?: DevspaceConfig["subagents"]; + logging?: Partial; + oauth?: Partial; +}; + +export function writeTestDevspaceConfig( + configDir: string, + overrides: SectionOverrides = {}, +): NodeJS.ProcessEnv { + const defaults = defaultDevspaceConfig(); + const env = { DEVSPACE_CONFIG_DIR: configDir }; + writeDevspaceConfig({ + ...defaults, + server: { ...defaults.server, ...overrides.server }, + workspaces: { ...defaults.workspaces, ...overrides.workspaces }, + storage: { ...defaults.storage, ...overrides.storage }, + tools: { ...defaults.tools, ...overrides.tools }, + ui: { ...defaults.ui, ...overrides.ui }, + artifacts: { ...defaults.artifacts, ...overrides.artifacts }, + skills: { ...defaults.skills, ...overrides.skills }, + subagents: overrides.subagents ?? defaults.subagents, + logging: { ...defaults.logging, ...overrides.logging }, + oauth: { ...defaults.oauth, ...overrides.oauth }, + }, env); + return { + ...env, + DEVSPACE_OAUTH_OWNER_TOKEN: "test-owner-token-that-is-long-enough", + }; +} diff --git a/src/workspace-conversation.test.ts b/src/workspace-conversation.test.ts index 5af9f991d..45c45a14d 100644 --- a/src/workspace-conversation.test.ts +++ b/src/workspace-conversation.test.ts @@ -9,6 +9,7 @@ import { loadConfig, type ServerConfig } from "./config.js"; import { openDatabase } from "./db/client.js"; import { SqliteWorkspaceStore } from "./workspace-store.js"; import { WorkspaceRegistry } from "./workspaces.js"; +import { writeTestDevspaceConfig } from "./test-support/config.test.js"; const execFileAsync = promisify(execFile); @@ -242,14 +243,14 @@ test("canonical checkout identity survives macOS var path aliases", { skip: plat return; } - const aliasConfig = loadConfig({ - DEVSPACE_CONFIG_DIR: join(context.root, ".alias-config"), - DEVSPACE_ALLOWED_ROOTS: `${context.root},${macAlias}`, - DEVSPACE_WORKTREE_ROOT: join(context.root, ".worktrees"), - DEVSPACE_AGENT_DIR: join(context.root, "agent"), - DEVSPACE_OAUTH_OWNER_TOKEN: "test-owner-token-that-is-long-enough", - PORT: "1", - }); + const aliasConfig = loadConfig(writeTestDevspaceConfig(join(context.root, ".alias-config"), { + server: { port: 1 }, + workspaces: { + allowedRoots: [context.root, macAlias], + worktreeRoot: join(context.root, ".worktrees"), + }, + skills: { agentDir: join(context.root, "agent") }, + })); const aliasRegistry = new WorkspaceRegistry(aliasConfig, context.store); const direct = await context.registry.openWorkspace(context.project, { @@ -416,15 +417,12 @@ async function fixture( if (options.git) await initializeGitRepository(project); - const config = loadConfig({ - DEVSPACE_CONFIG_DIR: join(root, ".config"), - DEVSPACE_ALLOWED_ROOTS: root, - DEVSPACE_WORKTREE_ROOT: join(root, ".worktrees"), - DEVSPACE_AGENT_DIR: agentDir, - DEVSPACE_SUBAGENTS: "1", - DEVSPACE_OAUTH_OWNER_TOKEN: "test-owner-token-that-is-long-enough", - PORT: "1", - }); + const config = loadConfig(writeTestDevspaceConfig(join(root, ".config"), { + server: { port: 1 }, + workspaces: { allowedRoots: [root], worktreeRoot: join(root, ".worktrees") }, + skills: { agentDir }, + subagents: { enabled: true, providers: [] }, + })); const openStore = () => { const store = new SqliteWorkspaceStore(stateDir); stores.add(store); diff --git a/src/workspaces.test.ts b/src/workspaces.test.ts index 8584c1b7e..3dab10807 100644 --- a/src/workspaces.test.ts +++ b/src/workspaces.test.ts @@ -9,6 +9,7 @@ import { loadConfig, type ServerConfig } from "./config.js"; import { GitWorktreeError } from "./git-worktrees.js"; import { SqliteWorkspaceStore } from "./workspace-store.js"; import { WorkspaceRegistry } from "./workspaces.js"; +import { writeTestDevspaceConfig } from "./test-support/config.test.js"; const execFileAsync = promisify(execFile); @@ -47,14 +48,17 @@ test("a checkout exposes initial and nested instruction context while filtering await writeFile(join(context.outsideRoot, "secret.txt"), "outside secret\n"); await symlink(join(context.outsideRoot, "secret.txt"), join(unsafeAgentDir, "AGENTS.md")); - const unsafeConfig = loadConfig({ - DEVSPACE_CONFIG_DIR: join(context.root, ".devspace-unsafe-home"), - DEVSPACE_ALLOWED_ROOTS: context.root, - DEVSPACE_WORKTREE_ROOT: join(context.root, ".devspace", "unsafe-worktrees"), - DEVSPACE_AGENT_DIR: unsafeAgentDir, - DEVSPACE_OAUTH_OWNER_TOKEN: "test-owner-token-that-is-long-enough", - PORT: "1", - }); + const unsafeConfig = loadConfig(writeTestDevspaceConfig( + join(context.root, ".devspace-unsafe-home"), + { + server: { port: 1 }, + workspaces: { + allowedRoots: [context.root], + worktreeRoot: join(context.root, ".devspace", "unsafe-worktrees"), + }, + skills: { agentDir: unsafeAgentDir }, + }, + )); const unsafeWorkspace = await new WorkspaceRegistry(unsafeConfig).openWorkspace(context.root); assert.deepEqual( @@ -144,13 +148,17 @@ test("a symlinked allowed root preserves checkout and worktree path behavior", { await symlink(context.root, aliasRoot, "dir"); await createGitProject(context.root); - const aliasConfig = loadConfig({ - DEVSPACE_ALLOWED_ROOTS: aliasRoot, - DEVSPACE_WORKTREE_ROOT: join(aliasRoot, ".devspace", "alias-worktrees"), - DEVSPACE_AGENT_DIR: context.agentDir, - DEVSPACE_OAUTH_OWNER_TOKEN: "test-owner-token-that-is-long-enough", - PORT: "1", - }); + const aliasConfig = loadConfig(writeTestDevspaceConfig( + join(context.root, ".devspace-alias-home"), + { + server: { port: 1 }, + workspaces: { + allowedRoots: [aliasRoot], + worktreeRoot: join(aliasRoot, ".devspace", "alias-worktrees"), + }, + skills: { agentDir: context.agentDir }, + }, + )); const aliasRegistry = new WorkspaceRegistry(aliasConfig); const worktree = await aliasRegistry.openWorkspace({ @@ -207,15 +215,15 @@ async function fixture(t: TestContext): Promise { await writeFile(join(root, "nested", "AGENTS.md"), "nested instructions\n"); await writeFile(join(root, "nested", "file.txt"), "hello\n"); - const config = loadConfig({ - DEVSPACE_CONFIG_DIR: join(root, ".devspace-home"), - DEVSPACE_ALLOWED_ROOTS: root, - DEVSPACE_WORKTREE_ROOT: join(root, ".devspace", "worktrees"), - DEVSPACE_AGENT_DIR: agentDir, - DEVSPACE_SUBAGENTS: "1", - DEVSPACE_OAUTH_OWNER_TOKEN: "test-owner-token-that-is-long-enough", - PORT: "1", - }); + const config = loadConfig(writeTestDevspaceConfig(join(root, ".devspace-home"), { + server: { port: 1 }, + workspaces: { + allowedRoots: [root], + worktreeRoot: join(root, ".devspace", "worktrees"), + }, + skills: { agentDir }, + subagents: { enabled: true, providers: [] }, + })); t.after(async () => { await rm(root, { recursive: true, force: true }); From aa2d6ea41237ae57bf1c98c898ba25b2c03b0b5f Mon Sep 17 00:00:00 2001 From: Waishnav <86405648+Waishnav@users.noreply.github.com> Date: Mon, 24 Aug 2026 01:41:36 +0530 Subject: [PATCH 29/75] docs(config): document JSONC configuration --- .github/workflows/ci.yml | 2 - docs/artifact-exchange.md | 5 +- docs/chatgpt-coding-workflow.md | 14 +- docs/configuration.md | 392 ++++++++++++++------------------ docs/gotchas.md | 33 +-- docs/security.md | 10 +- docs/setup.md | 12 +- src/cli.ts | 7 +- 8 files changed, 201 insertions(+), 274 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2fffcedb0..9db018c62 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,9 +24,7 @@ jobs: - windows-latest env: - DEVSPACE_ALLOWED_ROOTS: ${{ github.workspace }} DEVSPACE_OAUTH_OWNER_TOKEN: ci-owner-token-that-is-long-enough - DEVSPACE_PUBLIC_BASE_URL: http://127.0.0.1:7676 steps: - name: Checkout diff --git a/docs/artifact-exchange.md b/docs/artifact-exchange.md index 6a6f5c75a..f4728eb0d 100644 --- a/docs/artifact-exchange.md +++ b/docs/artifact-exchange.md @@ -1,7 +1,8 @@ # Download a native file DevSpace can save a file attached or generated by an MCP host, such as ChatGPT, -directly into an open workspace. Enable the tool with `DEVSPACE_ARTIFACTS=1`. +directly into an open workspace. Enable the tool with +`artifacts.enabled` in `~/.devspace/config.jsonc`. ## Workflow @@ -36,7 +37,7 @@ file-object shape, trusted OpenAI download hosts, and redirects before streaming Malformed references, unknown fields, absolute paths, traversal, and symlinked parents are rejected. -Downloads are streamed under `DEVSPACE_ARTIFACT_MAX_FILE_BYTES` and published as +Downloads are streamed under `artifacts.maxFileBytes` and published as owner-only files without overwriting an existing destination. The tool is currently available on Linux. It is not registered on macOS, Windows, or BSD because Node.js does not expose the required descriptor-relative filesystem diff --git a/docs/chatgpt-coding-workflow.md b/docs/chatgpt-coding-workflow.md index a2296fa8e..1cb43e7ac 100644 --- a/docs/chatgpt-coding-workflow.md +++ b/docs/chatgpt-coding-workflow.md @@ -117,8 +117,8 @@ DevSpace discovers standard Agent Skills from: It also keeps compatibility with: - the bundled `subagents` skill when Subagents are enabled, unless `~/.devspace/skills/subagents/SKILL.md` exists -- `DEVSPACE_AGENT_DIR/skills`, defaulting to `~/.codex/skills` -- additional paths from `DEVSPACE_SKILL_PATHS` +- `skills.agentDir/skills`, defaulting to `~/.codex/skills` +- additional paths from `skills.paths` When Subagents are enabled, DevSpace discovers agent profiles from `~/.devspace/agents/*.md` and project `.devspace/agents/*.md`. @@ -130,7 +130,7 @@ Example profiles are packaged under `examples/agents/` for users who want starter templates. Copy or adapt them into one of the active profile directories before use. -Legacy project paths such as `.pi/skills` can be added through `DEVSPACE_SKILL_PATHS` when needed. +Legacy project paths such as `.pi/skills` can be added to `skills.paths` when needed. When `open_workspace` returns matching skills, the model should read the advertised `SKILL.md` before following that skill. @@ -140,8 +140,8 @@ Skill paths may be outside the workspace. DevSpace only permits reading: - advertised `SKILL.md` files - files under a skill directory after that skill's `SKILL.md` has been read -Set `DEVSPACE_SKILLS=0` to hide skills from workspace output. Enable Subagents -and choose providers through `devspace init` or the persisted provider +Set `skills.enabled` to `false` to hide skills from workspace output. Enable +Subagents and choose providers through `devspace init` or the persisted provider configuration. The bundled `subagents` skill teaches the minimal `devspace agents targets`, `devspace agents ls`, `devspace agents run`, `devspace agents continue`, and `devspace agents show` workflow. The catalog @@ -173,7 +173,7 @@ returns a process session ID when a command is still running after its yield window. Use `write_stdin` to poll it, send input, resize a PTY, or send Ctrl-C. Set `tty: true` only for commands that need a terminal. -Set `tools.mode` to `claude` in `~/.devspace/config.json` to expose `write`, +Set `tools.mode` to `claude` in `~/.devspace/config.jsonc` to expose `write`, `edit`, and `bash` instead of the Codex mutation and command tools. Dedicated MCP tools for `grep`, `glob`, and `ls` are not registered in either mode; use the configured shell tool with command-line tools such as `rg`, `find`, and @@ -184,7 +184,7 @@ the configured shell tool with command-line tools such as `rg`, `find`, and DevSpace exposes `show_changes` in both tool modes and attaches widget UI only to `open_workspace` and `show_changes`. Reads, edits, and commands return normal MCP results without creating an iframe for each call. Set `ui.enabled` to -`false` in `~/.devspace/config.json` to disable UI metadata while keeping the +`false` in `~/.devspace/config.jsonc` to disable UI metadata while keeping the aggregate review tool available. Call `show_changes` exactly once after the final file modification in any turn diff --git a/docs/configuration.md b/docs/configuration.md index d246f4f1b..6a6f607bd 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -1,171 +1,112 @@ # Configuration Reference -DevSpace can be configured through `devspace init`, persisted config files, or -environment variables. +DevSpace stores durable settings in `~/.devspace/config.jsonc`. The file accepts +comments and trailing commas and is validated before the server starts. Editor +completion is provided by the versioned [JSON Schema](../schema/v1/devspace.schema.json), +also hosted at the URL in the file's `$schema` property. -The default files are: +Authentication stays separate because it contains a secret: ```text -~/.devspace/config.json +~/.devspace/config.jsonc ~/.devspace/auth.json ``` -Use another config directory with: +Run `devspace init` to create both files. `devspace config set publicBaseUrl +` updates the JSONC document without discarding its comments. -```bash -DEVSPACE_CONFIG_DIR=/path/to/config npx @waishnav/devspace serve -``` - -## Commands - -```bash -npx @waishnav/devspace init -npx @waishnav/devspace serve -npx @waishnav/devspace doctor -npx @waishnav/devspace config get -npx @waishnav/devspace config set publicBaseUrl https://devspace.example.com -``` - -## Core Environment Variables - -| Variable | Purpose | -| --- | --- | -| `HOST` | Local bind host. Defaults to `127.0.0.1`. | -| `PORT` | Local port. Defaults to `7676`. | -| `DEVSPACE_ALLOWED_ROOTS` | Comma-separated local roots that workspaces may open. | -| `DEVSPACE_PUBLIC_BASE_URL` | Public origin for the server, without `/mcp`. | -| `DEVSPACE_ALLOWED_HOSTS` | Optional Host header allowlist override. | -| `DEVSPACE_OAUTH_OWNER_TOKEN` | Owner password for OAuth approval. Must be at least 16 characters. | -| `DEVSPACE_WORKTREE_ROOT` | Directory for managed Git worktrees. Defaults to `~/.devspace/worktrees`. | -| `DEVSPACE_STATE_DIR` | Directory for SQLite state. Defaults to `~/.local/share/devspace`. | - -## Native Artifact Download - -Native-file download is disabled by default. Enable it when ChatGPT needs to hand -an attached or generated file into an already-open workspace: - -```bash -DEVSPACE_ARTIFACTS=1 npx @waishnav/devspace serve -``` - -This feature currently supports Linux. It is not registered on macOS, Windows, -or BSD because the secure publication path depends on traversable, -descriptor-anchored directory paths provided by Linux procfs. - -| Variable | Default | Purpose | -| --- | --- | --- | -| `DEVSPACE_ARTIFACTS` | `0` | Expose `download_artifact` for trusted native files. | -| `DEVSPACE_ARTIFACT_MAX_FILE_BYTES` | `104857600` | Maximum streamed size of one file (100 MiB). | - -The same settings may be persisted in `~/.devspace/config.json` as -`artifactsEnabled` and `artifactMaxFileBytes`. - -`download_artifact` accepts the native file object supplied by the MCP connector, -a `workspaceId` returned by `open_workspace`, and a relative workspace `path`. -DevSpace safely creates missing parent directories, refuses to overwrite an -existing destination, and returns only the normalized workspace-relative path. -It does not accept conflict modes, expected hashes, arbitrary URL strings, local -paths, embedded credentials, or extra object fields. +## Complete example -There is no artifact root, total quota, TTL, pinning, persistent database record, -or background artifact cleanup service. See [Native File Download](artifact-exchange.md) -for the supported connector shape and security boundaries. - -## OAuth - -DevSpace uses a single-user OAuth approval flow. - -| Variable | Default | -| --- | --- | -| `DEVSPACE_OAUTH_ACCESS_TOKEN_TTL_SECONDS` | `3600` | -| `DEVSPACE_OAUTH_REFRESH_TOKEN_TTL_SECONDS` | `2592000` | -| `DEVSPACE_OAUTH_SCOPES` | `devspace` | -| `DEVSPACE_OAUTH_ALLOWED_REDIRECT_HOSTS` | `chatgpt.com,localhost,127.0.0.1` | - -MCP clients discover metadata from: - -```text -/.well-known/oauth-protected-resource/mcp -/.well-known/oauth-authorization-server -``` - -## Tool Modes - -`tools.mode` in `~/.devspace/config.json` controls the tool surface: - -```json +```jsonc { + "$schema": "https://raw.githubusercontent.com/Waishnav/devspace/main/schema/v1/devspace.schema.json", + "configVersion": 1, + + "server": { + "host": "127.0.0.1", + "port": 7676, + // Use the public origin only; do not append /mcp. + "publicBaseUrl": "https://devspace.example.com", + "allowedHosts": [], + "trustProxy": false, + }, + "workspaces": { + "allowedRoots": ["~/personal", "~/work"], + "worktreeRoot": "~/.devspace/worktrees", + }, + "storage": { + "stateDir": "~/.local/share/devspace", + }, "tools": { - "mode": "codex" - } -} -``` - -`DEVSPACE_TOOL_MODE` and `DEVSPACE_MINIMAL_TOOLS` are no longer read. Set -`tools.mode` in the configuration file when selecting the Claude surface; -omitting it selects Codex. - -| Value | Behavior | -| --- | --- | -| `codex` | Default. Exposes `open_workspace`, `read`, `apply_patch`, `exec_command`, and `write_stdin`. | -| `claude` | Exposes `open_workspace`, `read`, `write`, `edit`, and `bash`. Clients use `bash` with tools such as `rg`, `find`, and `ls` for inspection. | - -The dedicated MCP tools `grep`, `glob`, and `ls` are no longer exposed. Both -modes use their shell tool for search, file discovery, and directory inspection. - -Codex-mode commands run without a PTY by default. Set `tty: true` on -`exec_command` for interactive terminal programs. PTY support uses the optional -`node-pty` dependency; `write_stdin` can send input, poll output, and resize PTY -sessions. - -## UI - -DevSpace attaches ChatGPT Apps UI metadata only to `open_workspace` and -`show_changes`. This avoids creating an iframe for every read, edit, or command -tool call. The aggregate `show_changes` tool remains available to every MCP -host, including hosts that ignore UI metadata. - -UI is enabled by default. Disable it without removing `show_changes`: - -```json -{ + "mode": "codex", + }, "ui": { - "enabled": false - } + "enabled": true, + }, + "artifacts": { + "enabled": false, + "maxFileBytes": 104857600, + }, + "skills": { + "enabled": true, + "paths": [], + "agentDir": "~/.codex", + }, + "subagents": { + "enabled": false, + "providers": [], + }, + "logging": { + "level": "info", + "format": "json", + "requests": true, + "assets": false, + "toolCalls": true, + "shellCommands": false, + }, + "oauth": { + "accessTokenTtlSeconds": 3600, + "refreshTokenTtlSeconds": 2592000, + "scopes": ["devspace"], + "allowedRedirectHosts": ["chatgpt.com", "localhost", "127.0.0.1"], + }, } ``` -## Skills +Omitted sections and keys use the defaults shown above. An empty +`workspaces.allowedRoots` uses the current working directory. Unknown keys are +rejected so spelling mistakes cannot silently alter behavior. -| Variable | Purpose | -| --- | --- | -| `DEVSPACE_SKILLS` | Set to `0` to hide skills. Enabled by default. | -| `DEVSPACE_SUBAGENTS` | Optional master override for the persisted Subagents configuration. | -| `DEVSPACE_AGENT_DIR` | Defaults to `~/.codex`; its `skills` child is loaded for compatibility. | -| `DEVSPACE_SKILL_PATHS` | Optional comma-separated additional skill directories. | +## Tool modes and UI -DevSpace discovers standard Agent Skills from: +`tools.mode` accepts two values: -- `~/.agents/skills` -- project `.agents/skills` -- `~/.devspace/skills` +| Value | Tool surface | +| --- | --- | +| `codex` | Default. `open_workspace`, `read`, `apply_patch`, `exec_command`, `write_stdin`, and `show_changes`. | +| `claude` | `open_workspace`, `read`, `write`, `edit`, `bash`, and `show_changes`. | -It also keeps compatibility with: +The dedicated MCP tools `grep`, `glob`, and `ls` are not exposed. Each mode uses +its shell tool with programs such as `rg`, `find`, and `ls` when it needs those +operations. -- the bundled `subagents` skill when Subagents are enabled, unless `~/.devspace/skills/subagents/SKILL.md` exists -- `DEVSPACE_AGENT_DIR/skills`, defaulting to `~/.codex/skills` -- additional paths from `DEVSPACE_SKILL_PATHS` +DevSpace attaches Apps UI metadata only to `open_workspace` and `show_changes`. +This avoids rendering an iframe for every read, edit, search, or command call. +Setting `ui.enabled` to `false` removes the metadata but does not remove the +`show_changes` tool. -When Subagents are enabled, DevSpace discovers agent profiles -from: +## Skills and subagents -- `~/.devspace/agents/*.md` -- project `.devspace/agents/*.md` +DevSpace discovers standard Agent Skills from `~/.agents/skills`, project +`.agents/skills`, and `~/.devspace/skills`. It also checks +`skills.agentDir/skills` and each path in `skills.paths`. Relative custom paths +are resolved from the active workspace. -Enable providers and set their defaults in `~/.devspace/config.json`: +Subagent providers are explicit. Omitted providers are disabled: -```json +```jsonc { + "configVersion": 1, "subagents": { "enabled": true, "providers": [ @@ -173,101 +114,104 @@ Enable providers and set their defaults in `~/.devspace/config.json`: "id": "codex", "enabled": true, "model": "gpt-5.4", - "effort": "high" + "effort": "high", }, { "id": "claude", "enabled": true, - "model": "sonnet" + "model": "sonnet", }, - { - "id": "grok", - "enabled": true, - "model": "grok-4.5", - "effort": "low" - } - ] - } + ], + }, } ``` -Each entry controls one provider. Providers omitted from the array are disabled. -`model` and `effort` are optional defaults; an invocation override wins over a -profile value, which wins over the provider default. The legacy boolean -`"subagents": true` remains readable and enables every provider, but new -configuration should use the explicit object form. - -`devspace agents targets` shows usable providers and profiles for the current -workspace. Add `--json` for a compact list of exact target names and their -selection metadata. Disabled, unavailable, and unconfigured providers are -omitted. Provider availability is runtime state and never rewrites the -configuration. - -Grok Build is discovered from the `grok` executable. Authenticate it with -`grok login` or `XAI_API_KEY`; DevSpace does not read or store Grok credentials. -Grok supports `grok-build` by default and validates explicit model and effort -values against the ACP session metadata when available. Set `GROK_COMMAND` when -the executable is not on the normal PATH. If your Grok installation selects a -custom agent profile, set `GROK_AGENT_PROFILE` to that profile's path; DevSpace -passes it to `grok agent stdio` without writing to Grok's configuration. - -`open_workspace` returns a compact catalog containing profile names, -descriptions, providers, and optional models/effort levels so the host model can choose an -agent without reading provider-specific launch details. Disabled or unavailable -providers and their profiles are omitted from this model-facing catalog. `devspace agents ls` -lists existing subagent sessions for the current workspace, scoped by the -workspace environment injected into shell commands. The `subagents` -skill teaches the model to use only the minimal `devspace agents ls`, -`devspace agents targets`, `devspace agents run`, `devspace agents continue`, -and `devspace agents show` workflow. - -For Codex, Claude Code, OpenCode, Pi, or another supported Coding Agent, use -the Skills CLI to install the same skill. DevSpace setup prints this command but -does not run it or write into agent skill directories: - -```bash -npx skills add Waishnav/devspace --skill subagents --global -``` +Profiles are loaded from `~/.devspace/agents/*.md` and project +`.devspace/agents/*.md`. `devspace agents targets` prints the configured targets +available in the current workspace. -Starter profile templates are available under `examples/agents/`. Copy or adapt -them into one of the active profile directories before use. +Provider executable discovery remains process-scoped. The supported overrides +are `CODEX_COMMAND`, `CODEX_HOME`, `CLAUDE_COMMAND`, `CURSOR_COMMAND`, +`COPILOT_COMMAND`, `GROK_COMMAND`, and `GROK_AGENT_PROFILE`. DevSpace does not +persist provider credentials. -Legacy project paths such as `.pi/skills` can be added through `DEVSPACE_SKILL_PATHS` when needed. +## Native artifact download -Example: +Set `artifacts.enabled` to `true` when a host needs to save a native attached or +generated file into an open workspace. `artifacts.maxFileBytes` limits one +streamed file. The secure publication path is currently available only on +Linux; the tool is not registered on macOS, Windows, or BSD. -```bash -DEVSPACE_SKILL_PATHS="$HOME/.claude/skills,$HOME/company/skills" \ -npx @waishnav/devspace serve -``` +## Environment boundary -## Logging +Only two user-facing DevSpace environment variables remain: -| Variable | Default | +| Variable | Purpose | | --- | --- | -| `DEVSPACE_LOG_LEVEL` | `info` | -| `DEVSPACE_LOG_FORMAT` | `json` | -| `DEVSPACE_LOG_REQUESTS` | `1` | -| `DEVSPACE_LOG_ASSETS` | `0` | -| `DEVSPACE_LOG_TOOL_CALLS` | `1` | -| `DEVSPACE_LOG_SHELL_COMMANDS` | `0` | -| `DEVSPACE_TRUST_PROXY` | `0` | - -Set `DEVSPACE_LOG_FORMAT=pretty` for local debugging. - -Set `DEVSPACE_LOG_SHELL_COMMANDS=1` only when you intentionally want command -previews in logs. - -## Env-Only Example - -```bash -DEVSPACE_OAUTH_OWNER_TOKEN="$(openssl rand -base64 32)" \ -DEVSPACE_ALLOWED_ROOTS="$HOME/personal,$HOME/work" \ -DEVSPACE_PUBLIC_BASE_URL="https://devspace.example.com" \ -DEVSPACE_WORKTREE_ROOT="$HOME/.devspace/worktrees" \ -DEVSPACE_ARTIFACTS="1" \ -npx @waishnav/devspace serve -``` +| `DEVSPACE_CONFIG_DIR` | Bootstrap location for `config.jsonc`, `auth.json`, skills, and profiles. | +| `DEVSPACE_OAUTH_OWNER_TOKEN` | Optional secret override for the owner token stored in `auth.json`. | -The environment assignments must be part of the same command invocation, or -exported first. +Durable environment settings were removed in v1.1. Move existing deployment +values to these JSONC keys: + +| Removed setting | JSONC key | +| --- | --- | +| `HOST`, `PORT` | `server.host`, `server.port` | +| `DEVSPACE_PUBLIC_BASE_URL` | `server.publicBaseUrl` | +| `DEVSPACE_ALLOWED_HOSTS` | `server.allowedHosts` | +| `DEVSPACE_TRUST_PROXY` | `server.trustProxy` | +| `DEVSPACE_ALLOWED_ROOTS` | `workspaces.allowedRoots` | +| `DEVSPACE_WORKTREE_ROOT` | `workspaces.worktreeRoot` | +| `DEVSPACE_STATE_DIR` | `storage.stateDir` | +| `DEVSPACE_TOOL_MODE`, `DEVSPACE_MINIMAL_TOOLS` | `tools.mode` | +| `DEVSPACE_WIDGETS` | `ui.enabled` | +| `DEVSPACE_ARTIFACTS` | `artifacts.enabled` | +| `DEVSPACE_ARTIFACT_MAX_FILE_BYTES` | `artifacts.maxFileBytes` | +| `DEVSPACE_SKILLS` | `skills.enabled` | +| `DEVSPACE_SKILL_PATHS` | `skills.paths` | +| `DEVSPACE_AGENT_DIR` | `skills.agentDir` | +| `DEVSPACE_SUBAGENTS` | `subagents.enabled` | +| `DEVSPACE_LOG_LEVEL` | `logging.level` | +| `DEVSPACE_LOG_FORMAT` | `logging.format` | +| `DEVSPACE_LOG_REQUESTS` | `logging.requests` | +| `DEVSPACE_LOG_ASSETS` | `logging.assets` | +| `DEVSPACE_LOG_TOOL_CALLS` | `logging.toolCalls` | +| `DEVSPACE_LOG_SHELL_COMMANDS` | `logging.shellCommands` | +| `DEVSPACE_OAUTH_ACCESS_TOKEN_TTL_SECONDS` | `oauth.accessTokenTtlSeconds` | +| `DEVSPACE_OAUTH_REFRESH_TOKEN_TTL_SECONDS` | `oauth.refreshTokenTtlSeconds` | +| `DEVSPACE_OAUTH_SCOPES` | `oauth.scopes` | +| `DEVSPACE_OAUTH_ALLOWED_REDIRECT_HOSTS` | `oauth.allowedRedirectHosts` | + +These environment values are not read or auto-imported in v1.1. Environment is +process state, so there is no reliable file DevSpace can migrate on the user's +behalf. + +## v1.0 file migration + +The first v1.1 load performs one migration when `config.jsonc` is missing and +`config.json` exists: + +1. Validate the old JSON document. +2. Translate its known fields into the versioned JSONC structure. +3. Write and validate a temporary `config.jsonc`. +4. Atomically publish it. +5. Rename the old file to `config.json.v1.0.bak`. + +If `config.jsonc` exists, DevSpace never reads `config.json`. Invalid JSONC also +never falls back to the old file. Unsupported legacy keys stop migration with an +actionable error instead of being silently discarded. + +The persisted fields map as follows: + +| v1.0 JSON field | v1.1 JSONC key | +| --- | --- | +| `host`, `port` | `server.host`, `server.port` | +| `publicBaseUrl`, `allowedHosts` | `server.publicBaseUrl`, `server.allowedHosts` | +| `allowedRoots`, `worktreeRoot` | `workspaces.allowedRoots`, `workspaces.worktreeRoot` | +| `stateDir` | `storage.stateDir` | +| `artifactsEnabled`, `artifactMaxFileBytes` | `artifacts.enabled`, `artifacts.maxFileBytes` | +| `agentDir` | `skills.agentDir` | +| `subagents` | `subagents` | +| `tools.mode`, `ui.enabled` | unchanged nested keys | + +`auth.json` is unchanged. diff --git a/docs/gotchas.md b/docs/gotchas.md index cd5369314..2f6093552 100644 --- a/docs/gotchas.md +++ b/docs/gotchas.md @@ -69,10 +69,10 @@ npx @waishnav/devspace config set publicBaseUrl https://your-tunnel-host.example Temporary tunnels often change URLs between runs. -For a one-off run: +Update the configured URL: ```bash -DEVSPACE_PUBLIC_BASE_URL="https://new-tunnel.example.com" npx @waishnav/devspace serve +npx @waishnav/devspace config set publicBaseUrl https://new-tunnel.example.com ``` For a stable URL: @@ -94,11 +94,8 @@ npx @waishnav/devspace doctor Confirm the public URL hostname appears in allowed hosts. If you changed tunnel URLs, update `publicBaseUrl`. -Use this only for intentional local debugging: - -```bash -DEVSPACE_ALLOWED_HOSTS="*" npx @waishnav/devspace serve -``` +For intentional local debugging only, set `server.allowedHosts` to `["*"]` in +`~/.devspace/config.jsonc`. ## OAuth Redirect Host Rejected @@ -110,11 +107,8 @@ localhost 127.0.0.1 ``` -If another MCP client uses a different redirect host, configure: - -```bash -DEVSPACE_OAUTH_ALLOWED_REDIRECT_HOSTS="chatgpt.com,example.com" npx @waishnav/devspace serve -``` +If another MCP client uses a different redirect host, add it to +`oauth.allowedRedirectHosts` in `~/.devspace/config.jsonc`. ## Owner Password Not Accepted @@ -204,11 +198,8 @@ Confirm Bash is detected. ## Skills Do Not Appear -Skills are enabled by default. Check: - -```bash -DEVSPACE_SKILLS=1 npx @waishnav/devspace serve -``` +Skills are enabled by default. Confirm `skills.enabled` is `true` in +`~/.devspace/config.jsonc`. DevSpace looks in standard Agent Skills locations: @@ -219,8 +210,8 @@ DevSpace looks in standard Agent Skills locations: It also checks compatibility and custom paths: - the bundled `subagents` skill when Subagents are enabled, unless `~/.devspace/skills/subagents/SKILL.md` exists -- `DEVSPACE_AGENT_DIR/skills`, defaulting to `~/.codex/skills` -- additional paths from `DEVSPACE_SKILL_PATHS` +- `skills.agentDir/skills`, defaulting to `~/.codex/skills` +- additional paths from `skills.paths` When Subagents are enabled, DevSpace loads agent profiles from `~/.devspace/agents/*.md` and project `.devspace/agents/*.md`, then exposes a @@ -246,7 +237,7 @@ not copy files into agent skill directories. Packaged agent profile examples under `examples/agents/` are starter templates. Copy or adapt them into one of the active profile directories before use. -Legacy project paths such as `.pi/skills` can be added through `DEVSPACE_SKILL_PATHS` when needed. +Legacy project paths such as `.pi/skills` can be added to `skills.paths` when needed. If a skill appears in `open_workspace`, the model must read that skill's `SKILL.md` before reading other files inside the skill directory. @@ -259,4 +250,4 @@ to avoid one iframe per call. Plain MCP clients may ignore ChatGPT Apps widget metadata and only show text results; `show_changes` remains available there. If both cards are missing in ChatGPT, confirm that `ui.enabled` is not `false` -in `~/.devspace/config.json` and reconnect the MCP server. +in `~/.devspace/config.jsonc` and reconnect the MCP server. diff --git a/docs/security.md b/docs/security.md index d7ec0e1d6..69bbc1303 100644 --- a/docs/security.md +++ b/docs/security.md @@ -51,8 +51,8 @@ DEVSPACE_OAUTH_OWNER_TOKEN="$(openssl rand -base64 32)" ## Public URL And Host Allowlist -DevSpace needs `DEVSPACE_PUBLIC_BASE_URL` so MCP clients can discover OAuth -metadata and connect to the correct resource. +DevSpace needs `server.publicBaseUrl` in `config.jsonc` so MCP clients can +discover OAuth metadata and connect to the correct resource. The value should be the origin only: @@ -60,10 +60,10 @@ The value should be the origin only: https://your-tunnel-host.example.com ``` -Do not include `/mcp` in `DEVSPACE_PUBLIC_BASE_URL`. +Do not include `/mcp` in `server.publicBaseUrl`. By default, DevSpace derives allowed Host headers from the local host and public -URL. Use `DEVSPACE_ALLOWED_HOSTS=*` only for intentional local debugging. +URL. Put `"*"` in `server.allowedHosts` only for intentional local debugging. ## Tunnels @@ -112,7 +112,7 @@ execute transferred content. ## Logs By default, DevSpace logs requests and tool calls. Shell command previews are -disabled unless `DEVSPACE_LOG_SHELL_COMMANDS=1`. +disabled unless `logging.shellCommands` is `true`. Do not enable shell command logging if commands may contain secrets. diff --git a/docs/setup.md b/docs/setup.md index 934b0b8c8..e5e76d8f9 100644 --- a/docs/setup.md +++ b/docs/setup.md @@ -56,7 +56,7 @@ remain limited to the roots configured for ChatGPT. Setup detects supported Coding Agents and asks which ones DevSpace may use. These choices are stored as provider objects under `subagents` in -`~/.devspace/config.json`. +`~/.devspace/config.jsonc`. If you selected Coding Agents, setup prints: @@ -99,13 +99,7 @@ Run: npx @waishnav/devspace serve ``` -If your tunnel URL changes for one run, override it without rewriting config: - -```bash -DEVSPACE_PUBLIC_BASE_URL="https://new-tunnel.example.com" npx @waishnav/devspace serve -``` - -For a stable public URL, persist it: +If your tunnel URL changes, update the persisted value before starting: ```bash npx @waishnav/devspace config set publicBaseUrl https://devspace.example.com @@ -120,7 +114,7 @@ password approval page. Enter the Owner password printed during setup. The default config files are: ```text -~/.devspace/config.json +~/.devspace/config.jsonc ~/.devspace/auth.json ``` diff --git a/src/cli.ts b/src/cli.ts index c745e791e..16cc950df 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -99,6 +99,9 @@ function normalizeCommand(command: string | undefined): Command { async function ensureConfigured(): Promise { const files = loadDevspaceFiles(); + if (files.migratedLegacyConfig) { + console.log(`Migrated ${files.dir}/config.json to ${files.configPath}`); + } if (files.configExists && files.authExists) return; if (process.env.DEVSPACE_OAUTH_OWNER_TOKEN) return; @@ -674,10 +677,6 @@ async function textPrompt(options: TextPromptOptions): Promise { return value || options.defaultValue; } -function isValidPort(value: unknown): value is number { - return Number.isInteger(value) && Number(value) >= 1 && Number(value) <= 65535; -} - function validateRequiredPublicBaseUrl(value: string | undefined): string | undefined { const trimmed = value?.trim() ?? ""; if (!trimmed) return "Enter the public URL from your tunnel or reverse proxy."; From c9ce866c6ed8c2f58baa54e176800e64d3f86f16 Mon Sep 17 00:00:00 2001 From: Waishnav <86405648+Waishnav@users.noreply.github.com> Date: Mon, 24 Aug 2026 01:43:31 +0530 Subject: [PATCH 30/75] fix(config): preserve migration and daemon ownership --- src/cli.ts | 2 +- src/config.test.ts | 1 + src/config.ts | 2 ++ src/local-agent-client.ts | 26 +++++++++++++++++++++----- src/local-agent-daemon.test.ts | 12 +++++++++++- src/user-config.ts | 5 +++++ 6 files changed, 41 insertions(+), 7 deletions(-) diff --git a/src/cli.ts b/src/cli.ts index 16cc950df..f5e3bcb5f 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -100,7 +100,7 @@ function normalizeCommand(command: string | undefined): Command { async function ensureConfigured(): Promise { const files = loadDevspaceFiles(); if (files.migratedLegacyConfig) { - console.log(`Migrated ${files.dir}/config.json to ${files.configPath}`); + console.log(`Migrated legacy configuration to ${files.configPath}`); } if (files.configExists && files.authExists) return; if (process.env.DEVSPACE_OAUTH_OWNER_TOKEN) return; diff --git a/src/config.test.ts b/src/config.test.ts index e353de53c..47a39652a 100644 --- a/src/config.test.ts +++ b/src/config.test.ts @@ -73,6 +73,7 @@ try { writeDevspaceAuth({ ownerToken: "persisted-owner-token-long-enough" }, env); const configured = loadConfig({ DEVSPACE_CONFIG_DIR: configDir }); + assert.equal(configured.configDir, configDir); assert.equal(configured.host, "0.0.0.0"); assert.equal(configured.port, 8787); assert.equal(configured.publicBaseUrl, "https://devspace.example.com"); diff --git a/src/config.ts b/src/config.ts index 496f3bdb8..e53305268 100644 --- a/src/config.ts +++ b/src/config.ts @@ -9,6 +9,7 @@ import type { SubagentsConfig } from "./local-agent-config.js"; export type { ToolMode } from "./config-schema.js"; export interface ServerConfig { + configDir: string; host: string; port: number; oauth: OAuthConfig; @@ -48,6 +49,7 @@ export function loadConfig(env: NodeJS.ProcessEnv = process.env): ServerConfig { ]; return { + configDir: files.dir, host, port, oauth: { diff --git a/src/local-agent-client.ts b/src/local-agent-client.ts index 157826eb5..01f8c1cdc 100644 --- a/src/local-agent-client.ts +++ b/src/local-agent-client.ts @@ -49,6 +49,7 @@ import type { StartLocalAgentInput, } from "./local-agent-manager.js"; import type { LocalAgentRecord, LocalAgentWorkspaceScope } from "./local-agent-store.js"; +import { devspaceConfigDir } from "./user-config.js"; const DEFAULT_STARTUP_TIMEOUT_MS = 8_000; const DEFAULT_REQUEST_TIMEOUT_MS = 30_000; @@ -63,6 +64,7 @@ type RequestError = export interface LocalAgentClientOptions { stateDir: string; + configDir?: string; startupTimeoutMs?: number; requestTimeoutMs?: number; spawnDaemon?: () => void; @@ -84,7 +86,9 @@ export class LocalAgentClient { this.endpoint = options.endpoint ?? this.paths.endpoint; this.startupTimeoutMs = options.startupTimeoutMs ?? DEFAULT_STARTUP_TIMEOUT_MS; this.requestTimeoutMs = options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS; - this.spawnDaemon = options.spawnDaemon ?? (() => spawnLocalAgentDaemon()); + this.spawnDaemon = options.spawnDaemon ?? (() => spawnLocalAgentDaemon( + options.configDir ?? devspaceConfigDir(), + )); } async run( @@ -404,21 +408,33 @@ export class LocalAgentClient { } } -export function createLocalAgentClient(config: Pick): LocalAgentClient { - return new LocalAgentClient({ stateDir: config.stateDir }); +export function createLocalAgentClient( + config: Pick, +): LocalAgentClient { + return new LocalAgentClient({ configDir: config.configDir, stateDir: config.stateDir }); } -export function spawnLocalAgentDaemon(env: NodeJS.ProcessEnv = process.env): void { +export function spawnLocalAgentDaemon( + configDir: string, + env: NodeJS.ProcessEnv = process.env, +): void { const entrypoint = resolveDaemonEntrypoint(); const child = spawn(process.execPath, [...daemonExecArgv(process.execArgv), entrypoint], { detached: true, stdio: "ignore", windowsHide: true, - env, + env: localAgentDaemonEnvironment(configDir, env), }); child.unref(); } +export function localAgentDaemonEnvironment( + configDir: string, + env: NodeJS.ProcessEnv, +): NodeJS.ProcessEnv { + return { ...env, DEVSPACE_CONFIG_DIR: configDir }; +} + export function daemonExecArgv(execArgv: readonly string[]): string[] { const result: string[] = []; for (let index = 0; index < execArgv.length; index += 1) { diff --git a/src/local-agent-daemon.test.ts b/src/local-agent-daemon.test.ts index 3b5abef45..6ea66e652 100644 --- a/src/local-agent-daemon.test.ts +++ b/src/local-agent-daemon.test.ts @@ -5,7 +5,11 @@ import { mkdir, mkdtemp, rm } from "node:fs/promises"; import { createConnection, createServer as createNetServer } from "node:net"; import { join } from "node:path"; import { tmpdir } from "node:os"; -import { daemonExecArgv, LocalAgentClient } from "./local-agent-client.js"; +import { + daemonExecArgv, + localAgentDaemonEnvironment, + LocalAgentClient, +} from "./local-agent-client.js"; import { LocalAgentDaemon, type LocalAgentDaemonManager } from "./local-agent-daemon.js"; import { ensureLocalAgentDaemonSecret, @@ -112,6 +116,12 @@ assert.deepEqual( "detached daemon startup must not inherit inspector flags", ); +assert.deepEqual( + localAgentDaemonEnvironment("/alternate/config", { PATH: "/bin" }), + { PATH: "/bin", DEVSPACE_CONFIG_DIR: "/alternate/config" }, + "the daemon must reload the same persisted configuration as its client", +); + let shutdownSocket: ReturnType | undefined; try { const started = unwrap(await client.run({ diff --git a/src/user-config.ts b/src/user-config.ts index b1524ee01..051fe1f15 100644 --- a/src/user-config.ts +++ b/src/user-config.ts @@ -153,13 +153,18 @@ function migrateLegacyConfigFile( } const temporaryPath = temporaryFilePath(configPath); + let published = false; try { mkdirSync(dirname(configPath), { recursive: true }); writeFileSync(temporaryPath, serializeConfig(migrated), { mode: 0o600, flag: "wx" }); readJsoncConfig(temporaryPath); renameSync(temporaryPath, configPath); + published = true; renameSync(legacyPath, backupPath); } catch (error) { + if (published && existsSync(legacyPath)) { + rmSync(configPath, { force: true }); + } rmSync(temporaryPath, { force: true }); throw fileError("migrate", legacyPath, error); } From 172608302ee123521429d81dbe4adc5450700658 Mon Sep 17 00:00:00 2001 From: Waishnav <86405648+Waishnav@users.noreply.github.com> Date: Mon, 24 Aug 2026 02:11:19 +0530 Subject: [PATCH 31/75] fix(config): make legacy migration race-safe --- src/user-config.test.ts | 68 +++++++++++++++++++++++++++++++++++++++++ src/user-config.ts | 20 ++++++++++-- 2 files changed, 85 insertions(+), 3 deletions(-) diff --git a/src/user-config.test.ts b/src/user-config.test.ts index 9ff9df6e6..01ae8ca9d 100644 --- a/src/user-config.test.ts +++ b/src/user-config.test.ts @@ -1,4 +1,5 @@ import assert from "node:assert/strict"; +import { spawn } from "node:child_process"; import { existsSync, mkdtempSync, @@ -44,6 +45,24 @@ withConfigDir((configDir, env) => { assert.equal(nextLoad.migratedLegacyConfig, false); }); +await withConfigDirAsync(async (configDir) => { + writeFileSync(join(configDir, "config.json"), JSON.stringify({ + port: 8787, + allowedRoots: ["/work"], + })); + + const results = await Promise.all([ + migrateInChildProcess(configDir), + migrateInChildProcess(configDir), + ]); + assert.equal(results.filter((result) => result.migrated).length, 1); + assert.equal(results.filter((result) => !result.migrated).length, 1); + assert.equal(existsSync(join(configDir, "config.json")), false); + assert.equal(existsSync(join(configDir, "config.jsonc")), true); + assert.equal(existsSync(join(configDir, "config.json.v1.0.bak")), true); + assert.equal(loadDevspaceFiles({ DEVSPACE_CONFIG_DIR: configDir }).config.server.port, 8787); +}); + withConfigDir((configDir, env) => { writeFileSync(join(configDir, "config.jsonc"), `{ // This comment must survive config updates. @@ -101,3 +120,52 @@ function withConfigDir( rmSync(configDir, { recursive: true, force: true }); } } + +async function withConfigDirAsync( + test: (configDir: string) => Promise, +): Promise { + const configDir = mkdtempSync(join(tmpdir(), "devspace-user-config-test-")); + try { + await test(configDir); + } finally { + rmSync(configDir, { recursive: true, force: true }); + } +} + +async function migrateInChildProcess( + configDir: string, +): Promise<{ migrated: boolean }> { + const moduleUrl = new URL("./user-config.ts", import.meta.url).href; + const source = [ + `import { loadDevspaceFiles } from ${JSON.stringify(moduleUrl)};`, + "const files = loadDevspaceFiles();", + "process.stdout.write(JSON.stringify({ migrated: files.migratedLegacyConfig }));", + ].join("\n"); + + return new Promise((resolve, reject) => { + const child = spawn( + process.execPath, + ["--import", "tsx", "--input-type=module", "--eval", source], + { + env: { ...process.env, DEVSPACE_CONFIG_DIR: configDir }, + stdio: ["ignore", "pipe", "pipe"], + }, + ); + let stdout = ""; + let stderr = ""; + child.stdout.setEncoding("utf8").on("data", (chunk: string) => { + stdout += chunk; + }); + child.stderr.setEncoding("utf8").on("data", (chunk: string) => { + stderr += chunk; + }); + child.once("error", reject); + child.once("close", (code) => { + if (code !== 0) { + reject(new Error(`migration child exited with ${code}: ${stderr}`)); + return; + } + resolve(JSON.parse(stdout) as { migrated: boolean }); + }); + }); +} diff --git a/src/user-config.ts b/src/user-config.ts index 051fe1f15..7b62b7615 100644 --- a/src/user-config.ts +++ b/src/user-config.ts @@ -1,6 +1,7 @@ import { randomBytes } from "node:crypto"; import { existsSync, + linkSync, mkdirSync, readFileSync, renameSync, @@ -140,7 +141,7 @@ function migrateLegacyConfigFile( legacyPath: string, configPath: string, backupPath: string, -): true { +): boolean { if (existsSync(backupPath)) { throw new Error(`Unable to migrate ${legacyPath}: backup already exists at ${backupPath}`); } @@ -158,15 +159,24 @@ function migrateLegacyConfigFile( mkdirSync(dirname(configPath), { recursive: true }); writeFileSync(temporaryPath, serializeConfig(migrated), { mode: 0o600, flag: "wx" }); readJsoncConfig(temporaryPath); - renameSync(temporaryPath, configPath); + try { + // A hard link publishes the complete temporary file atomically without + // replacing config.jsonc if another first-start process won the race. + linkSync(temporaryPath, configPath); + } catch (error) { + if (!isErrnoException(error) || error.code !== "EEXIST") throw error; + readJsoncConfig(configPath); + return false; + } published = true; renameSync(legacyPath, backupPath); } catch (error) { if (published && existsSync(legacyPath)) { rmSync(configPath, { force: true }); } - rmSync(temporaryPath, { force: true }); throw fileError("migrate", legacyPath, error); + } finally { + rmSync(temporaryPath, { force: true }); } return true; } @@ -236,4 +246,8 @@ function fileError(action: "read" | "migrate", filePath: string, error: unknown) return new DevspaceConfigFileError(`Unable to ${action} ${filePath}: ${reason}`); } +function isErrnoException(error: unknown): error is NodeJS.ErrnoException { + return error instanceof Error && "code" in error; +} + class DevspaceConfigFileError extends Error {} From 8cf4fcb9692d2717d5cb5c8ae3d8d3b18356b2aa Mon Sep 17 00:00:00 2001 From: Waishnav <86405648+Waishnav@users.noreply.github.com> Date: Mon, 24 Aug 2026 02:11:48 +0530 Subject: [PATCH 32/75] fix(ci): normalize schema line endings --- src/config-schema.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config-schema.test.ts b/src/config-schema.test.ts index f76c9ac60..129224df7 100644 --- a/src/config-schema.test.ts +++ b/src/config-schema.test.ts @@ -21,7 +21,7 @@ const generatedSchema = `${JSON.stringify(devspaceConfigJsonSchema(), null, 2)}\ const committedSchema = readFileSync( resolve("schema/v1/devspace.schema.json"), "utf8", -); +).replace(/\r\n/g, "\n"); assert.equal(committedSchema, generatedSchema, "run `npm run schema:config` after changing config-schema.ts"); console.log("config schema tests passed"); From d1ae28673b5033458d4d185cf9cf0b541222e6c9 Mon Sep 17 00:00:00 2001 From: Waishnav <86405648+Waishnav@users.noreply.github.com> Date: Mon, 24 Aug 2026 02:28:19 +0530 Subject: [PATCH 33/75] fix(config): preserve tunnel URL during init --- src/cli.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/cli.ts b/src/cli.ts index f5e3bcb5f..361c28a47 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -225,9 +225,8 @@ async function runInit({ force }: { force: boolean }): Promise { ...files.config, server: { ...files.config.server, - host: files.config.server.host, port, - publicBaseUrl, + ...(useChatGpt ? { publicBaseUrl } : {}), }, workspaces: { ...files.config.workspaces, From b22412932dea396151f99e8a58a5e46a7b302ffe Mon Sep 17 00:00:00 2001 From: Waishnav <86405648+Waishnav@users.noreply.github.com> Date: Mon, 24 Aug 2026 02:28:40 +0530 Subject: [PATCH 34/75] fix(config): explain migration conflict recovery --- src/user-config.test.ts | 14 ++++++++++++++ src/user-config.ts | 5 ++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/user-config.test.ts b/src/user-config.test.ts index 01ae8ca9d..5f5fe477b 100644 --- a/src/user-config.test.ts +++ b/src/user-config.test.ts @@ -107,6 +107,20 @@ withConfigDir((configDir, env) => { assert.equal(existsSync(join(configDir, "config.json.v1.0.bak")), false); }); +withConfigDir((configDir, env) => { + const legacyPath = join(configDir, "config.json"); + const backupPath = join(configDir, "config.json.v1.0.bak"); + writeFileSync(legacyPath, JSON.stringify({ port: 8787 })); + writeFileSync(backupPath, JSON.stringify({ port: 7676 })); + + assert.throws( + () => loadDevspaceFiles(env), + (error: unknown) => error instanceof Error + && error.message.includes(`backup already exists at ${backupPath}`) + && error.message.includes(`Move ${backupPath} out of the way, then run DevSpace again.`), + ); +}); + console.log("user config tests passed"); function withConfigDir( diff --git a/src/user-config.ts b/src/user-config.ts index 7b62b7615..c6f7bc1f2 100644 --- a/src/user-config.ts +++ b/src/user-config.ts @@ -143,7 +143,10 @@ function migrateLegacyConfigFile( backupPath: string, ): boolean { if (existsSync(backupPath)) { - throw new Error(`Unable to migrate ${legacyPath}: backup already exists at ${backupPath}`); + throw new Error( + `Unable to migrate ${legacyPath}: backup already exists at ${backupPath}. ` + + `Move ${backupPath} out of the way, then run DevSpace again.`, + ); } let migrated: DevspaceConfig; From d5ef7192eddc817eac0fa288afa3f32c35d22356 Mon Sep 17 00:00:00 2001 From: Waishnav <86405648+Waishnav@users.noreply.github.com> Date: Mon, 24 Aug 2026 02:29:23 +0530 Subject: [PATCH 35/75] fix(config): preserve comments during init --- src/cli.ts | 27 +++++++++++---------------- src/user-config.test.ts | 10 ++++++++++ src/user-config.ts | 21 ++++++++++++++++++--- 3 files changed, 39 insertions(+), 19 deletions(-) diff --git a/src/cli.ts b/src/cli.ts index 361c28a47..922481516 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -46,9 +46,8 @@ import { generateOwnerToken, loadDevspaceFiles, setDevspaceConfigValue, + setDevspaceConfigValues, writeDevspaceAuth, - writeDevspaceConfig, - type DevspaceUserConfig, } from "./user-config.js"; import { expandHomePath } from "./roots.js"; import { shutdownHttpServer } from "./server-shutdown.js"; @@ -221,24 +220,20 @@ async function runInit({ force }: { force: boolean }): Promise { selectedProviders, ); - const config: DevspaceUserConfig = { - ...files.config, - server: { - ...files.config.server, - port, - ...(useChatGpt ? { publicBaseUrl } : {}), - }, - workspaces: { - ...files.config.workspaces, - ...(allowedRoots ? { allowedRoots } : {}), - }, - subagents, - }; const auth = { ownerToken: files.auth.ownerToken ?? generateOwnerToken(), }; - writeDevspaceConfig(config); + setDevspaceConfigValues([ + { path: ["server", "port"], value: port }, + ...(useChatGpt + ? [{ path: ["server", "publicBaseUrl"], value: publicBaseUrl }] + : []), + ...(allowedRoots + ? [{ path: ["workspaces", "allowedRoots"], value: allowedRoots }] + : []), + { path: ["subagents"], value: subagents }, + ]); writeDevspaceAuth(auth); const lines = [ diff --git a/src/user-config.test.ts b/src/user-config.test.ts index 5f5fe477b..8e09b9122 100644 --- a/src/user-config.test.ts +++ b/src/user-config.test.ts @@ -12,6 +12,7 @@ import { join } from "node:path"; import { loadDevspaceFiles, setDevspaceConfigValue, + setDevspaceConfigValues, } from "./user-config.js"; withConfigDir((configDir, env) => { @@ -80,6 +81,15 @@ withConfigDir((configDir, env) => { const updated = readFileSync(join(configDir, "config.jsonc"), "utf8"); assert.match(updated, /This comment must survive config updates/); assert.equal(loadDevspaceFiles(env).config.server.publicBaseUrl, "https://new.example.com"); + + setDevspaceConfigValues([ + { path: ["server", "port"], value: 7676 }, + { path: ["tools", "mode"], value: "claude" }, + ], env); + const multiUpdated = readFileSync(join(configDir, "config.jsonc"), "utf8"); + assert.match(multiUpdated, /This comment must survive config updates/); + assert.equal(loadDevspaceFiles(env).config.server.port, 7676); + assert.equal(loadDevspaceFiles(env).config.tools.mode, "claude"); }); withConfigDir((configDir, env) => { diff --git a/src/user-config.ts b/src/user-config.ts index c6f7bc1f2..6775e4b7c 100644 --- a/src/user-config.ts +++ b/src/user-config.ts @@ -45,6 +45,11 @@ export interface DevspaceFiles { migratedLegacyConfig: boolean; } +export interface DevspaceConfigEdit { + path: (string | number)[]; + value: unknown; +} + export function devspaceConfigDir(env: NodeJS.ProcessEnv = process.env): string { return resolve(expandHomePath(env.DEVSPACE_CONFIG_DIR ?? join(homedir(), ".devspace"))); } @@ -110,14 +115,24 @@ export function setDevspaceConfigValue( path: (string | number)[], value: unknown, env: NodeJS.ProcessEnv = process.env, +): string { + return setDevspaceConfigValues([{ path, value }], env); +} + +export function setDevspaceConfigValues( + edits: DevspaceConfigEdit[], + env: NodeJS.ProcessEnv = process.env, ): string { const files = loadDevspaceFiles(env); const source = files.configExists ? readFileSync(files.configPath, "utf8") : serializeConfig(files.config); - const updated = applyEdits(source, modify(source, path, value, { - formattingOptions: { insertSpaces: true, tabSize: 2, eol: "\n" }, - })); + const updated = edits.reduce( + (document, edit) => applyEdits(document, modify(document, edit.path, edit.value, { + formattingOptions: { insertSpaces: true, tabSize: 2, eol: "\n" }, + })), + source, + ); parseJsoncConfig(updated, files.configPath); atomicWrite(files.configPath, updated.endsWith("\n") ? updated : `${updated}\n`, 0o600); return files.configPath; From 3f4ff9e59bbf003d02827dca8964878c28dc582b Mon Sep 17 00:00:00 2001 From: Waishnav <86405648+Waishnav@users.noreply.github.com> Date: Mon, 24 Aug 2026 02:29:44 +0530 Subject: [PATCH 36/75] fix(config): anchor schema paths to modules --- scripts/generate-config-schema.ts | 5 ++--- src/config-schema.test.ts | 3 +-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/scripts/generate-config-schema.ts b/scripts/generate-config-schema.ts index 2e56f4a8e..324b611b6 100644 --- a/scripts/generate-config-schema.ts +++ b/scripts/generate-config-schema.ts @@ -1,10 +1,9 @@ import { mkdirSync, writeFileSync } from "node:fs"; -import { dirname, resolve } from "node:path"; import { devspaceConfigJsonSchema, } from "../src/config-schema.js"; -const outputPath = resolve("schema/v1/devspace.schema.json"); +const outputPath = new URL("../schema/v1/devspace.schema.json", import.meta.url); -mkdirSync(dirname(outputPath), { recursive: true }); +mkdirSync(new URL(".", outputPath), { recursive: true }); writeFileSync(outputPath, `${JSON.stringify(devspaceConfigJsonSchema(), null, 2)}\n`); diff --git a/src/config-schema.test.ts b/src/config-schema.test.ts index 129224df7..78438f63c 100644 --- a/src/config-schema.test.ts +++ b/src/config-schema.test.ts @@ -1,6 +1,5 @@ import assert from "node:assert/strict"; import { readFileSync } from "node:fs"; -import { resolve } from "node:path"; import { defaultDevspaceConfig, devspaceConfigJsonSchema, @@ -19,7 +18,7 @@ assert.throws( const generatedSchema = `${JSON.stringify(devspaceConfigJsonSchema(), null, 2)}\n`; const committedSchema = readFileSync( - resolve("schema/v1/devspace.schema.json"), + new URL("../schema/v1/devspace.schema.json", import.meta.url), "utf8", ).replace(/\r\n/g, "\n"); assert.equal(committedSchema, generatedSchema, "run `npm run schema:config` after changing config-schema.ts"); From 9b98cc7767df49e6791ff7d3c2b86819f211e955 Mon Sep 17 00:00:00 2001 From: Waishnav <86405648+Waishnav@users.noreply.github.com> Date: Tue, 25 Aug 2026 21:50:35 +0530 Subject: [PATCH 37/75] fix(review): preserve historical change snapshots --- src/review-checkpoints.test.ts | 50 ++++++++++++ src/review-checkpoints.ts | 144 +++++++++++++++++++++++++++++---- 2 files changed, 177 insertions(+), 17 deletions(-) diff --git a/src/review-checkpoints.test.ts b/src/review-checkpoints.test.ts index 499cdb9b2..6e9d09636 100644 --- a/src/review-checkpoints.test.ts +++ b/src/review-checkpoints.test.ts @@ -62,12 +62,58 @@ test("show_changes reports and advances the last-shown checkpoint", async (t) => markReviewed: true, }); assert.equal(markedReviewed.summary.files, 2); + assert.match(markedReviewed.reviewRef, /^[0-9a-f]{40,64}$/); + + const restored = await manager.reviewByRef({ + workspaceId: "ws_incremental", + root, + reviewRef: markedReviewed.reviewRef, + }); + assert.deepEqual(restored.summary, markedReviewed.summary); + assert.deepEqual(restored.files, markedReviewed.files); + assert.equal(restored.patch, markedReviewed.patch); const afterReviewed = await manager.reviewChanges({ workspaceId: "ws_incremental", root }); assert.equal(afterReviewed.summary.files, 0); assert.equal(afterReviewed.patch, ""); }); +test("historical review refs survive later reviews and manager restarts", async (t) => { + const root = await committedRepository(t); + const manager = createReviewCheckpointManager(); + await manager.initializeWorkspace({ workspaceId: "ws_history", root }); + + await writeFile(join(root, "README.md"), "hello\nfirst\n"); + const first = await manager.reviewChanges({ workspaceId: "ws_history", root }); + + await writeFile(join(root, "README.md"), "hello\nfirst\nsecond\n"); + const second = await manager.reviewChanges({ workspaceId: "ws_history", root }); + assert.notEqual(first.reviewRef, second.reviewRef); + + const restarted = createReviewCheckpointManager(); + const restoredFirst = await restarted.reviewByRef({ + workspaceId: "ws_history", + root, + reviewRef: first.reviewRef, + }); + assert.deepEqual(restoredFirst.summary, first.summary); + assert.equal(restoredFirst.patch, first.patch); + assert.match(restoredFirst.patch, /\+first/); + assert.doesNotMatch(restoredFirst.patch, /\+second/); +}); + +test("review refs are scoped to the workspace review history", async (t) => { + const root = await committedRepository(t); + const manager = createReviewCheckpointManager(); + await manager.initializeWorkspace({ workspaceId: "ws_scoped", root }); + + const head = await gitOutput(root, ["rev-parse", "HEAD"]); + await assert.rejects( + () => manager.reviewByRef({ workspaceId: "ws_scoped", root, reviewRef: head }), + /Unknown review reference/, + ); +}); + test("review checkpoints survive a manager restart", async (t) => { const root = await committedRepository(t); const manager = createReviewCheckpointManager(); @@ -246,3 +292,7 @@ async function deleteReviewRef( async function git(cwd: string, args: string[]): Promise { await execFileAsync("git", args, { cwd }); } + +async function gitOutput(cwd: string, args: string[]): Promise { + return (await execFileAsync("git", args, { cwd })).stdout.trim(); +} diff --git a/src/review-checkpoints.ts b/src/review-checkpoints.ts index 21a0d6608..037ca8c51 100644 --- a/src/review-checkpoints.ts +++ b/src/review-checkpoints.ts @@ -20,6 +20,7 @@ export interface ReviewFile { } export interface ReviewChangesResult { + reviewRef: string; result: string; summary: ReviewSummary; files: ReviewFile[]; @@ -48,6 +49,11 @@ export interface ReviewCheckpointManager { since?: ReviewSince; markReviewed?: boolean; }): Promise; + reviewByRef(input: { + workspaceId: string; + root: string; + reviewRef: string; + }): Promise; } const REVIEW_REF_PREFIX = "refs/devspace/review"; @@ -113,15 +119,8 @@ export function createReviewCheckpointManager(): ReviewCheckpointManager { const baselineRef = effectiveSince === "workspace_open" ? state.openRef : state.baselineRef; const baseline = (await git(state.gitRoot, ["rev-parse", "--verify", `${baselineRef}^{commit}`])).stdout.trim(); - const current = await createWorkingTreeSnapshot(state.gitRoot); - const patch = (await git(state.gitRoot, ["diff", "--binary", "--no-color", baseline, current], { - maxBuffer: 50 * 1024 * 1024, - })).stdout; - const numstat = (await git(state.gitRoot, ["diff", "--numstat", "-z", baseline, current], { - maxBuffer: 50 * 1024 * 1024, - })).stdout; - const files = parseNumstat(numstat); - const summary = summarizeFiles(files); + const current = await createWorkingTreeSnapshot(state.gitRoot, baseline); + const review = await readReviewBetween(state.gitRoot, baseline, current); if (markReviewed) { await git(state.gitRoot, ["update-ref", state.baselineRef, current]); @@ -132,19 +131,66 @@ export function createReviewCheckpointManager(): ReviewCheckpointManager { ? ` The last-shown checkpoint was missing, so changes were compared from workspace open${markReviewed ? " and the baseline was re-established" : ""}.` : ""; return { + reviewRef: current, result: `${ - summary.files === 0 + review.summary.files === 0 ? `No changes since ${effectiveSince === "workspace_open" ? "workspace open" : "last shown changes"}.` - : `Changed ${summary.files} ${summary.files === 1 ? "file" : "files"} (+${summary.additions} -${summary.removals}).` + : formatChangedFiles(review.summary) }${fallbackNote}`, - summary, - files, - patch, + ...review, }; }, + + async reviewByRef({ workspaceId, root, reviewRef }) { + let state = states.get(workspaceId); + assertWorkspaceRoot(state, workspaceId, root); + if (!isReadyState(state)) { + await this.initializeWorkspace({ workspaceId, root }); + state = states.get(workspaceId); + } + assertWorkspaceRoot(state, workspaceId, root); + + if (!state?.gitRoot) { + throw new Error(state?.diagnostic ?? "show_changes requires a Git workspace in this version."); + } + + const [openCommit, baselineCommit, reviewCommit] = await Promise.all([ + commitForRef(state.gitRoot, state.openRef), + commitForRef(state.gitRoot, state.baselineRef), + resolveReviewCommitOrUndefined(state.gitRoot, reviewRef), + ]); + if ( + !openCommit + || !baselineCommit + || !reviewCommit + || reviewCommit === openCommit + ) { + throw new Error(`Unknown review reference for workspace ${workspaceId}: ${reviewRef}`); + } + + const [isAfterOpen, isBeforeBaseline] = await Promise.all([ + isAncestor(state.gitRoot, openCommit, reviewCommit), + isAncestor(state.gitRoot, reviewCommit, baselineCommit), + ]); + if (!isAfterOpen || !isBeforeBaseline) { + throw new Error(`Unknown review reference for workspace ${workspaceId}: ${reviewRef}`); + } + + return readReviewCommit(state.gitRoot, reviewCommit); + }, }; } +export async function readReviewRef(root: string, reviewRef: string): Promise { + const eligibility = await getGitEligibility(root); + if (!eligibility.ok || !eligibility.gitRoot) { + throw new Error(eligibility.message ?? "show-changes requires a Git workspace."); + } + + const commit = await resolveReviewCommit(eligibility.gitRoot, reviewRef); + return readReviewCommit(eligibility.gitRoot, commit); +} + function assertWorkspaceRoot( state: WorkspaceReviewState | undefined, workspaceId: string, @@ -181,7 +227,8 @@ async function initializeWorkspaceState( ]); if (!openCommit && !baselineCommit) { - const initialCommit = await createWorkingTreeSnapshot(eligibility.gitRoot); + const head = (await git(eligibility.gitRoot, ["rev-parse", "--verify", "HEAD^{commit}"])).stdout.trim(); + const initialCommit = await createWorkingTreeSnapshot(eligibility.gitRoot, head); await git(eligibility.gitRoot, ["update-ref", state.openRef, initialCommit]); await git(eligibility.gitRoot, ["update-ref", state.baselineRef, initialCommit]); state.openRefAvailable = true; @@ -230,7 +277,7 @@ function reviewRefs( }; } -async function createWorkingTreeSnapshot(gitRoot: string): Promise { +async function createWorkingTreeSnapshot(gitRoot: string, parent: string): Promise { const tempDir = await mkdtemp(join(tmpdir(), "devspace-review-index-")); const indexPath = join(tempDir, "index"); const env = checkpointEnv(indexPath); @@ -239,13 +286,76 @@ async function createWorkingTreeSnapshot(gitRoot: string): Promise { await git(gitRoot, ["read-tree", "HEAD"], { env }); await git(gitRoot, ["add", "-A", "--", "."], { env }); const tree = (await git(gitRoot, ["write-tree"], { env })).stdout.trim(); - const parent = (await git(gitRoot, ["rev-parse", "--verify", "HEAD^{commit}"])).stdout.trim(); return (await git(gitRoot, ["commit-tree", tree, "-p", parent, "-m", "DevSpace review snapshot"], { env })).stdout.trim(); } finally { await rm(tempDir, { recursive: true, force: true }); } } +async function readReviewCommit(gitRoot: string, reviewRef: string): Promise { + const parent = (await git(gitRoot, ["rev-parse", "--verify", `${reviewRef}^1`])).stdout.trim(); + const review = await readReviewBetween(gitRoot, parent, reviewRef); + return { + reviewRef, + result: review.summary.files === 0 ? "No changes in this review." : formatChangedFiles(review.summary), + ...review, + }; +} + +async function readReviewBetween( + gitRoot: string, + before: string, + after: string, +): Promise> { + const patch = (await git(gitRoot, ["diff", "--binary", "--no-color", before, after], { + maxBuffer: 50 * 1024 * 1024, + })).stdout; + const numstat = (await git(gitRoot, ["diff", "--numstat", "-z", before, after], { + maxBuffer: 50 * 1024 * 1024, + })).stdout; + const files = parseNumstat(numstat); + return { + summary: summarizeFiles(files), + files, + patch, + }; +} + +async function resolveReviewCommit(gitRoot: string, reviewRef: string): Promise { + if (!isReviewRef(reviewRef)) { + throw new Error(`Invalid review reference: ${reviewRef}`); + } + return (await git(gitRoot, ["rev-parse", "--verify", `${reviewRef}^{commit}`])).stdout.trim(); +} + +async function resolveReviewCommitOrUndefined( + gitRoot: string, + reviewRef: string, +): Promise { + try { + return await resolveReviewCommit(gitRoot, reviewRef); + } catch { + return undefined; + } +} + +async function isAncestor(gitRoot: string, ancestor: string, descendant: string): Promise { + try { + await git(gitRoot, ["merge-base", "--is-ancestor", ancestor, descendant]); + return true; + } catch { + return false; + } +} + +function isReviewRef(value: string): boolean { + return /^[0-9a-f]{40,64}$/.test(value); +} + +function formatChangedFiles(summary: ReviewSummary): string { + return `Changed ${summary.files} ${summary.files === 1 ? "file" : "files"} (+${summary.additions} -${summary.removals}).`; +} + function checkpointEnv(indexPath: string): NodeJS.ProcessEnv { return { GIT_INDEX_FILE: indexPath, From e70c2b1585212045c4c962cdc4a247776baf5686 Mon Sep 17 00:00:00 2001 From: Waishnav <86405648+Waishnav@users.noreply.github.com> Date: Tue, 25 Aug 2026 21:50:35 +0530 Subject: [PATCH 38/75] fix(ui): restore cards from durable tool output --- package.json | 2 +- src/server.test.ts | 70 +++++++++-- src/server.ts | 47 +++---- src/ui/card-types.test.ts | 7 -- src/ui/card-types.ts | 8 -- src/ui/tool-result.test.ts | 137 ++++++++++++++++++++ src/ui/tool-result.ts | 250 +++++++++++++++++++++++++++++++++++++ src/ui/vite-env.d.ts | 7 ++ src/ui/workspace-app.tsx | 160 +++++++++++++++++------- 9 files changed, 593 insertions(+), 95 deletions(-) create mode 100644 src/ui/tool-result.test.ts create mode 100644 src/ui/tool-result.ts diff --git a/package.json b/package.json index dfa5cf557..6d60fee7b 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "postinstall": "node scripts/fix-node-pty-permissions.mjs", "schema:config": "tsx scripts/generate-config-schema.ts", "start": "node dist/cli.js serve", - "test": "tsx src/user-config.test.ts && tsx src/config.test.ts && tsx src/onboarding.test.ts && tsx src/cli-workspace.test.ts && tsx src/request-meta.test.ts && tsx src/incoming-artifacts.test.ts && tsx src/artifact-download.test.ts && tsx src/ui/card-types.test.ts && tsx src/ui/patch-display.test.ts && tsx src/apply-patch.test.ts && tsx src/process-platform.test.ts && tsx src/process-sessions.test.ts && tsx src/mcp-sessions.test.ts && tsx src/server-shutdown.test.ts && tsx src/local-agent-config.test.ts && tsx src/local-agent-catalog.test.ts && tsx src/local-agent-presentation.test.ts && tsx src/local-agent-runtime.test.ts && tsx src/local-agent-daemon-lifecycle.test.ts && tsx src/local-agent-daemon-protocol.test.ts && tsx src/local-agent-daemon.test.ts && tsx src/local-agent-codex.test.ts && tsx src/local-agent-opencode.test.ts && tsx src/local-agent-acp.test.ts && tsx src/local-agent-grok.test.ts && tsx src/local-agent-pi-sandbox.test.ts && tsx src/local-agent-pi.test.ts && tsx src/local-agent-claude.test.ts && tsx src/local-agent-adapters.test.ts && tsx src/local-agent-availability.test.ts && tsx src/local-agent-profiles.test.ts && tsx src/local-agent-targets.test.ts && tsx src/local-agent-store.test.ts && tsx src/local-agent-manager.test.ts && tsx src/roots.test.ts && tsx src/skills.test.ts && tsx src/workspaces.test.ts && tsx src/workspace-conversation.test.ts && tsx src/review-checkpoints.test.ts && tsx src/server.test.ts && tsx src/oauth-store.test.ts && tsx src/cli.test.ts", + "test": "tsx src/user-config.test.ts && tsx src/config.test.ts && tsx src/onboarding.test.ts && tsx src/cli-workspace.test.ts && tsx src/request-meta.test.ts && tsx src/incoming-artifacts.test.ts && tsx src/artifact-download.test.ts && tsx src/ui/card-types.test.ts && tsx src/ui/tool-result.test.ts && tsx src/ui/patch-display.test.ts && tsx src/apply-patch.test.ts && tsx src/process-platform.test.ts && tsx src/process-sessions.test.ts && tsx src/mcp-sessions.test.ts && tsx src/server-shutdown.test.ts && tsx src/local-agent-config.test.ts && tsx src/local-agent-catalog.test.ts && tsx src/local-agent-presentation.test.ts && tsx src/local-agent-runtime.test.ts && tsx src/local-agent-daemon-lifecycle.test.ts && tsx src/local-agent-daemon-protocol.test.ts && tsx src/local-agent-daemon.test.ts && tsx src/local-agent-codex.test.ts && tsx src/local-agent-opencode.test.ts && tsx src/local-agent-acp.test.ts && tsx src/local-agent-grok.test.ts && tsx src/local-agent-pi-sandbox.test.ts && tsx src/local-agent-pi.test.ts && tsx src/local-agent-claude.test.ts && tsx src/local-agent-adapters.test.ts && tsx src/local-agent-availability.test.ts && tsx src/local-agent-profiles.test.ts && tsx src/local-agent-targets.test.ts && tsx src/local-agent-store.test.ts && tsx src/local-agent-manager.test.ts && tsx src/roots.test.ts && tsx src/skills.test.ts && tsx src/workspaces.test.ts && tsx src/workspace-conversation.test.ts && tsx src/review-checkpoints.test.ts && tsx src/server.test.ts && tsx src/oauth-store.test.ts && tsx src/cli-show-changes.test.ts && tsx src/cli.test.ts", "typecheck": "tsx src/config-schema.test.ts && tsc -p tsconfig.json --noEmit" }, "keywords": [], diff --git a/src/server.test.ts b/src/server.test.ts index b7d8d3513..127abba1f 100644 --- a/src/server.test.ts +++ b/src/server.test.ts @@ -74,7 +74,7 @@ test("open_workspace reports aggregate review availability", async (t) => { assert.deepEqual(gitReview, { available: true }); }); -test("show_changes exposes the aggregate diff to plain MCP hosts", async (t) => { +test("show_changes keeps model output compact and preserves the rich review card", async (t) => { const context = await fixture(t, { git: true, uiEnabled: false }); const opened = structuredContent( await callOpen(context.client, context.project, "review"), @@ -88,13 +88,22 @@ test("show_changes exposes the aggregate diff to plain MCP hosts", async (t) => arguments: { workspaceId }, }); const structured = structuredContent(review); + assert.equal((review._meta as Record | undefined)?.tool, undefined); - assert.deepEqual(structured.summary, { + assert.equal(structured.workspaceId, workspaceId); + assert.match(structured.reviewRef as string, /^[0-9a-f]{40,64}$/); + assert.match(structured.result as string, /Changed 1 file \(\+1 -1\)/); + assert.equal("summary" in structured, false); + assert.equal("files" in structured, false); + assert.equal("patch" in structured, false); + + const card = responseCard(review); + assert.deepEqual(card.summary, { files: 1, additions: 1, removals: 1, }); - assert.deepEqual(structured.files, [ + assert.deepEqual(card.files, [ { path: "README.md", type: "change", @@ -102,14 +111,59 @@ test("show_changes exposes the aggregate diff to plain MCP hosts", async (t) => removals: 1, }, ]); - assert.match(structured.patch as string, /-hello\n\+goodbye/); + assert.match( + ((card.payload as { patch?: string } | undefined)?.patch) ?? "", + /-hello\n\+goodbye/, + ); const tools = await context.client.listTools(); const outputProperties = tools.tools.find((tool) => tool.name === "show_changes") ?.outputSchema?.properties; - assert.ok(outputProperties && "summary" in outputProperties); - assert.ok(outputProperties && "files" in outputProperties); - assert.ok(outputProperties && "patch" in outputProperties); + assert.ok(outputProperties && "workspaceId" in outputProperties); + assert.ok(outputProperties && "reviewRef" in outputProperties); + assert.equal(outputProperties && "summary" in outputProperties, false); + assert.equal(outputProperties && "files" in outputProperties, false); + assert.equal(outputProperties && "patch" in outputProperties, false); + const inputProperties = tools.tools.find((tool) => tool.name === "show_changes") + ?.inputSchema?.properties; + assert.equal(inputProperties && "reviewRef" in inputProperties, false); +}); + +test("show_changes can reopen a historical review without advancing the checkpoint", async (t) => { + const context = await fixture(t, { git: true }); + const workspaceId = structuredContent( + await callOpen(context.client, context.project, "review-history"), + ).workspaceId; + assert.equal(typeof workspaceId, "string"); + + await writeFile(join(context.project, "README.md"), "first\n"); + const first = structuredContent(await context.client.callTool({ + name: "show_changes", + arguments: { workspaceId }, + })); + const reviewRef = first.reviewRef; + assert.equal(typeof reviewRef, "string"); + + await writeFile(join(context.project, "README.md"), "second\n"); + const reopened = await context.client.callTool({ + name: "show_changes", + arguments: { workspaceId }, + _meta: { "devspace/reviewRef": reviewRef }, + } as Parameters[0]); + assert.equal(structuredContent(reopened).reviewRef, reviewRef); + assert.match( + (((responseCard(reopened).payload as { patch?: string } | undefined)?.patch) ?? ""), + /\+first/, + ); + + const current = await context.client.callTool({ + name: "show_changes", + arguments: { workspaceId }, + }); + assert.match( + (((responseCard(current).payload as { patch?: string } | undefined)?.patch) ?? ""), + /-first\n\+second/, + ); }); test("open_workspace keeps lifecycle flags out of model output and preserves complete card metadata", async (t) => { @@ -119,6 +173,8 @@ test("open_workspace keeps lifecycle flags out of model output and preserves com }); const first = await callOpen(context.client, context.project, "chat-1"); const repeated = await callOpen(context.client, context.project, "chat-1"); + assert.equal((first._meta as Record | undefined)?.tool, undefined); + assert.equal((repeated._meta as Record | undefined)?.tool, undefined); const tools = await context.client.listTools(); const openTool = tools.tools.find((tool) => tool.name === "open_workspace"); diff --git a/src/server.ts b/src/server.ts index dd0a15e82..9e7ded7fd 100644 --- a/src/server.ts +++ b/src/server.ts @@ -167,20 +167,6 @@ const workspaceAvailableAgentsFileOutputSchema = z.object({ path: z.string(), }); -const reviewFileOutputSchema = z.object({ - path: z.string(), - previousPath: z.string().optional(), - type: z.enum(["change", "rename-pure", "rename-changed", "new", "deleted"]), - additions: z.number(), - removals: z.number(), -}); - -const reviewSummaryOutputSchema = z.object({ - files: z.number(), - additions: z.number(), - removals: z.number(), -}); - function sendJsonRpcError( res: Response, status: number, @@ -504,7 +490,6 @@ export function createMcpServer( return { content: resultContent, _meta: { - tool: "open_workspace", card: { workspaceId: workspace.id, root: workspace.root, @@ -653,21 +638,29 @@ export function createMcpServer( workspaceId: z.string().describe(workspaceIdDescription), }, outputSchema: resultOutputSchema({ - summary: reviewSummaryOutputSchema, - files: z.array(reviewFileOutputSchema), - patch: z.string(), + workspaceId: z.string(), + reviewRef: z.string().regex(/^[0-9a-f]{40,64}$/), }), ...workspaceAppDescriptorMeta(config), annotations: { readOnlyHint: true }, }, - async ({ workspaceId }) => { + async ({ workspaceId }, { _meta }) => { const startedAt = performance.now(); const workspace = workspaces.getWorkspace(workspaceId); - const review = await reviewCheckpoints.reviewChanges({ - workspaceId, - root: workspace.root, - markReviewed: true, - }); + const reviewRef = typeof _meta?.["devspace/reviewRef"] === "string" + ? _meta["devspace/reviewRef"] + : undefined; + const review = reviewRef + ? await reviewCheckpoints.reviewByRef({ + workspaceId, + root: workspace.root, + reviewRef, + }) + : await reviewCheckpoints.reviewChanges({ + workspaceId, + root: workspace.root, + markReviewed: true, + }); const content = [textBlock(review.result)]; logToolCall(config, { @@ -680,7 +673,6 @@ export function createMcpServer( return { content, _meta: { - tool: "show_changes", card: { workspaceId, summary: review.summary, @@ -691,10 +683,9 @@ export function createMcpServer( }, }, structuredContent: { + workspaceId, + reviewRef: review.reviewRef, result: contentText(content), - summary: review.summary, - files: review.files, - patch: review.patch, }, }; }, diff --git a/src/ui/card-types.test.ts b/src/ui/card-types.test.ts index 2ae9f55e9..19e5b582a 100644 --- a/src/ui/card-types.test.ts +++ b/src/ui/card-types.test.ts @@ -3,15 +3,8 @@ import test from "node:test"; import { isExpandableCard, isInitiallyExpandedCard, - isToolName, } from "./card-types.js"; -test("only UI-backed tools are recognized as card tools", () => { - assert.equal(isToolName("open_workspace"), true); - assert.equal(isToolName("show_changes"), true); - assert.equal(isToolName("read"), false); -}); - test("aggregate review opens when a patch is available", () => { const card = { tool: "show_changes" as const, diff --git a/src/ui/card-types.ts b/src/ui/card-types.ts index 5ab0a5321..6c84d0fa7 100644 --- a/src/ui/card-types.ts +++ b/src/ui/card-types.ts @@ -67,14 +67,6 @@ export interface ToolResultCard { instruction?: string; } -export function isToolName(value: unknown): value is ToolName { - return value === "open_workspace" || value === "show_changes"; -} - -export function isToolResultCard(value: unknown): value is Omit { - return Boolean(value && typeof value === "object"); -} - export function summaryNumber( summary: Record | undefined, key: string, diff --git a/src/ui/tool-result.test.ts b/src/ui/tool-result.test.ts new file mode 100644 index 000000000..4687e028c --- /dev/null +++ b/src/ui/tool-result.test.ts @@ -0,0 +1,137 @@ +import assert from "node:assert/strict"; +import test from "node:test"; +import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; +import { + decodeToolResult, + toolResultFromChatGptGlobals, +} from "./tool-result.js"; + +test("workspace cards can be rebuilt from structured content without result metadata", () => { + const decoded = decodeToolResult({ + content: [], + structuredContent: { + workspaceId: "ws_1", + root: "/tmp/project", + mode: "checkout", + skills: [{ name: "tdd", description: "Tests first", path: "/tmp/tdd/SKILL.md" }], + agentsFiles: [{ path: "AGENTS.md", content: "instructions" }], + review: { available: true }, + instruction: "Reuse this workspace.", + }, + }); + + assert.equal(decoded.kind, "card"); + if (decoded.kind !== "card") return; + assert.equal(decoded.card.tool, "open_workspace"); + assert.equal(decoded.card.workspaceId, "ws_1"); + assert.equal(decoded.card.summary?.skills, 1); + assert.equal(decoded.card.summary?.agentsFiles, 1); +}); + +test("review results use rich metadata when the host provides it", () => { + const decoded = decodeToolResult({ + content: [], + structuredContent: { + workspaceId: "ws_1", + reviewRef: "a".repeat(40), + result: "Changed 1 file (+1 -0).", + }, + _meta: { + card: { + workspaceId: "ws_1", + summary: { files: 1, additions: 1, removals: 0 }, + files: [{ path: "new.txt", type: "new", additions: 1, removals: 0 }], + payload: { patch: "diff --git ..." }, + }, + }, + }); + + assert.equal(decoded.kind, "card"); + if (decoded.kind !== "card") return; + assert.equal(decoded.card.tool, "show_changes"); + assert.equal(decoded.card.files?.[0]?.path, "new.txt"); + assert.equal(decoded.card.payload?.patch, "diff --git ..."); +}); + +test("review structured content becomes a reload reference when metadata is missing", () => { + const decoded = decodeToolResult({ + content: [], + structuredContent: { + workspaceId: "ws_1", + reviewRef: "b".repeat(40), + result: "Changed 1 file (+1 -0).", + }, + }); + + assert.deepEqual(decoded, { + kind: "review-reference", + workspaceId: "ws_1", + reviewRef: "b".repeat(40), + }); +}); + +test("older review results can reload from their structured patch", () => { + const decoded = decodeToolResult({ + content: [], + structuredContent: { + result: "Changed 1 file (+1 -0).", + summary: { files: 1, additions: 1, removals: 0 }, + files: [{ path: "new.txt", type: "new", additions: 1, removals: 0 }], + patch: "diff --git a/new.txt b/new.txt", + }, + }); + + assert.equal(decoded.kind, "card"); + if (decoded.kind !== "card") return; + assert.equal(decoded.card.tool, "show_changes"); + assert.equal(decoded.card.files?.[0]?.path, "new.txt"); + assert.equal(decoded.card.payload?.patch, "diff --git a/new.txt b/new.txt"); +}); + +test("ChatGPT globals restore structured output and hidden MCP result metadata together", () => { + const fullResult: CallToolResult = { + content: [{ type: "text", text: "Changed 1 file." }], + structuredContent: { stale: true }, + _meta: { card: { workspaceId: "ws_1", payload: { patch: "patch" } } }, + }; + const restored = toolResultFromChatGptGlobals({ + toolOutput: { + workspaceId: "ws_1", + reviewRef: "c".repeat(40), + result: "Changed 1 file.", + }, + toolResponseMetadata: { + mcp_tool_result: fullResult, + }, + }); + + assert.deepEqual(restored?.structuredContent, { + workspaceId: "ws_1", + reviewRef: "c".repeat(40), + result: "Changed 1 file.", + }); + assert.deepEqual(restored?._meta, fullResult._meta); +}); + +test("ChatGPT globals also accept result metadata exposed directly", () => { + const restored = toolResultFromChatGptGlobals({ + toolOutput: { + workspaceId: "ws_1", + reviewRef: "d".repeat(40), + result: "Changed 1 file.", + }, + toolResponseMetadata: { + card: { + workspaceId: "ws_1", + summary: { files: 1, additions: 1, removals: 0 }, + }, + }, + }); + + assert.deepEqual(restored?._meta, { + card: { + workspaceId: "ws_1", + summary: { files: 1, additions: 1, removals: 0 }, + }, + }); +}); diff --git a/src/ui/tool-result.ts b/src/ui/tool-result.ts new file mode 100644 index 000000000..d029eaf11 --- /dev/null +++ b/src/ui/tool-result.ts @@ -0,0 +1,250 @@ +import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; +import type { ReviewFileType, ToolResultCard } from "./card-types.js"; + +export type DecodedToolResult = + | { kind: "card"; card: ToolResultCard } + | { kind: "review-reference"; workspaceId: string; reviewRef: string } + | { kind: "invalid" }; + +export interface ChatGptToolGlobals { + toolOutput?: unknown; + toolResponseMetadata?: unknown; +} + +export function decodeToolResult(result: CallToolResult): DecodedToolResult { + const structured = asRecord(result.structuredContent); + const metaCard = cardFields(asRecord(asRecord(result._meta)?.card)); + + if (structured) { + const workspaceId = stringField(structured.workspaceId); + const reviewRef = stringField(structured.reviewRef); + if (workspaceId && reviewRef) { + if (metaCard) { + return { + kind: "card", + card: { + ...metaCard, + tool: "show_changes", + workspaceId, + }, + }; + } + return { kind: "review-reference", workspaceId, reviewRef }; + } + + if (typeof structured.patch === "string" && Array.isArray(structured.files)) { + const legacyCard = cardFields({ + ...structured, + payload: { patch: structured.patch }, + }); + if (legacyCard) { + return { kind: "card", card: { ...legacyCard, tool: "show_changes" } }; + } + } + + const root = stringField(structured.root); + const mode = workspaceMode(structured.mode); + if (workspaceId && root && mode) { + const structuredCard = cardFields(structured) ?? {}; + return { + kind: "card", + card: { + ...structuredCard, + ...metaCard, + tool: "open_workspace", + workspaceId, + root, + mode, + summary: metaCard?.summary ?? workspaceSummary(structuredCard), + }, + }; + } + } + + // Existing conversations created before reviewRef was added can still render + // while the host supplies their live MCP Apps result metadata. + if (metaCard?.workspaceId && (metaCard.files?.length || metaCard.payload?.patch)) { + return { kind: "card", card: { ...metaCard, tool: "show_changes" } }; + } + if (metaCard?.workspaceId && metaCard.root && metaCard.mode) { + return { kind: "card", card: { ...metaCard, tool: "open_workspace" } }; + } + + return { kind: "invalid" }; +} + +export function toolResultFromChatGptGlobals( + globals: ChatGptToolGlobals | undefined, +): CallToolResult | undefined { + if (!globals) return undefined; + + const responseMetadata = asRecord(globals.toolResponseMetadata); + const metadataResult = mcpToolResult(globals.toolResponseMetadata); + const structuredContent = asRecord(globals.toolOutput) + ?? asRecord(metadataResult?.structuredContent); + const resultMeta = asRecord(metadataResult?._meta) + ?? directResultMeta(responseMetadata); + if (!metadataResult && !structuredContent && !resultMeta) return undefined; + + return { + ...(metadataResult ?? { content: [] }), + ...(structuredContent ? { structuredContent } : {}), + ...(resultMeta ? { _meta: resultMeta } : {}), + } as CallToolResult; +} + +function directResultMeta( + metadata: Record | undefined, +): Record | undefined { + if (!metadata) return undefined; + return "card" in metadata ? metadata : undefined; +} + +function mcpToolResult(value: unknown): CallToolResult | undefined { + const metadata = asRecord(value); + if (!metadata) return undefined; + + const direct = asRecord(metadata.mcp_tool_result); + if (direct) return direct as CallToolResult; + + const callToolResult = asRecord(metadata.call_tool_result); + const nested = asRecord(callToolResult?.mcp_tool_result); + return nested ? nested as CallToolResult : undefined; +} + +function cardFields(record: Record | undefined): Partial | undefined { + if (!record) return undefined; + + const agentsFiles = arrayRecords(record.agentsFiles)?.map((item) => ({ + path: stringField(item.path), + content: stringField(item.content), + })); + const availableAgentsFiles = arrayRecords(record.availableAgentsFiles)?.map((item) => ({ + path: stringField(item.path), + })); + const skills = arrayRecords(record.skills)?.map((item) => ({ + name: stringField(item.name), + description: stringField(item.description), + path: stringField(item.path), + })); + const agentProviders = arrayRecords(record.agentProviders)?.map((item) => ({ + id: stringField(item.id), + model: stringField(item.model), + effort: stringField(item.effort), + note: stringField(item.note), + })); + const agents = arrayRecords(record.agents)?.map((item) => ({ + name: stringField(item.name), + description: stringField(item.description), + provider: stringField(item.provider), + model: stringField(item.model), + effort: stringField(item.effort), + })); + const files = arrayRecords(record.files)?.map((item) => ({ + path: stringField(item.path), + previousPath: stringField(item.previousPath), + type: reviewFileType(item.type), + additions: numberField(item.additions), + removals: numberField(item.removals), + })); + const worktreeRecord = asRecord(record.worktree); + const reviewRecord = asRecord(record.review); + const summary = asRecord(record.summary); + const payloadRecord = asRecord(record.payload); + + return definedFields({ + workspaceId: stringField(record.workspaceId), + path: stringField(record.path), + root: stringField(record.root), + workspaceReused: booleanField(record.workspaceReused), + includeBootstrapContext: booleanField(record.includeBootstrapContext), + mode: workspaceMode(record.mode), + sourceRoot: stringField(record.sourceRoot), + worktree: worktreeRecord + ? { + path: stringField(worktreeRecord.path), + baseRef: stringField(worktreeRecord.baseRef), + baseSha: stringField(worktreeRecord.baseSha), + dirtySource: booleanField(worktreeRecord.dirtySource), + detached: booleanField(worktreeRecord.detached), + managed: booleanField(worktreeRecord.managed), + } + : undefined, + review: reviewAvailability(reviewRecord), + summary, + files, + payload: payloadRecord ? { patch: stringField(payloadRecord.patch) } : undefined, + agentsFiles, + availableAgentsFiles, + skills, + agentProviders, + agents, + instruction: stringField(record.instruction), + }); +} + +function workspaceSummary(card: Partial): Record { + return { + mode: card.mode, + agentsFiles: card.agentsFiles?.length ?? 0, + availableAgentsFiles: card.availableAgentsFiles?.length ?? 0, + skills: card.skills?.length ?? 0, + agentProviders: card.agentProviders?.length ?? 0, + agents: card.agents?.length ?? 0, + }; +} + +function reviewAvailability( + record: Record | undefined, +): ToolResultCard["review"] { + if (!record || typeof record.available !== "boolean") return undefined; + if (record.available) return { available: true }; + const reason = stringField(record.reason); + return reason ? { available: false, reason } : undefined; +} + +function reviewFileType(value: unknown): ReviewFileType | undefined { + return value === "change" + || value === "rename-pure" + || value === "rename-changed" + || value === "new" + || value === "deleted" + ? value + : undefined; +} + +function workspaceMode(value: unknown): ToolResultCard["mode"] { + return value === "checkout" || value === "worktree" ? value : undefined; +} + +function arrayRecords(value: unknown): Array> | undefined { + if (!Array.isArray(value)) return undefined; + return value.flatMap((item) => { + const record = asRecord(item); + return record ? [record] : []; + }); +} + +function asRecord(value: unknown): Record | undefined { + return value !== null && typeof value === "object" + ? value as Record + : undefined; +} + +function stringField(value: unknown): string | undefined { + return typeof value === "string" ? value : undefined; +} + +function numberField(value: unknown): number | undefined { + return typeof value === "number" && Number.isFinite(value) ? value : undefined; +} + +function booleanField(value: unknown): boolean | undefined { + return typeof value === "boolean" ? value : undefined; +} + +function definedFields>(record: T): T { + return Object.fromEntries( + Object.entries(record).filter(([, value]) => value !== undefined), + ) as T; +} diff --git a/src/ui/vite-env.d.ts b/src/ui/vite-env.d.ts index cbe652dbe..e224b6eba 100644 --- a/src/ui/vite-env.d.ts +++ b/src/ui/vite-env.d.ts @@ -1 +1,8 @@ declare module "*.css"; + +interface Window { + openai?: { + toolOutput?: unknown; + toolResponseMetadata?: unknown; + }; +} diff --git a/src/ui/workspace-app.tsx b/src/ui/workspace-app.tsx index c3c6f36e8..bc101cb37 100644 --- a/src/ui/workspace-app.tsx +++ b/src/ui/workspace-app.tsx @@ -8,11 +8,8 @@ import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; import { isExpandableCard, isInitiallyExpandedCard, - isToolName, - isToolResultCard, summaryNumber, type HostContext, - type ToolName, type ToolResultCard, } from "./card-types.js"; import { getProviderLogo, renderIcon, toolIcons, type ToolIcon } from "./icons.js"; @@ -20,6 +17,11 @@ import { getFileChangePathDisplay, getPatchDisplayParts, } from "./patch-display.js"; +import { + decodeToolResult, + toolResultFromChatGptGlobals, + type ChatGptToolGlobals, +} from "./tool-result.js"; import "./workspace-app.css"; interface CardDisplay { @@ -51,6 +53,8 @@ let currentPayload: MountedPayload | null = null; let currentPayloadContainer: HTMLElement | null = null; let openWorkspaceInstructionKey: string | null = null; let showAvailableWorkspaceInstructions = false; +let pendingToolResult: CallToolResult | null = null; +let pendingReviewKey: string | null = null; const maybeAppRoot = document.querySelector("#app"); @@ -71,32 +75,11 @@ async function boot(): Promise { ); app.ontoolresult = (result) => { - const structuredContent = getStructuredContent>(result); - const metaCard = cardFromMeta(result); - const structured = metaCard - ? { ...structuredContent, ...metaCard } - : structuredContent; - const tool = toolNameFromMeta(result); - - if (!tool || !isToolResultCard(structured)) { - card = null; - expanded = false; - reviewFilesExpanded = false; - openWorkspaceInstructionKey = null; - showAvailableWorkspaceInstructions = false; - errorMessage = "No result card is available for this tool result."; - render(); + if (!connected) { + pendingToolResult = result; return; } - - const nextCard = { ...structured, tool }; - card = nextCard; - expanded = isInitiallyExpandedCard(nextCard); - reviewFilesExpanded = false; - openWorkspaceInstructionKey = null; - showAvailableWorkspaceInstructions = false; - errorMessage = null; - render(); + void applyToolResult(result); }; app.onhostcontextchanged = (ctx) => { @@ -111,6 +94,7 @@ async function boot(): Promise { }; app.onteardown = async () => { + window.removeEventListener("openai:set_globals", handleChatGptGlobalsChanged); unmountPayload(); return {}; }; @@ -121,15 +105,114 @@ async function boot(): Promise { if (initialContext) hostContext = initialContext; applyHostContext(); connected = true; + window.addEventListener("openai:set_globals", handleChatGptGlobalsChanged); } catch (connectError) { connectionError = connectError instanceof Error ? connectError.message : String(connectError); } + const initialResult = pendingToolResult ?? chatGptRestoredResult(); + pendingToolResult = null; + if (initialResult) { + await applyToolResult(initialResult); + } else { + render(); + } +} + +async function applyToolResult(result: CallToolResult): Promise { + const decoded = decodeToolResult(result); + if (decoded.kind === "card") { + setCard(decoded.card); + return; + } + if (decoded.kind === "invalid") { + clearCard("No result card is available for this tool result."); + return; + } + + const reviewKey = `${decoded.workspaceId}:${decoded.reviewRef}`; + pendingReviewKey = reviewKey; + card = null; + errorMessage = null; + resetCardInteractions(); + render(); + + try { + const restored = await reopenReview(decoded.workspaceId, decoded.reviewRef); + if (pendingReviewKey !== reviewKey) return; + + const restoredResult = decodeToolResult(restored); + if (restoredResult.kind !== "card" || restoredResult.card.tool !== "show_changes") { + throw new Error("The host returned an incomplete historical review."); + } + setCard(restoredResult.card); + } catch (reviewError) { + if (pendingReviewKey !== reviewKey) return; + clearCard( + reviewError instanceof Error + ? reviewError.message + : String(reviewError), + ); + } +} + +function setCard(nextCard: ToolResultCard): void { + pendingReviewKey = null; + card = nextCard; + expanded = isInitiallyExpandedCard(nextCard); + reviewFilesExpanded = false; + openWorkspaceInstructionKey = null; + showAvailableWorkspaceInstructions = false; + errorMessage = null; render(); } +function clearCard(message: string): void { + pendingReviewKey = null; + card = null; + errorMessage = message; + resetCardInteractions(); + render(); +} + +function resetCardInteractions(): void { + expanded = false; + reviewFilesExpanded = false; + openWorkspaceInstructionKey = null; + showAvailableWorkspaceInstructions = false; +} + +async function reopenReview( + workspaceId: string, + reviewRef: string, +): Promise { + if (!app) throw new Error("The app bridge is not connected."); + if (!app.getHostCapabilities()?.serverTools) { + throw new Error("This host cannot reload historical review details."); + } + + return app.callServerTool({ + name: "show_changes", + arguments: { workspaceId }, + _meta: { "devspace/reviewRef": reviewRef }, + }); +} + +function chatGptRestoredResult(): CallToolResult | undefined { + return toolResultFromChatGptGlobals(window.openai); +} + +function handleChatGptGlobalsChanged(event: Event): void { + if (!connected || card) return; + + const customEvent = event as CustomEvent<{ globals?: ChatGptToolGlobals }>; + const restored = toolResultFromChatGptGlobals(customEvent.detail?.globals) + ?? chatGptRestoredResult(); + if (restored) void applyToolResult(restored); +} + function applyHostContext(): void { if (hostContext?.theme) applyDocumentTheme(hostContext.theme); if (hostContext?.styles?.variables) { @@ -401,9 +484,14 @@ function toolCardClassName(display: CardDisplay): string { function cardDisplay(card: ToolResultCard): CardDisplay { if (card.tool === "open_workspace") { + const title = card.workspaceReused === true + ? "Reused workspace" + : card.workspaceReused === false + ? "Opened workspace" + : "Workspace"; return { icon: card.mode === "worktree" ? toolIcons.gitBranch : toolIcons.folderOpen, - title: `${card.workspaceReused ? "Reused" : "Opened"} workspace`, + title, label: card.root ?? card.path, tone: "workspace", }; @@ -837,22 +925,6 @@ function renderWorkspaceChips(chips: WorkspaceChip[]): HTMLElement { return list; } -function toolNameFromMeta(result: CallToolResult): ToolName | undefined { - const meta = result._meta as Record | undefined; - const tool = meta?.tool; - return isToolName(tool) ? tool : undefined; -} - -function cardFromMeta(result: CallToolResult): Partial | undefined { - const meta = result._meta as Record | undefined; - const metaCard = meta?.card; - return metaCard && typeof metaCard === "object" ? metaCard : undefined; -} - -function getStructuredContent(result: CallToolResult): T | undefined { - return result.structuredContent as T | undefined; -} - function element( tag: K, options: { From 60f7f2795fcfad71196124c3a4c55eaab8e684d5 Mon Sep 17 00:00:00 2001 From: Waishnav <86405648+Waishnav@users.noreply.github.com> Date: Tue, 25 Aug 2026 21:50:35 +0530 Subject: [PATCH 39/75] feat(cli): inspect Git-backed reviews --- docs/chatgpt-coding-workflow.md | 10 +++++ docs/gotchas.md | 5 +++ src/cli-show-changes.test.ts | 72 +++++++++++++++++++++++++++++++++ src/cli.ts | 40 +++++++++++++++++- 4 files changed, 125 insertions(+), 2 deletions(-) create mode 100644 src/cli-show-changes.test.ts diff --git a/docs/chatgpt-coding-workflow.md b/docs/chatgpt-coding-workflow.md index 1cb43e7ac..f6826617c 100644 --- a/docs/chatgpt-coding-workflow.md +++ b/docs/chatgpt-coding-workflow.md @@ -192,6 +192,16 @@ that changes files. It shows the combined changes for that turn and advances the review point automatically. Reusing a workspace does not change this workflow. +The model-facing result stays compact: DevSpace returns the workspace ID, a +Git-backed `reviewRef`, and the summary text. MCP Apps hosts receive the full +file list and patch in result metadata for immediate rendering. If a host later +restores only the structured result, the review card can reopen that exact +`reviewRef` from DevSpace's Git review history without advancing the current +review point. + +For local inspection, run `devspace show-changes `. Add `--json` to +include the parsed summary, file list, and patch. + ## Shell Use The shell tool is for commands that belong in a terminal: diff --git a/docs/gotchas.md b/docs/gotchas.md index 2f6093552..5f6288678 100644 --- a/docs/gotchas.md +++ b/docs/gotchas.md @@ -251,3 +251,8 @@ metadata and only show text results; `show_changes` remains available there. If both cards are missing in ChatGPT, confirm that `ui.enabled` is not `false` in `~/.devspace/config.jsonc` and reconnect the MCP server. + +Historical `show_changes` cards use the `reviewRef` in their structured result +to recover the exact Git-backed review when a host reloads the app without its +original result metadata. `open_workspace` can rebuild its card directly from +its structured result. diff --git a/src/cli-show-changes.test.ts b/src/cli-show-changes.test.ts new file mode 100644 index 000000000..6d4d9ef57 --- /dev/null +++ b/src/cli-show-changes.test.ts @@ -0,0 +1,72 @@ +import assert from "node:assert/strict"; +import { execFile } from "node:child_process"; +import { mkdtemp, rm, writeFile } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { createRequire } from "node:module"; +import test from "node:test"; +import { fileURLToPath, pathToFileURL } from "node:url"; +import { promisify } from "node:util"; +import { createReviewCheckpointManager } from "./review-checkpoints.js"; +import { writeTestDevspaceConfig } from "./test-support/config.test.js"; + +const execFileAsync = promisify(execFile); +const require = createRequire(import.meta.url); +const tsxLoader = pathToFileURL(require.resolve("tsx")).href; +const cliPath = fileURLToPath(new URL("./cli.ts", import.meta.url)); + +test("show-changes prints a Git-backed historical review", async (t) => { + const root = await mkdtemp(join(tmpdir(), "devspace-cli-show-changes-")); + t.after(() => rm(root, { recursive: true, force: true })); + const project = join(root, "project"); + await execFileAsync("git", ["init", project]); + await git(project, ["config", "user.email", "devspace@example.com"]); + await git(project, ["config", "user.name", "DevSpace Test"]); + await writeFile(join(project, "README.md"), "hello\n"); + await git(project, ["add", "README.md"]); + await git(project, ["commit", "-m", "Initial commit"]); + + const manager = createReviewCheckpointManager(); + await manager.initializeWorkspace({ workspaceId: "ws_cli", root: project }); + await writeFile(join(project, "README.md"), "hello\nreview me\n"); + const review = await manager.reviewChanges({ workspaceId: "ws_cli", root: project }); + + const configDir = join(root, ".devspace"); + const env = writeTestDevspaceConfig(configDir, { + workspaces: { allowedRoots: [project] }, + storage: { stateDir: join(root, ".state") }, + }); + const cliArgs = ["--import", tsxLoader, cliPath, "show-changes", review.reviewRef]; + const plain = await execFileAsync("node", cliArgs, { + cwd: project, + env: { + ...process.env, + ...env, + DEVSPACE_WORKSPACE_ID: "", + DEVSPACE_WORKSPACE_ROOT: "", + }, + encoding: "utf8", + }); + assert.match(plain.stdout, /\+review me/); + + const json = await execFileAsync("node", [...cliArgs, "--json"], { + cwd: project, + env: { + ...process.env, + ...env, + DEVSPACE_WORKSPACE_ID: "", + DEVSPACE_WORKSPACE_ROOT: "", + }, + encoding: "utf8", + }); + const parsed = JSON.parse(json.stdout) as { + reviewRef: string; + patch: string; + }; + assert.equal(parsed.reviewRef, review.reviewRef); + assert.equal(parsed.patch, review.patch); +}); + +async function git(cwd: string, args: string[]): Promise { + await execFileAsync("git", args, { cwd }); +} diff --git a/src/cli.ts b/src/cli.ts index 922481516..b521556a3 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -50,9 +50,18 @@ import { writeDevspaceAuth, } from "./user-config.js"; import { expandHomePath } from "./roots.js"; +import { readReviewRef } from "./review-checkpoints.js"; import { shutdownHttpServer } from "./server-shutdown.js"; -type Command = "serve" | "init" | "doctor" | "config" | "agents" | "help" | "version"; +type Command = + | "serve" + | "init" + | "doctor" + | "config" + | "agents" + | "show-changes" + | "help" + | "version"; const require = createRequire(import.meta.url); const SUPPORTED_NODE_RANGE = ">=20.12 <27"; @@ -79,6 +88,9 @@ async function main(argv: string[]): Promise { case "agents": await runAgentsCommand(args); return; + case "show-changes": + await runShowChanges(args); + return; case "help": printHelp(); return; @@ -90,7 +102,13 @@ async function main(argv: string[]): Promise { function normalizeCommand(command: string | undefined): Command { if (!command || command === "serve" || command === "start") return "serve"; - if (command === "init" || command === "doctor" || command === "config" || command === "agents") return command; + if ( + command === "init" + || command === "doctor" + || command === "config" + || command === "agents" + || command === "show-changes" + ) return command; if (command === "help" || command === "--help" || command === "-h") return "help"; if (command === "version" || command === "--version" || command === "-v") return "version"; throw new Error(`Unknown command: ${command}`); @@ -391,6 +409,7 @@ function printHelp(): void { " devspace doctor Show config, runtime, and native dependency status", " devspace config get Print persisted config", " devspace config set publicBaseUrl ", + " devspace show-changes [--json]", " devspace agents ls List subagent sessions", " devspace agents run [--model ] [--effort ] ", " devspace agents continue [--model ] [--effort ] ", @@ -405,6 +424,23 @@ function printHelp(): void { ); } +async function runShowChanges(args: string[]): Promise { + const { args: commandArgs, json } = extractJsonOption(args); + const [reviewRef, ...extra] = commandArgs; + if (!reviewRef || extra.length > 0) { + throw new Error("Usage: devspace show-changes [--json]"); + } + + const config = loadConfig(); + const scope = resolveCliWorkspaceContext(config.allowedRoots); + const review = await readReviewRef(scope.workspaceRoot, reviewRef); + if (json) { + printJson(review); + return; + } + console.log(review.patch || review.result); +} + async function runAgentsCommand(args: string[]): Promise { const [subcommand, ...rest] = args; const { args: commandArgs, json } = extractJsonOption(rest); From ac1b6161b9c33742d724b9d52715d962c36641ca Mon Sep 17 00:00:00 2001 From: Waishnav <86405648+Waishnav@users.noreply.github.com> Date: Wed, 26 Aug 2026 01:12:08 +0530 Subject: [PATCH 40/75] fix(review): harden historical review restore --- src/cli-show-changes.test.ts | 41 +++++++++++++++++++++++++++++----- src/review-checkpoints.test.ts | 6 ++++- src/review-checkpoints.ts | 39 ++++++++++++++++++++++++++++++++ src/ui/tool-result.test.ts | 18 +++++++++++++++ src/ui/tool-result.ts | 17 +++++++++++++- 5 files changed, 113 insertions(+), 8 deletions(-) diff --git a/src/cli-show-changes.test.ts b/src/cli-show-changes.test.ts index 6d4d9ef57..7ee900b1e 100644 --- a/src/cli-show-changes.test.ts +++ b/src/cli-show-changes.test.ts @@ -1,21 +1,26 @@ import assert from "node:assert/strict"; import { execFile } from "node:child_process"; import { mkdtemp, rm, writeFile } from "node:fs/promises"; -import { tmpdir } from "node:os"; -import { join } from "node:path"; import { createRequire } from "node:module"; +import { tmpdir } from "node:os"; +import { dirname, join } from "node:path"; import test from "node:test"; -import { fileURLToPath, pathToFileURL } from "node:url"; +import { fileURLToPath } from "node:url"; import { promisify } from "node:util"; import { createReviewCheckpointManager } from "./review-checkpoints.js"; import { writeTestDevspaceConfig } from "./test-support/config.test.js"; const execFileAsync = promisify(execFile); const require = createRequire(import.meta.url); -const tsxLoader = pathToFileURL(require.resolve("tsx")).href; -const cliPath = fileURLToPath(new URL("./cli.ts", import.meta.url)); +const repoRoot = dirname(fileURLToPath(new URL("../package.json", import.meta.url))); +const cliPath = join(repoRoot, "dist", "cli.js"); +const tscPath = require.resolve("typescript/bin/tsc"); test("show-changes prints a Git-backed historical review", async (t) => { + await execFileAsync(process.execPath, [tscPath, "-p", join(repoRoot, "tsconfig.build.json")], { + cwd: repoRoot, + }); + const root = await mkdtemp(join(tmpdir(), "devspace-cli-show-changes-")); t.after(() => rm(root, { recursive: true, force: true })); const project = join(root, "project"); @@ -36,7 +41,7 @@ test("show-changes prints a Git-backed historical review", async (t) => { workspaces: { allowedRoots: [project] }, storage: { stateDir: join(root, ".state") }, }); - const cliArgs = ["--import", tsxLoader, cliPath, "show-changes", review.reviewRef]; + const cliArgs = [cliPath, "show-changes", review.reviewRef]; const plain = await execFileAsync("node", cliArgs, { cwd: project, env: { @@ -65,6 +70,30 @@ test("show-changes prints a Git-backed historical review", async (t) => { }; assert.equal(parsed.reviewRef, review.reviewRef); assert.equal(parsed.patch, review.patch); + + const head = (await execFileAsync("git", ["rev-parse", "HEAD"], { + cwd: project, + encoding: "utf8", + })).stdout.trim(); + await assert.rejects( + execFileAsync("node", [cliPath, "show-changes", head], { + cwd: project, + env: { + ...process.env, + ...env, + DEVSPACE_WORKSPACE_ID: "", + DEVSPACE_WORKSPACE_ROOT: "", + }, + encoding: "utf8", + }), + (error: unknown) => { + assert.match( + (error as { stderr?: string }).stderr ?? "", + /Unknown DevSpace review reference/, + ); + return true; + }, + ); }); async function git(cwd: string, args: string[]): Promise { diff --git a/src/review-checkpoints.test.ts b/src/review-checkpoints.test.ts index 6e9d09636..6707c6ea3 100644 --- a/src/review-checkpoints.test.ts +++ b/src/review-checkpoints.test.ts @@ -5,7 +5,7 @@ import { tmpdir } from "node:os"; import { join } from "node:path"; import test, { type TestContext } from "node:test"; import { promisify } from "node:util"; -import { createReviewCheckpointManager } from "./review-checkpoints.js"; +import { createReviewCheckpointManager, readReviewRef } from "./review-checkpoints.js"; const execFileAsync = promisify(execFile); @@ -112,6 +112,10 @@ test("review refs are scoped to the workspace review history", async (t) => { () => manager.reviewByRef({ workspaceId: "ws_scoped", root, reviewRef: head }), /Unknown review reference/, ); + await assert.rejects( + () => readReviewRef(root, head), + /Unknown DevSpace review reference/, + ); }); test("review checkpoints survive a manager restart", async (t) => { diff --git a/src/review-checkpoints.ts b/src/review-checkpoints.ts index 037ca8c51..6f6872752 100644 --- a/src/review-checkpoints.ts +++ b/src/review-checkpoints.ts @@ -188,6 +188,9 @@ export async function readReviewRef(root: string, reviewRef: string): Promise { + const refs = (await git(gitRoot, [ + "for-each-ref", + "--format=%(refname)\t%(objectname)", + REVIEW_REF_PREFIX, + ])).stdout.trim(); + if (!refs) return false; + + const histories = new Map(); + for (const line of refs.split("\n")) { + const [ref, commit] = line.split("\t"); + if (!ref || !commit) continue; + + const match = ref.match(/^refs\/devspace\/review\/(.+)\/(open|baseline)$/); + if (!match) continue; + const [, workspace, kind] = match; + if (!workspace || !kind) continue; + + const history = histories.get(workspace) ?? {}; + history[kind as "open" | "baseline"] = commit; + histories.set(workspace, history); + } + + const memberships = await Promise.all( + [...histories.values()].map(async ({ open, baseline }) => { + if (!open || !baseline || reviewCommit === open) return false; + const [isAfterOpen, isBeforeBaseline] = await Promise.all([ + isAncestor(gitRoot, open, reviewCommit), + isAncestor(gitRoot, reviewCommit, baseline), + ]); + return isAfterOpen && isBeforeBaseline; + }), + ); + return memberships.some(Boolean); +} + function isReviewRef(value: string): boolean { return /^[0-9a-f]{40,64}$/.test(value); } diff --git a/src/ui/tool-result.test.ts b/src/ui/tool-result.test.ts index 4687e028c..b7c5315dd 100644 --- a/src/ui/tool-result.test.ts +++ b/src/ui/tool-result.test.ts @@ -70,6 +70,24 @@ test("review structured content becomes a reload reference when metadata is miss }); }); +test("incomplete review metadata falls back to the durable review reference", () => { + const decoded = decodeToolResult({ + content: [], + structuredContent: { + workspaceId: "ws_1", + reviewRef: "e".repeat(40), + result: "Changed 1 file (+1 -0).", + }, + _meta: { card: {} }, + }); + + assert.deepEqual(decoded, { + kind: "review-reference", + workspaceId: "ws_1", + reviewRef: "e".repeat(40), + }); +}); + test("older review results can reload from their structured patch", () => { const decoded = decodeToolResult({ content: [], diff --git a/src/ui/tool-result.ts b/src/ui/tool-result.ts index d029eaf11..efd1bdb4e 100644 --- a/src/ui/tool-result.ts +++ b/src/ui/tool-result.ts @@ -19,7 +19,7 @@ export function decodeToolResult(result: CallToolResult): DecodedToolResult { const workspaceId = stringField(structured.workspaceId); const reviewRef = stringField(structured.reviewRef); if (workspaceId && reviewRef) { - if (metaCard) { + if (isCompleteReviewCard(metaCard)) { return { kind: "card", card: { @@ -73,6 +73,21 @@ export function decodeToolResult(result: CallToolResult): DecodedToolResult { return { kind: "invalid" }; } +function isCompleteReviewCard( + card: Partial | undefined, +): card is Partial & { + files: NonNullable; + payload: { patch: string }; + summary: Record; +} { + if (!card || !Array.isArray(card.files) || typeof card.payload?.patch !== "string") { + return false; + } + return numberField(card.summary?.files) !== undefined + && numberField(card.summary?.additions) !== undefined + && numberField(card.summary?.removals) !== undefined; +} + export function toolResultFromChatGptGlobals( globals: ChatGptToolGlobals | undefined, ): CallToolResult | undefined { From 809f1a4492de3cd3218981fc6f844d48e36d31c3 Mon Sep 17 00:00:00 2001 From: Waishnav <86405648+Waishnav@users.noreply.github.com> Date: Wed, 26 Aug 2026 01:19:10 +0530 Subject: [PATCH 41/75] test(cli): bind review smoke to package entrypoint --- src/cli-show-changes.test.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/cli-show-changes.test.ts b/src/cli-show-changes.test.ts index 7ee900b1e..40763b86e 100644 --- a/src/cli-show-changes.test.ts +++ b/src/cli-show-changes.test.ts @@ -1,5 +1,6 @@ import assert from "node:assert/strict"; import { execFile } from "node:child_process"; +import { readFileSync } from "node:fs"; import { mkdtemp, rm, writeFile } from "node:fs/promises"; import { createRequire } from "node:module"; import { tmpdir } from "node:os"; @@ -12,8 +13,14 @@ import { writeTestDevspaceConfig } from "./test-support/config.test.js"; const execFileAsync = promisify(execFile); const require = createRequire(import.meta.url); -const repoRoot = dirname(fileURLToPath(new URL("../package.json", import.meta.url))); -const cliPath = join(repoRoot, "dist", "cli.js"); +const packageJsonPath = fileURLToPath(new URL("../package.json", import.meta.url)); +const repoRoot = dirname(packageJsonPath); +const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8")) as { + bin: { devspace: string }; +}; +// This verifies the compiled entrypoint declared for the installed `devspace` +// command. npm's package-install shim itself is outside this focused test. +const cliPath = join(repoRoot, packageJson.bin.devspace); const tscPath = require.resolve("typescript/bin/tsc"); test("show-changes prints a Git-backed historical review", async (t) => { From 0ac5c7a319a7dc2882e4c88e3d0b94659e9cfbc7 Mon Sep 17 00:00:00 2001 From: Waishnav <86405648+Waishnav@users.noreply.github.com> Date: Sat, 29 Aug 2026 10:58:03 +0530 Subject: [PATCH 42/75] fix(ui): use theme-specific provider logos --- .../assets/provider-logos/copilot-light.svg | 1 + src/ui/assets/provider-logos/cursor-light.svg | 1 + src/ui/assets/provider-logos/openai-light.svg | 1 + .../assets/provider-logos/opencode-dark.svg | 2 +- .../assets/provider-logos/opencode-light.svg | 1 + src/ui/assets/provider-logos/pi-on-light.svg | 1 + src/ui/icons.ts | 39 +++++++++++++++---- src/ui/workspace-app.tsx | 24 ++++++++++-- 8 files changed, 57 insertions(+), 13 deletions(-) create mode 100644 src/ui/assets/provider-logos/copilot-light.svg create mode 100644 src/ui/assets/provider-logos/cursor-light.svg create mode 100644 src/ui/assets/provider-logos/openai-light.svg create mode 100644 src/ui/assets/provider-logos/opencode-light.svg create mode 100644 src/ui/assets/provider-logos/pi-on-light.svg diff --git a/src/ui/assets/provider-logos/copilot-light.svg b/src/ui/assets/provider-logos/copilot-light.svg new file mode 100644 index 000000000..64c546390 --- /dev/null +++ b/src/ui/assets/provider-logos/copilot-light.svg @@ -0,0 +1 @@ + diff --git a/src/ui/assets/provider-logos/cursor-light.svg b/src/ui/assets/provider-logos/cursor-light.svg new file mode 100644 index 000000000..d0cd849c9 --- /dev/null +++ b/src/ui/assets/provider-logos/cursor-light.svg @@ -0,0 +1 @@ + diff --git a/src/ui/assets/provider-logos/openai-light.svg b/src/ui/assets/provider-logos/openai-light.svg new file mode 100644 index 000000000..7aff9250b --- /dev/null +++ b/src/ui/assets/provider-logos/openai-light.svg @@ -0,0 +1 @@ + diff --git a/src/ui/assets/provider-logos/opencode-dark.svg b/src/ui/assets/provider-logos/opencode-dark.svg index 62e10df44..0bdfe0890 100644 --- a/src/ui/assets/provider-logos/opencode-dark.svg +++ b/src/ui/assets/provider-logos/opencode-dark.svg @@ -1 +1 @@ - + diff --git a/src/ui/assets/provider-logos/opencode-light.svg b/src/ui/assets/provider-logos/opencode-light.svg new file mode 100644 index 000000000..8a2743757 --- /dev/null +++ b/src/ui/assets/provider-logos/opencode-light.svg @@ -0,0 +1 @@ + diff --git a/src/ui/assets/provider-logos/pi-on-light.svg b/src/ui/assets/provider-logos/pi-on-light.svg new file mode 100644 index 000000000..5472a94ee --- /dev/null +++ b/src/ui/assets/provider-logos/pi-on-light.svg @@ -0,0 +1 @@ + diff --git a/src/ui/icons.ts b/src/ui/icons.ts index 8bada4133..8fcb11c4a 100644 --- a/src/ui/icons.ts +++ b/src/ui/icons.ts @@ -34,17 +34,40 @@ export const toolIcons = { export type ToolIcon = IconNode; const providerLogos = { - claude: new URL("./assets/provider-logos/claude.svg", import.meta.url).href, - codex: new URL("./assets/provider-logos/openai-dark.svg", import.meta.url).href, - copilot: new URL("./assets/provider-logos/copilot-dark.svg", import.meta.url).href, - cursor: new URL("./assets/provider-logos/cursor-dark.svg", import.meta.url).href, - opencode: new URL("./assets/provider-logos/opencode-dark.svg", import.meta.url).href, - pi: new URL("./assets/provider-logos/pi-on-dark.svg", import.meta.url).href, + claude: { + light: new URL("./assets/provider-logos/claude.svg", import.meta.url).href, + dark: new URL("./assets/provider-logos/claude.svg", import.meta.url).href, + }, + codex: { + light: new URL("./assets/provider-logos/openai-light.svg", import.meta.url).href, + dark: new URL("./assets/provider-logos/openai-dark.svg", import.meta.url).href, + }, + copilot: { + light: new URL("./assets/provider-logos/copilot-light.svg", import.meta.url).href, + dark: new URL("./assets/provider-logos/copilot-dark.svg", import.meta.url).href, + }, + cursor: { + light: new URL("./assets/provider-logos/cursor-light.svg", import.meta.url).href, + dark: new URL("./assets/provider-logos/cursor-dark.svg", import.meta.url).href, + }, + opencode: { + light: new URL("./assets/provider-logos/opencode-light.svg", import.meta.url).href, + dark: new URL("./assets/provider-logos/opencode-dark.svg", import.meta.url).href, + }, + pi: { + light: new URL("./assets/provider-logos/pi-on-light.svg", import.meta.url).href, + dark: new URL("./assets/provider-logos/pi-on-dark.svg", import.meta.url).href, + }, } as const; -export function getProviderLogo(name: string): string | undefined { +export type ProviderLogoTheme = "light" | "dark"; + +export function getProviderLogo( + name: string, + theme: ProviderLogoTheme = "dark", +): string | undefined { const normalizedName = name.trim().toLowerCase() as keyof typeof providerLogos; - return providerLogos[normalizedName]; + return providerLogos[normalizedName]?.[theme]; } export function renderIcon(icon: ToolIcon, className = "icon-svg"): SVGElement { diff --git a/src/ui/workspace-app.tsx b/src/ui/workspace-app.tsx index bc101cb37..9dec916df 100644 --- a/src/ui/workspace-app.tsx +++ b/src/ui/workspace-app.tsx @@ -12,7 +12,13 @@ import { type HostContext, type ToolResultCard, } from "./card-types.js"; -import { getProviderLogo, renderIcon, toolIcons, type ToolIcon } from "./icons.js"; +import { + getProviderLogo, + renderIcon, + toolIcons, + type ProviderLogoTheme, + type ToolIcon, +} from "./icons.js"; import { getFileChangePathDisplay, getPatchDisplayParts, @@ -83,6 +89,7 @@ async function boot(): Promise { }; app.onhostcontextchanged = (ctx) => { + const previousTheme = hostContext?.theme; hostContext = { ...hostContext, ...ctx, @@ -90,7 +97,11 @@ async function boot(): Promise { applyHostContext(); // Workspace details inherit host variables directly. Rebuilding their DOM on // iframe resize would reset an in-progress instruction preview interaction. - if (card?.tool !== "open_workspace") renderPayloadIfNeeded(); + if (card?.tool === "open_workspace") { + if (ctx.theme && ctx.theme !== previousTheme) render(); + } else { + renderPayloadIfNeeded(); + } }; app.onteardown = async () => { @@ -583,6 +594,9 @@ function renderWorkspacePayload(container: HTMLElement, card: ToolResultCard): v const providers = card.agentProviders ?? []; const agents = card.agents ?? []; + const providerLogoTheme: ProviderLogoTheme = hostContext?.theme === "light" + ? "light" + : "dark"; const agentChips: WorkspaceChip[] = agents.map((agent) => { const name = agent.name ?? "Unnamed agent"; const providerName = agent.provider?.trim(); @@ -594,14 +608,16 @@ function renderWorkspacePayload(container: HTMLElement, card: ToolResultCard): v ].filter((value): value is string => Boolean(value)).join("\n"); return { label: name, - logo: providerName ? getProviderLogo(providerName) : undefined, + logo: providerName + ? getProviderLogo(providerName, providerLogoTheme) + : undefined, profile: true, title: title || undefined, }; }); const providerChips: WorkspaceChip[] = providers.map((provider) => { const name = provider.id?.trim() || "Unknown provider"; - const logo = getProviderLogo(name); + const logo = getProviderLogo(name, providerLogoTheme); const title = [ provider.model ? `Model: ${provider.model}` : undefined, provider.effort ? `Effort: ${provider.effort}` : undefined, From 0f2548db093707934d2749fa7b7c6c6bc31b68bb Mon Sep 17 00:00:00 2001 From: Waishnav <86405648+Waishnav@users.noreply.github.com> Date: Sat, 29 Aug 2026 11:03:21 +0530 Subject: [PATCH 43/75] chore(ui): refresh official provider assets --- src/ui/assets/provider-logos/copilot-dark.svg | 2 +- src/ui/assets/provider-logos/copilot-light.svg | 2 +- src/ui/assets/provider-logos/cursor-dark.svg | 13 ++++++++++++- src/ui/assets/provider-logos/cursor-light.svg | 13 ++++++++++++- src/ui/assets/provider-logos/openai-dark.svg | 12 +++++++++++- src/ui/assets/provider-logos/openai-light.svg | 12 +++++++++++- 6 files changed, 48 insertions(+), 6 deletions(-) diff --git a/src/ui/assets/provider-logos/copilot-dark.svg b/src/ui/assets/provider-logos/copilot-dark.svg index d09df8056..275b83930 100644 --- a/src/ui/assets/provider-logos/copilot-dark.svg +++ b/src/ui/assets/provider-logos/copilot-dark.svg @@ -1 +1 @@ - + diff --git a/src/ui/assets/provider-logos/copilot-light.svg b/src/ui/assets/provider-logos/copilot-light.svg index 64c546390..b52d96cff 100644 --- a/src/ui/assets/provider-logos/copilot-light.svg +++ b/src/ui/assets/provider-logos/copilot-light.svg @@ -1 +1 @@ - + diff --git a/src/ui/assets/provider-logos/cursor-dark.svg b/src/ui/assets/provider-logos/cursor-dark.svg index d50421b51..6849fbc32 100644 --- a/src/ui/assets/provider-logos/cursor-dark.svg +++ b/src/ui/assets/provider-logos/cursor-dark.svg @@ -1 +1,12 @@ - + + + + + + + + diff --git a/src/ui/assets/provider-logos/cursor-light.svg b/src/ui/assets/provider-logos/cursor-light.svg index d0cd849c9..b054b189c 100644 --- a/src/ui/assets/provider-logos/cursor-light.svg +++ b/src/ui/assets/provider-logos/cursor-light.svg @@ -1 +1,12 @@ - + + + + + + + + diff --git a/src/ui/assets/provider-logos/openai-dark.svg b/src/ui/assets/provider-logos/openai-dark.svg index 7e19c92d2..cd86afb59 100644 --- a/src/ui/assets/provider-logos/openai-dark.svg +++ b/src/ui/assets/provider-logos/openai-dark.svg @@ -1 +1,11 @@ - + + + + + + + + + + + diff --git a/src/ui/assets/provider-logos/openai-light.svg b/src/ui/assets/provider-logos/openai-light.svg index 7aff9250b..a57ca0dab 100644 --- a/src/ui/assets/provider-logos/openai-light.svg +++ b/src/ui/assets/provider-logos/openai-light.svg @@ -1 +1,11 @@ - + + + + + + + + + + + From 16fada9820789d29b710f3881abdd31f81e895f0 Mon Sep 17 00:00:00 2001 From: Waishnav <86405648+Waishnav@users.noreply.github.com> Date: Sat, 29 Aug 2026 11:05:41 +0530 Subject: [PATCH 44/75] fix(ui): preserve workspace state on theme changes --- src/ui/workspace-app.tsx | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/ui/workspace-app.tsx b/src/ui/workspace-app.tsx index 9dec916df..27d4f5b88 100644 --- a/src/ui/workspace-app.tsx +++ b/src/ui/workspace-app.tsx @@ -98,7 +98,9 @@ async function boot(): Promise { // Workspace details inherit host variables directly. Rebuilding their DOM on // iframe resize would reset an in-progress instruction preview interaction. if (card?.tool === "open_workspace") { - if (ctx.theme && ctx.theme !== previousTheme) render(); + if (ctx.theme && ctx.theme !== previousTheme) { + syncWorkspaceProviderLogos(ctx.theme === "light" ? "light" : "dark"); + } } else { renderPayloadIfNeeded(); } @@ -611,6 +613,7 @@ function renderWorkspacePayload(container: HTMLElement, card: ToolResultCard): v logo: providerName ? getProviderLogo(providerName, providerLogoTheme) : undefined, + logoProvider: providerName, profile: true, title: title || undefined, }; @@ -626,6 +629,7 @@ function renderWorkspacePayload(container: HTMLElement, card: ToolResultCard): v return { label: name, logo, + logoProvider: logo ? name : undefined, bareLogo: Boolean(logo), ariaLabel: name, title: title || name, @@ -652,6 +656,7 @@ function renderWorkspacePayload(container: HTMLElement, card: ToolResultCard): v interface WorkspaceChip { label: string; logo?: string; + logoProvider?: string; profile?: boolean; bareLogo?: boolean; ariaLabel?: string; @@ -929,6 +934,7 @@ function renderWorkspaceChips(chips: WorkspaceChip[]): HTMLElement { ? "workspace-agent-profile-logo" : "workspace-chip-logo"; logo.src = chip.logo; + if (chip.logoProvider) logo.dataset.provider = chip.logoProvider; logo.alt = ""; logo.setAttribute("aria-hidden", "true"); item.append(logo); @@ -941,6 +947,15 @@ function renderWorkspaceChips(chips: WorkspaceChip[]): HTMLElement { return list; } +function syncWorkspaceProviderLogos(theme: ProviderLogoTheme): void { + for (const logo of document.querySelectorAll("img[data-provider]")) { + const providerName = logo.dataset.provider; + if (!providerName) continue; + const src = getProviderLogo(providerName, theme); + if (src && logo.src !== src) logo.src = src; + } +} + function element( tag: K, options: { From e4ef98997aa82a7a59fd0a820809409337cd8bce Mon Sep 17 00:00:00 2001 From: Waishnav <86405648+Waishnav@users.noreply.github.com> Date: Sat, 29 Aug 2026 12:47:05 +0530 Subject: [PATCH 45/75] Delete docs/assets/v11-review-ui-interaction.mp4 --- docs/assets/v11-review-ui-interaction.mp4 | Bin 39984 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 docs/assets/v11-review-ui-interaction.mp4 diff --git a/docs/assets/v11-review-ui-interaction.mp4 b/docs/assets/v11-review-ui-interaction.mp4 deleted file mode 100644 index 3d5b422ddae23cbce30474b8dac8e2b96d2ec362..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 39984 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

qJ0-tsE3|sBe!|YaokpUK#PMjwv?a;9z)a4$ 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 Date: Mon, 31 Aug 2026 05:20:39 +0530 Subject: [PATCH 46/75] fix(claude): scope restricted agent authority --- src/local-agent-claude.test.ts | 24 +++++++++---- src/local-agent-claude.ts | 61 +++++++++------------------------- 2 files changed, 32 insertions(+), 53 deletions(-) diff --git a/src/local-agent-claude.test.ts b/src/local-agent-claude.test.ts index 183d83ef1..e8b3d506b 100644 --- a/src/local-agent-claude.test.ts +++ b/src/local-agent-claude.test.ts @@ -121,6 +121,8 @@ assert.equal(query?.model, "sonnet"); assert.equal(lastOptions?.resume, undefined); assert.equal(lastOptions?.permissionMode, "dontAsk"); assert.equal(lastOptions?.allowDangerouslySkipPermissions, undefined); +assert.deepEqual(lastOptions?.allowedTools, ["Read(/**)", "Edit(/**)", "Bash"]); +assert.equal(lastOptions?.pathToClaudeCodeExecutable, undefined); const initialSandbox = lastOptions?.sandbox as Record; assert.equal(initialSandbox.enabled, true); assert.equal(initialSandbox.failIfUnavailable, true); @@ -131,17 +133,15 @@ assert.deepEqual((initialSandbox.filesystem as Record).denyWrit const allowedSettings = claudeAuthoritySettings("/tmp/project", "allowed"); const allowedPermissions = allowedSettings.permissions as Record; const allowedSandbox = allowedSettings.sandbox as Record; -assert.ok((allowedPermissions.allow as string[]).includes("Bash(*)")); +assert.deepEqual(allowedPermissions.deny, []); assert.deepEqual(allowedSandbox.filesystem, { allowWrite: ["/tmp/project"], denyWrite: [], - denyRead: (allowedSandbox.filesystem as Record).denyRead, - allowRead: ["/tmp/project"], }); const readOnlySettings = claudeAuthoritySettings("/tmp/project", "read_only"); const readOnlyPermissions = readOnlySettings.permissions as Record; -assert.equal((readOnlyPermissions.allow as string[]).some((rule) => rule.startsWith("Edit(")), false); -assert.ok((readOnlyPermissions.deny as string[]).includes("Bash(*)")); +assert.ok((readOnlyPermissions.deny as string[]).includes("Bash")); +assert.ok((readOnlyPermissions.deny as string[]).includes("Edit")); assert.deepEqual( ((readOnlySettings.sandbox as Record).filesystem as Record).allowWrite, [], @@ -161,8 +161,9 @@ assert.equal( "dontAsk", ); assert.equal(query?.flagSettings[1]?.effortLevel, "low"); -assert.ok( - ((query?.flagSettings[1]?.permissions as Record).allow as string[]).includes("Bash(*)"), +assert.equal( + ((query?.flagSettings[1]?.permissions as Record).deny as string[]).includes("Edit"), + false, ); assert.equal( (query?.flagSettings[2]?.permissions as Record).defaultMode, @@ -177,6 +178,15 @@ const coldRuntime = await driver.createRuntime({ ...context, providerSessionId: assert.equal(coldRuntime.isOk(), true); assert.equal(lastOptions?.resume, "cold_session"); +const customCommandDriver = new ClaudeLocalAgentDriver(({ prompt, options }) => { + lastOptions = options; + return new FakeClaudeQuery(prompt); +}, { CLAUDE_COMMAND: "/opt/claude" }); +const customCommandRuntime = await customCommandDriver.createRuntime(context); +assert.equal(customCommandRuntime.isOk(), true); +assert.equal(lastOptions?.pathToClaudeCodeExecutable, "/opt/claude"); +if (customCommandRuntime.isOk()) await customCommandRuntime.value.close(); + const cancelled = await new ClaudeLocalAgentDriver(async () => { throw new DOMException("cancelled", "AbortError"); }).createRuntime(context); diff --git a/src/local-agent-claude.ts b/src/local-agent-claude.ts index 16feed692..a639f2f46 100644 --- a/src/local-agent-claude.ts +++ b/src/local-agent-claude.ts @@ -1,6 +1,3 @@ -import { spawnSync } from "node:child_process"; -import { homedir } from "node:os"; -import { join } from "node:path"; import { AgentProviderExecutionError, AgentProviderProtocolError, @@ -21,6 +18,14 @@ import type { type ClaudePermissionMode = "default" | "acceptEdits" | "bypassPermissions" | "plan" | "dontAsk" | "auto"; +const CLAUDE_WORKSPACE_ALLOWED_TOOLS = [ + // allowedTools is passed as a session/CLI rule, so `/` is anchored to the query cwd. + "Read(/**)", + "Edit(/**)", + "Bash", +] as const; + + export interface ClaudeQueryLike extends AsyncIterable { close(): void; setPermissionMode(mode: ClaudePermissionMode): Promise; @@ -262,7 +267,7 @@ export function claudeQueryOptions( input: LocalAgentRunInput, env: NodeJS.ProcessEnv = process.env, ): Record { - const executable = env.CLAUDE_COMMAND ?? resolveExecutable("claude", env); + const executable = env.CLAUDE_COMMAND; const permissionMode = claudePermissionMode(input.writeMode); const authority = claudeAuthorityOptions(input.workspaceRoot, input.writeMode); return { @@ -271,6 +276,11 @@ export function claudeQueryOptions( ...(input.effort ? { thinking: { type: "adaptive" }, effort: input.effort } : {}), ...(context.providerSessionId ? { resume: context.providerSessionId } : {}), permissionMode, + // Restricted runtimes stay warm across read_only/allowed turns. Keep the + // workspace capabilities static and narrow individual turns with deny rules. + ...(input.writeMode === "full_access" + ? {} + : { allowedTools: [...CLAUDE_WORKSPACE_ALLOWED_TOOLS] }), sandbox: authority.sandbox, settings: authority.settings, ...(input.writeMode === "full_access" ? { allowDangerouslySkipPermissions: true } : {}), @@ -316,25 +326,10 @@ function claudeAuthorityOptions( }; } - const resolvedWorkspace = workspaceRoot.replaceAll("\\", "/"); - const workspaceRules = [ - `Read(${resolvedWorkspace}/**)`, - `Glob(${resolvedWorkspace}/**)`, - `Grep(${resolvedWorkspace}/**)`, - `LS(${resolvedWorkspace}/**)`, - ]; const allowed = writeMode !== "read_only"; - const protectedPaths = claudeProtectedPaths(); const permissions = { defaultMode: "dontAsk", - allow: [ - ...workspaceRules, - ...(allowed ? [`Edit(${resolvedWorkspace}/**)`, "Bash(*)"] : []), - ], - deny: [ - ...protectedPaths.map((path) => `Read(${path.replaceAll("\\", "/")}/**)`), - ...(allowed ? [] : ["Bash(*)", "Edit(*)", "Write(*)", "NotebookEdit(*)"]), - ], + deny: allowed ? [] : ["Bash", "Edit"], }; const sandbox = { enabled: true, @@ -344,25 +339,11 @@ function claudeAuthorityOptions( filesystem: { allowWrite: allowed ? [workspaceRoot] : [], denyWrite: allowed ? [] : [workspaceRoot], - denyRead: protectedPaths, - allowRead: [workspaceRoot], }, }; return { sandbox, settings: { permissions, sandbox } }; } -function claudeProtectedPaths(): string[] { - const home = homedir(); - return [ - join(home, ".ssh"), - join(home, ".aws"), - join(home, ".gnupg"), - join(home, ".config", "gcloud"), - join(home, ".netrc"), - join(home, ".npmrc"), - ]; -} - export function claudeCommandEnvironment(env: NodeJS.ProcessEnv): NodeJS.ProcessEnv { const next = { ...env }; for (const key of [ @@ -395,18 +376,6 @@ export interface ClaudeUserMessage { parent_tool_use_id: null; } -function resolveExecutable(command: string, env: NodeJS.ProcessEnv): string | undefined { - const commandHasPath = command.includes("/") || command.includes("\\"); - if (commandHasPath) return command; - const result = spawnSync(process.platform === "win32" ? "where.exe" : "which", [command], { - encoding: "utf8", - env, - windowsHide: true, - }); - const executable = result.stdout?.split(/\r?\n/).find((line) => line.trim()); - return executable?.trim() || undefined; -} - function directString(value: unknown): string | undefined { return typeof value === "string" && value.trim() ? value.trim() : undefined; } From 802be135119be0e04018c81577e3b3227b47f5a0 Mon Sep 17 00:00:00 2001 From: Waishnav <86405648+Waishnav@users.noreply.github.com> Date: Mon, 31 Aug 2026 05:20:39 +0530 Subject: [PATCH 47/75] chore(claude): pin agent SDK runtime --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 13d8c3622..0821d3d53 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "license": "MIT", "dependencies": { "@agentclientprotocol/sdk": "^1.1.0", - "@anthropic-ai/claude-agent-sdk": "^0.3.200", + "@anthropic-ai/claude-agent-sdk": "0.3.200", "@anthropic-ai/sandbox-runtime": "0.0.71", "@clack/prompts": "^1.5.1", "@earendil-works/pi-coding-agent": "^0.80.3", diff --git a/package.json b/package.json index 4c64031c5..6889caffb 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "license": "MIT", "dependencies": { "@agentclientprotocol/sdk": "^1.1.0", - "@anthropic-ai/claude-agent-sdk": "^0.3.200", + "@anthropic-ai/claude-agent-sdk": "0.3.200", "@anthropic-ai/sandbox-runtime": "0.0.71", "@clack/prompts": "^1.5.1", "@earendil-works/pi-coding-agent": "^0.80.3", From dd0381a68eddc00cbd79f9b90448a47c08a3e11f Mon Sep 17 00:00:00 2001 From: Waishnav <86405648+Waishnav@users.noreply.github.com> Date: Mon, 31 Aug 2026 05:30:15 +0530 Subject: [PATCH 48/75] fix(ui): use one vertical scrollbar for change review --- src/ui/workspace-app.css | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/ui/workspace-app.css b/src/ui/workspace-app.css index 906cbeb65..92dc4bf2e 100644 --- a/src/ui/workspace-app.css +++ b/src/ui/workspace-app.css @@ -654,7 +654,7 @@ body { } .review-diff-file { - overflow: hidden; + overflow: clip; border: 0; border-radius: 0; } @@ -664,6 +664,9 @@ body { } .review-diff-file-header { + position: sticky; + top: 0; + z-index: 2; display: grid; grid-template-columns: 22px minmax(0, 1fr) auto; align-items: center; @@ -672,7 +675,7 @@ body { min-height: 42px; padding: 0 12px; border: 0; - background: transparent; + background: var(--tool-card-body-bg); color: var(--color-text-primary, #f5f5f6); cursor: pointer; font: inherit; @@ -711,7 +714,9 @@ body { } .review-single-file { - overflow: hidden; + max-height: 520px; + overflow-x: hidden; + overflow-y: auto; } .review-diff-file-header:hover { @@ -791,8 +796,7 @@ body { --diffs-font-size: var(--font-text-sm-size, 12px); --diffs-line-height: 20px; display: block; - max-height: 420px; - overflow: auto; + overflow: visible; border-bottom-right-radius: 8px; border-bottom-left-radius: 8px; } From 09a9969b27545765503d3018b56047f1dd6043ee Mon Sep 17 00:00:00 2001 From: Waishnav <86405648+Waishnav@users.noreply.github.com> Date: Mon, 31 Aug 2026 06:18:34 +0530 Subject: [PATCH 49/75] fix: ship executable CLI launchers --- bin/devspace-agentd.js | 2 ++ bin/devspace.js | 2 ++ package.json | 5 +++-- 3 files changed, 7 insertions(+), 2 deletions(-) create mode 100755 bin/devspace-agentd.js create mode 100755 bin/devspace.js diff --git a/bin/devspace-agentd.js b/bin/devspace-agentd.js new file mode 100755 index 000000000..268c36097 --- /dev/null +++ b/bin/devspace-agentd.js @@ -0,0 +1,2 @@ +#!/usr/bin/env node +import "../dist/local-agent-daemon-main.js"; diff --git a/bin/devspace.js b/bin/devspace.js new file mode 100755 index 000000000..8fb127218 --- /dev/null +++ b/bin/devspace.js @@ -0,0 +1,2 @@ +#!/usr/bin/env node +import "../dist/cli.js"; diff --git a/package.json b/package.json index 4c64031c5..28998c9c2 100644 --- a/package.json +++ b/package.json @@ -8,10 +8,11 @@ "node": ">=22.19 <27" }, "bin": { - "devspace": "dist/cli.js", - "devspace-agentd": "dist/local-agent-daemon-main.js" + "devspace": "bin/devspace.js", + "devspace-agentd": "bin/devspace-agentd.js" }, "files": [ + "bin", "dist", "docs", "examples", From b9ea8bdf2f6b2e2bf59bfe0393b4356db6e144ec Mon Sep 17 00:00:00 2001 From: Waishnav <86405648+Waishnav@users.noreply.github.com> Date: Mon, 31 Aug 2026 06:33:14 +0530 Subject: [PATCH 50/75] fix(claude): expose general shell execution --- src/tool-surfaces/claude.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/tool-surfaces/claude.ts b/src/tool-surfaces/claude.ts index e1b98858b..4fc21f221 100644 --- a/src/tool-surfaces/claude.ts +++ b/src/tool-surfaces/claude.ts @@ -22,7 +22,7 @@ import { textBlock, } from "./shared.js"; -const CLAUDE_INSTRUCTIONS = `Use ${toolNames.read} for direct file reads, ${toolNames.shell} with command-line tools such as rg, find, ls, and tree for search and directory inspection, ${toolNames.edit} for targeted modifications, and ${toolNames.write} only for new files or complete rewrites. Use ${toolNames.shell} for tests, builds, git inspection, package scripts, and other commands, but do not create or modify files through shell commands. Shell commands run with the local user's authority and are not sandboxed; workspace validation only selects their initial working directory. Follow instructions returned by ${toolNames.openWorkspace}; read applicable instruction and skill files before working in their scope.`; +const CLAUDE_INSTRUCTIONS = `Use ${toolNames.read} for direct file reads, ${toolNames.edit} for targeted modifications, ${toolNames.write} only for new files or complete rewrites, and ${toolNames.shell} for inspection, tests, builds, and other commands. Shell commands run with the local user's authority and are not sandboxed; workspace validation only selects their initial working directory. Follow instructions returned by ${toolNames.openWorkspace}; read applicable instruction and skill files before working in their scope.`; export function claudeInstructions({ agents, @@ -36,7 +36,7 @@ export function registerClaudeTools(context: ToolRegistrationContext): void { registerShellTool(context); } -const CLAUDE_SHELL_DESCRIPTION = `Run a shell command in a workspace with the local user's authority. Commands are not sandboxed; workspace validation only selects the initial working directory. Use it for tests, builds, git inspection, package scripts, search, file discovery, and directory inspection. Do not use ${toolNames.shell} to create or modify files. Do not use shell redirection, heredocs, tee, sed -i, perl -i, node/python/ruby scripts, or generated scripts to write project files; use ${toolNames.edit} for targeted changes and ${toolNames.write} for new files or full rewrites. Prefer ${toolNames.read} for direct file reads. This is powerful execution and should only be exposed behind strong authentication.`; +const CLAUDE_SHELL_DESCRIPTION = `Run a shell command with the local user's authority. Commands are not sandboxed; workspace validation only selects the initial working directory. Use this for file inspection, tests, builds, package scripts, and other commands.`; function registerClaudeMutationTools(context: ToolRegistrationContext): void { const { server, config, workspaces } = context; @@ -183,9 +183,7 @@ function registerShellTool(context: ToolRegistrationContext): void { workspaceId: z.string().describe(workspaceIdDescription), command: z .string() - .describe( - `Shell command to run. Must not create or modify project files; use ${toolNames.edit} or ${toolNames.write} for file changes.`, - ), + .describe("Shell command to execute."), workingDirectory: z .string() .optional() From 7b57d7051220999fd8706090c530fb9177426025 Mon Sep 17 00:00:00 2001 From: Waishnav <86405648+Waishnav@users.noreply.github.com> Date: Mon, 31 Aug 2026 06:45:51 +0530 Subject: [PATCH 51/75] fix: make npm package self-contained --- package-lock.json | 4 ++-- package.json | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 13d8c3622..416b5972f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -34,8 +34,8 @@ "zod": "^4.4.3" }, "bin": { - "devspace": "dist/cli.js", - "devspace-agentd": "dist/local-agent-daemon-main.js" + "devspace": "bin/devspace.js", + "devspace-agentd": "bin/devspace-agentd.js" }, "devDependencies": { "@types/better-sqlite3": "^7.6.13", diff --git a/package.json b/package.json index 28998c9c2..d49712741 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "build:app": "vite build", "dev": "node scripts/dev-server.mjs", "postinstall": "node scripts/fix-node-pty-permissions.mjs", + "prepack": "npm run build", "schema:config": "tsx scripts/generate-config-schema.ts", "start": "node dist/cli.js serve", "test": "tsx src/user-config.test.ts && tsx src/config.test.ts && tsx src/onboarding.test.ts && tsx src/cli-workspace.test.ts && tsx src/request-meta.test.ts && tsx src/incoming-artifacts.test.ts && tsx src/artifact-download.test.ts && tsx src/ui/card-types.test.ts && tsx src/ui/tool-result.test.ts && tsx src/ui/patch-display.test.ts && tsx src/apply-patch.test.ts && tsx src/process-platform.test.ts && tsx src/process-sessions.test.ts && tsx src/mcp-sessions.test.ts && tsx src/server-shutdown.test.ts && tsx src/local-agent-config.test.ts && tsx src/local-agent-catalog.test.ts && tsx src/local-agent-presentation.test.ts && tsx src/local-agent-runtime.test.ts && tsx src/local-agent-daemon-lifecycle.test.ts && tsx src/local-agent-daemon-protocol.test.ts && tsx src/local-agent-daemon.test.ts && tsx src/local-agent-codex.test.ts && tsx src/local-agent-opencode.test.ts && tsx src/local-agent-acp.test.ts && tsx src/local-agent-grok.test.ts && tsx src/local-agent-pi-sandbox.test.ts && tsx src/local-agent-pi.test.ts && tsx src/local-agent-claude.test.ts && tsx src/local-agent-adapters.test.ts && tsx src/local-agent-availability.test.ts && tsx src/local-agent-profiles.test.ts && tsx src/local-agent-targets.test.ts && tsx src/local-agent-store.test.ts && tsx src/local-agent-manager.test.ts && tsx src/roots.test.ts && tsx src/skills.test.ts && tsx src/workspaces.test.ts && tsx src/workspace-conversation.test.ts && tsx src/review-checkpoints.test.ts && tsx src/server.test.ts && tsx src/oauth-store.test.ts && tsx src/cli-show-changes.test.ts && tsx src/cli.test.ts", From 8a28dbacf87bed8cb310d272eff66725f7dab308 Mon Sep 17 00:00:00 2001 From: Waishnav <86405648+Waishnav@users.noreply.github.com> Date: Mon, 31 Aug 2026 07:09:49 +0530 Subject: [PATCH 52/75] test: remove test-only helper coverage --- src/local-agent-adapters.test.ts | 19 ------------------- src/local-agent-adapters.ts | 17 ----------------- src/local-agent-availability.test.ts | 9 --------- src/local-agent-availability.ts | 19 ------------------- src/local-agent-config.test.ts | 12 +++++------- src/local-agent-config.ts | 8 -------- src/local-agent-daemon-lifecycle.test.ts | 2 -- src/local-agent-daemon-lifecycle.ts | 4 ---- src/local-agent-profiles.test.ts | 10 +--------- src/local-agent-profiles.ts | 12 ------------ src/local-agent-targets.test.ts | 3 --- src/local-agent-targets.ts | 9 --------- 12 files changed, 6 insertions(+), 118 deletions(-) diff --git a/src/local-agent-adapters.test.ts b/src/local-agent-adapters.test.ts index 4938bd0bd..395072a1d 100644 --- a/src/local-agent-adapters.test.ts +++ b/src/local-agent-adapters.test.ts @@ -2,7 +2,6 @@ import assert from "node:assert/strict"; import { delimiter } from "node:path"; import { claudeCommandEnvironment, - createLocalAgentAdapter, extractOpenCodeFinalResponse, extractPiFinalResponse, extractPiProviderError, @@ -10,24 +9,6 @@ import { resolveAcpEffortConfigUpdate, } from "./local-agent-adapters.js"; import { removeDevspaceNodeModulesBinFromPath } from "./local-agent-path.js"; -import type { LocalAgentProvider } from "./local-agent-profiles.js"; - -const providers: LocalAgentProvider[] = [ - "codex", - "claude", - "opencode", - "pi", - "cursor", - "copilot", - "grok", -]; - -for (const provider of providers) { - const adapter = createLocalAgentAdapter(provider); - assert.equal(adapter.provider, provider); - assert.equal(typeof adapter.runtimeKey, "function"); -} - assert.deepEqual( resolveAcpModelConfigUpdate({ sessionId: "session_model_1", diff --git a/src/local-agent-adapters.ts b/src/local-agent-adapters.ts index 2b84c58b4..03a5cc40c 100644 --- a/src/local-agent-adapters.ts +++ b/src/local-agent-adapters.ts @@ -1,4 +1,3 @@ -import type { LocalAgentProvider } from "./local-agent-profiles.js"; import { AcpLocalAgentDriver, resolveAcpCommand, @@ -47,22 +46,6 @@ export function createLocalAgentDrivers( ]; } -export function createLocalAgentAdapter( - provider: LocalAgentProvider, - options: LocalAgentDriverOptions = {}, -): LocalAgentDriver { - switch (provider) { - case "codex": return new CodexLocalAgentDriver(options.env); - case "claude": return new ClaudeLocalAgentDriver(options.claudeQueryFactory, options.env); - case "opencode": return new OpencodeLocalAgentDriver(options.opencodeFactory); - case "pi": return new PiLocalAgentDriver(options.piSessionFactory); - case "cursor": - case "copilot": - case "grok": - return new AcpLocalAgentDriver(provider, options.env); - } -} - export function extractLocalAgentResponseText(value: unknown): string { return extractOpenCodeFinalResponse(value) || extractPiFinalResponse(value); } diff --git a/src/local-agent-availability.test.ts b/src/local-agent-availability.test.ts index 49d88aad5..08a73a10e 100644 --- a/src/local-agent-availability.test.ts +++ b/src/local-agent-availability.test.ts @@ -1,7 +1,6 @@ import assert from "node:assert/strict"; import { checkLocalAgentProviderAvailability, - formatLocalAgentProviderAvailabilitySummary, getLocalAgentProviderAvailabilitySnapshot, } from "./local-agent-availability.js"; @@ -38,11 +37,3 @@ import { ); assert.equal(snapshot.find((provider) => provider.name === "pi")?.available, true); } - -assert.equal( - formatLocalAgentProviderAvailabilitySummary([ - { name: "codex", available: true, note: "available" }, - { name: "pi", available: false, reason: "pi executable not found" }, - ]), - "available: codex (available); unavailable: pi (pi executable not found)", -); diff --git a/src/local-agent-availability.ts b/src/local-agent-availability.ts index 822ef6900..a021dbde9 100644 --- a/src/local-agent-availability.ts +++ b/src/local-agent-availability.ts @@ -51,21 +51,6 @@ export function assertLocalAgentProviderAvailable( ); } -export function formatLocalAgentProviderAvailabilitySummary( - providers: LocalAgentProviderAvailability[], -): string { - const available = providers - .filter((provider) => provider.available) - .map(formatAvailableProvider); - const unavailable = providers - .filter((provider) => !provider.available) - .map((provider) => `${provider.name} (${provider.reason ?? "unavailable"})`); - return [ - available.length > 0 ? `available: ${available.join(", ")}` : undefined, - unavailable.length > 0 ? `unavailable: ${unavailable.join(", ")}` : undefined, - ].filter(Boolean).join("; "); -} - function packageAvailability( provider: LocalAgentProvider, packageName: string, @@ -124,10 +109,6 @@ function resolveCommand(command: string, env: NodeJS.ProcessEnv): string | undef return undefined; } -function formatAvailableProvider(provider: LocalAgentProviderAvailability): string { - return provider.note ? `${provider.name} (${provider.note})` : provider.name; -} - function executableExists(command: string): boolean { const mode = process.platform === "win32" ? constants.F_OK : constants.X_OK; try { diff --git a/src/local-agent-config.test.ts b/src/local-agent-config.test.ts index 1751d0639..e37ceafac 100644 --- a/src/local-agent-config.test.ts +++ b/src/local-agent-config.test.ts @@ -1,11 +1,11 @@ import assert from "node:assert/strict"; import { isSubagentProviderEnabled, - resolveSubagentsConfig, subagentProviderConfig, + subagentsConfigSchema, } from "./local-agent-config.js"; -const config = resolveSubagentsConfig({ +const config = subagentsConfigSchema.parse({ enabled: true, providers: [ { id: "codex", enabled: true, model: " gpt-5.4 ", effort: " high " }, @@ -24,24 +24,22 @@ assert.equal(isSubagentProviderEnabled(config, "claude"), false); assert.equal(isSubagentProviderEnabled(config, "pi"), false); assert.equal(subagentProviderConfig(config, "codex")?.model, "gpt-5.4"); -assert.equal(resolveSubagentsConfig(undefined).providers.length, 0); - assert.throws( - () => resolveSubagentsConfig({ + () => subagentsConfigSchema.parse({ enabled: true, providers: [{ id: "codex", enabled: true }, { id: "codex", enabled: false }], }), /Duplicate subagent provider: codex/, ); assert.throws( - () => resolveSubagentsConfig({ + () => subagentsConfigSchema.parse({ enabled: true, providers: [{ id: "unknown", enabled: true }], }), /Invalid option/, ); assert.throws( - () => resolveSubagentsConfig({ + () => subagentsConfigSchema.parse({ enabled: true, providers: [{ id: "codex", enabled: true, effort: " " }], }), diff --git a/src/local-agent-config.ts b/src/local-agent-config.ts index e008788c7..62e9c35a0 100644 --- a/src/local-agent-config.ts +++ b/src/local-agent-config.ts @@ -37,14 +37,6 @@ export type SubagentProviderConfig = z.infer; export type SubagentsConfig = z.infer; export type StoredSubagentsConfig = z.infer; -export function resolveSubagentsConfig( - value: unknown, -): SubagentsConfig { - return value === undefined - ? { enabled: false, providers: [] } - : subagentsConfigSchema.parse(value); -} - export function subagentProviderConfig( config: SubagentsConfig, provider: LocalAgentProvider, diff --git a/src/local-agent-daemon-lifecycle.test.ts b/src/local-agent-daemon-lifecycle.test.ts index b4438cdcb..41eea2f23 100644 --- a/src/local-agent-daemon-lifecycle.test.ts +++ b/src/local-agent-daemon-lifecycle.test.ts @@ -10,7 +10,6 @@ import { localAgentDaemonPaths, removeLocalAgentDaemonFiles, ensureLocalAgentDaemonSecret, - writeLocalAgentDaemonPid, } from "./local-agent-daemon-lifecycle.js"; const root = await mkdtemp(join(tmpdir(), "devspace-agentd-lifecycle-test-")); @@ -38,7 +37,6 @@ try { const recovered = new LocalAgentDaemonLock(paths); recovered.acquire(); assert.equal(await readFile(paths.lockPath, "utf8"), `${process.pid}\n`); - writeLocalAgentDaemonPid(paths); assert.equal(await readFile(paths.pidPath, "utf8"), `${process.pid}\n`); assert.equal(isProcessAlive(process.pid), true); recovered.release(); diff --git a/src/local-agent-daemon-lifecycle.ts b/src/local-agent-daemon-lifecycle.ts index 3b573b914..df0b81b95 100644 --- a/src/local-agent-daemon-lifecycle.ts +++ b/src/local-agent-daemon-lifecycle.ts @@ -116,10 +116,6 @@ export class LocalAgentDaemonLock { } } -export function writeLocalAgentDaemonPid(paths: LocalAgentDaemonPaths): void { - writeFileSecure(paths.pidPath, `${process.pid}\n`); -} - export function ensureLocalAgentDaemonSecret(paths: LocalAgentDaemonPaths): string { ensureLocalAgentDaemonStateDir(paths.stateDir); try { diff --git a/src/local-agent-profiles.test.ts b/src/local-agent-profiles.test.ts index d3c5705f1..d802c17f3 100644 --- a/src/local-agent-profiles.test.ts +++ b/src/local-agent-profiles.test.ts @@ -3,7 +3,7 @@ import { mkdtemp, mkdir, rm, writeFile } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { loadConfig } from "./config.js"; -import { loadLocalAgentProfiles, summarizeLocalAgentProfile } from "./local-agent-profiles.js"; +import { loadLocalAgentProfiles } from "./local-agent-profiles.js"; import { writeTestDevspaceConfig } from "./test-support/config.test.js"; const root = await mkdtemp(join(tmpdir(), "devspace-agent-profiles-test-")); @@ -71,14 +71,6 @@ try { assert.equal(profiles[0]?.model, "sonnet"); assert.equal(profiles[0]?.effort, "high"); assert.equal(profiles[0]?.body, "Project body."); - assert.deepEqual(summarizeLocalAgentProfile(profiles[0]!), { - name: "reviewer", - description: "Project reviewer #1.", - provider: "claude", - model: "sonnet", - effort: "high", - }); - await writeFile( join(workspaceRoot, ".devspace", "agents", "custom.md"), [ diff --git a/src/local-agent-profiles.ts b/src/local-agent-profiles.ts index af532e49d..ad99a225c 100644 --- a/src/local-agent-profiles.ts +++ b/src/local-agent-profiles.ts @@ -67,18 +67,6 @@ export async function loadLocalAgentProfiles( .sort((a, b) => a.name.localeCompare(b.name)); } -export function summarizeLocalAgentProfile( - profile: LocalAgentProfile, -): LocalAgentProfileSummary { - return { - name: profile.name, - description: profile.description, - provider: profile.provider, - model: profile.model, - effort: profile.effort, - }; -} - async function loadProfilesFromDirectory(directory: string): Promise { const resolvedDirectory = resolve(directory); if (!existsSync(resolvedDirectory)) return []; diff --git a/src/local-agent-targets.test.ts b/src/local-agent-targets.test.ts index a3a70160f..fde0c000e 100644 --- a/src/local-agent-targets.test.ts +++ b/src/local-agent-targets.test.ts @@ -1,6 +1,5 @@ import assert from "node:assert/strict"; import { - formatAvailableLocalAgentTargets, parseLocalAgentRunArgs, resolveLocalAgentTarget, } from "./local-agent-targets.js"; @@ -147,5 +146,3 @@ assert.deepEqual(parseLocalAgentRunArgs(["codex", "--", "--json", "literal"]), { } assert.equal(resolveLocalAgentTarget("missing", profiles), undefined); -assert.match(formatAvailableLocalAgentTargets(profiles), /profiles: reviewer, claude/); -assert.match(formatAvailableLocalAgentTargets([]), /providers: codex, claude, opencode, pi, cursor, copilot, grok/); diff --git a/src/local-agent-targets.ts b/src/local-agent-targets.ts index 5c852f0e5..367b04761 100644 --- a/src/local-agent-targets.ts +++ b/src/local-agent-targets.ts @@ -156,12 +156,3 @@ export function resolveLocalAgentTarget( return undefined; } - -export function formatAvailableLocalAgentTargets(profiles: LocalAgentProfile[]): string { - const profileNames = profiles.map((profile) => profile.name); - const parts = [ - profileNames.length > 0 ? `profiles: ${profileNames.join(", ")}` : undefined, - `providers: ${LOCAL_AGENT_PROVIDERS.join(", ")}`, - ].filter(Boolean); - return parts.join("; "); -} From 59fbd08f1ff10e75a7822a6f70de362ccd0a11df Mon Sep 17 00:00:00 2001 From: Waishnav <86405648+Waishnav@users.noreply.github.com> Date: Mon, 31 Aug 2026 07:10:46 +0530 Subject: [PATCH 53/75] test: drop copy-only assertions --- src/cli.test.ts | 1 - src/local-agent-pi.test.ts | 8 -------- src/review-checkpoints.test.ts | 3 --- src/server.test.ts | 1 - src/ui/patch-display.test.ts | 17 ----------------- 5 files changed, 30 deletions(-) diff --git a/src/cli.test.ts b/src/cli.test.ts index 27c91da40..0983417c3 100644 --- a/src/cli.test.ts +++ b/src/cli.test.ts @@ -214,7 +214,6 @@ try { error: { code: string; message: string; retryable: boolean; target: string }; }; assert.equal(payload.error.code, "UNKNOWN_TARGET"); - assert.equal(payload.error.message, "Unknown subagent profile or provider: missing."); assert.equal(payload.error.retryable, false); assert.equal(payload.error.target, "missing"); diff --git a/src/local-agent-pi.test.ts b/src/local-agent-pi.test.ts index f404105e9..bc0dd1685 100644 --- a/src/local-agent-pi.test.ts +++ b/src/local-agent-pi.test.ts @@ -1,13 +1,10 @@ import assert from "node:assert/strict"; -import { basename } from "node:path"; import type { AgentSessionEvent, AgentSessionEventListener } from "@earendil-works/pi-coding-agent"; import { PiLocalAgentDriver, - piToolsForWriteMode, type PiSessionFactory, type PiSessionLike, } from "./local-agent-pi.js"; -import { createPiSandboxConfig } from "./local-agent-pi-sandbox.js"; import { LocalAgentRuntimePool } from "./local-agent-runtime-pool.js"; import type { LocalAgentRuntimeContext } from "./local-agent-runtime.js"; @@ -105,11 +102,6 @@ assert.equal(second.value.finalResponse, "response:second"); assert.deepEqual(sessions[0]?.model, { id: "model" }); assert.equal(sessions[0]?.effort, "high"); assert.deepEqual(sessionIds, ["pi_session_1"]); -assert.deepEqual(piToolsForWriteMode("allowed"), ["read", "grep", "find", "ls", "edit", "write", "bash"]); -assert.ok( - createPiSandboxConfig().filesystem.denyRead.some((path) => basename(path) === ".ssh"), - "sandbox config includes the protected-home read rule; enforcement is covered by local-agent-pi-sandbox.test.ts", -); assert.deepEqual(sessions[0]?.activeTools, ["read", "grep", "find", "ls"]); assert.deepEqual(sessions[0]?.toolHistory, [ ["read", "grep", "find", "ls"], diff --git a/src/review-checkpoints.test.ts b/src/review-checkpoints.test.ts index 6707c6ea3..37ee2c558 100644 --- a/src/review-checkpoints.test.ts +++ b/src/review-checkpoints.test.ts @@ -18,7 +18,6 @@ test("a clean workspace reports no changes from the last-shown checkpoint", asyn assert.equal(clean.summary.files, 0); assert.equal(clean.patch, ""); - assert.match(clean.result, /No changes since last shown changes/); }); test("initialization reports whether aggregate review is available", async (t) => { @@ -176,7 +175,6 @@ test("a missing last-shown checkpoint falls back after restart and can be re-est markReviewed: false, }); assert.equal(fallback.summary.files, 1); - assert.match(fallback.result, /compared from workspace open/); assert.match(fallback.patch, /changed/); const reestablished = await restartedManager.reviewChanges({ @@ -185,7 +183,6 @@ test("a missing last-shown checkpoint falls back after restart and can be re-est markReviewed: true, }); assert.equal(reestablished.summary.files, 1); - assert.match(reestablished.result, /baseline was re-established/); const afterReestablished = await restartedManager.reviewChanges({ workspaceId: "ws_missing_baseline", diff --git a/src/server.test.ts b/src/server.test.ts index 127abba1f..d6ae6baf2 100644 --- a/src/server.test.ts +++ b/src/server.test.ts @@ -92,7 +92,6 @@ test("show_changes keeps model output compact and preserves the rich review card assert.equal(structured.workspaceId, workspaceId); assert.match(structured.reviewRef as string, /^[0-9a-f]{40,64}$/); - assert.match(structured.result as string, /Changed 1 file \(\+1 -1\)/); assert.equal("summary" in structured, false); assert.equal("files" in structured, false); assert.equal("patch" in structured, false); diff --git a/src/ui/patch-display.test.ts b/src/ui/patch-display.test.ts index 7b5822566..009b5df35 100644 --- a/src/ui/patch-display.test.ts +++ b/src/ui/patch-display.test.ts @@ -2,26 +2,9 @@ import assert from "node:assert/strict"; import test from "node:test"; import { getFileChangePathDisplay, - getPatchDisplayParts, getRenderedFileChangeKind, } from "./patch-display.js"; -test("review titles describe a uniform or mixed file set", () => { - assert.equal(getPatchDisplayParts( - { files: [] }, - { emptyTitle: "Changes ready" }, - ).title, "Changes ready"); - assert.equal(getPatchDisplayParts({ - files: [{ path: "a.ts", type: "new" }], - }).title, "Added 1 file"); - assert.equal(getPatchDisplayParts({ - files: [ - { path: "a.ts", type: "new" }, - { path: "b.ts", type: "change" }, - ], - }).title, "Changed 2 files"); -}); - test("rename paths stay compact within one directory", () => { assert.deepEqual(getFileChangePathDisplay({ path: "src/new.ts", From a34976ca1ba5dc5696f5e759c00e580b699a8b6d Mon Sep 17 00:00:00 2001 From: Waishnav <86405648+Waishnav@users.noreply.github.com> Date: Mon, 31 Aug 2026 07:10:59 +0530 Subject: [PATCH 54/75] test: collapse request metadata cases --- src/request-meta.test.ts | 34 ++++++++++------------------------ 1 file changed, 10 insertions(+), 24 deletions(-) diff --git a/src/request-meta.test.ts b/src/request-meta.test.ts index effd3dd09..5a6c7b02d 100644 --- a/src/request-meta.test.ts +++ b/src/request-meta.test.ts @@ -2,31 +2,17 @@ import assert from "node:assert/strict"; import test from "node:test"; import { openAiConversationScopeId } from "./request-meta.js"; -test("undefined request metadata has no conversation scope", () => { - assert.equal(openAiConversationScopeId(undefined), undefined); -}); - -test("missing session metadata has no conversation scope", () => { - assert.equal(openAiConversationScopeId({}), undefined); -}); - -test("an empty session string has no conversation scope", () => { - assert.equal(openAiConversationScopeId({ "openai/session": "" }), undefined); -}); - -test("a non-string session value has no conversation scope", () => { - assert.equal(openAiConversationScopeId({ "openai/session": 42 }), undefined); - assert.equal(openAiConversationScopeId({ "openai/session": {} }), undefined); -}); - -test("valid OpenAI session metadata returns the raw opaque session value", () => { - assert.equal( - openAiConversationScopeId({ "openai/session": "chat-session-opaque-value" }), - "chat-session-opaque-value", - ); -}); +test("OpenAI conversation scope accepts only a non-empty session string", () => { + for (const meta of [ + undefined, + {}, + { "openai/session": "" }, + { "openai/session": 42 }, + { "openai/session": {} }, + ]) { + assert.equal(openAiConversationScopeId(meta), undefined); + } -test("unrelated metadata fields do not alter the selected conversation scope", () => { assert.equal( openAiConversationScopeId({ "openai/session": "chat-session-opaque-value", From a354203468890e3553f90b6c8ab2b92d63c8707c Mon Sep 17 00:00:00 2001 From: Waishnav <86405648+Waishnav@users.noreply.github.com> Date: Mon, 31 Aug 2026 07:12:47 +0530 Subject: [PATCH 55/75] test: keep workspace reuse coverage at its owner --- src/server.test.ts | 136 +++++---------------------------------------- 1 file changed, 13 insertions(+), 123 deletions(-) diff --git a/src/server.test.ts b/src/server.test.ts index d6ae6baf2..79c21a66c 100644 --- a/src/server.test.ts +++ b/src/server.test.ts @@ -272,121 +272,24 @@ test("open_workspace omits providers disabled by configuration", async (t) => { ); }); -test("concurrent checkout opens return one full context and one reuse instruction", async (t) => { - const context = await fixture(t); - const [first, second] = await Promise.all([ - callOpen(context.client, context.project, "chat-1"), - callOpen(context.client, context.project, "chat-1"), - ]); - - assert.equal(structuredContent(first).workspaceId, structuredContent(second).workspaceId); - assert.equal( - [first, second].filter((result) => Array.isArray(structuredContent(result).agentsFiles)).length, - 1, - ); - assert.equal( - [first, second].filter((result) => responseText(result).includes("Workspace already open as")).length, - 1, - ); -}); - -test("new worktrees always receive a fresh workspace and complete worktree context", async (t) => { - const context = await fixture(t, { git: true }); - const checkout = await callOpen(context.client, context.project, "chat-1"); - const firstWorktree = await callOpen(context.client, context.project, "chat-1", "worktree"); - const secondWorktree = await callOpen(context.client, context.project, "chat-1", "worktree"); - const checkoutAgain = await callOpen(context.client, context.project, "chat-1"); - - assert.notEqual(structuredContent(firstWorktree).workspaceId, structuredContent(secondWorktree).workspaceId); - assert.equal(structuredContent(checkoutAgain).workspaceId, structuredContent(checkout).workspaceId); - for (const result of [firstWorktree, secondWorktree]) { - const structured = structuredContent(result); - assert.equal(structured.mode, "worktree"); - assert.ok(Array.isArray(structured.agentsFiles)); - assert.ok(Array.isArray(structured.availableAgentsFiles)); - assert.ok(Array.isArray(structured.skills)); - assert.ok(Array.isArray(structured.agentProviders)); - assert.ok(Array.isArray(structured.agents)); - assert.ok(Array.isArray(structured.skillDiagnostics)); - assert.match(responseText(result), /Opened isolated worktree workspace/); - } - assert.equal(structuredContent(checkoutAgain).agentsFiles, undefined); -}); - -test("checkout opened after a worktree receives its own complete context", async (t) => { - const context = await fixture(t, { git: true }); - const worktree = await callOpen(context.client, context.project, "chat-1", "worktree"); - const checkout = await callOpen(context.client, context.project, "chat-1"); - const checkoutAgain = await callOpen(context.client, context.project, "chat-1"); - - assert.equal(structuredContent(worktree).mode, "worktree"); - assert.ok(Array.isArray(structuredContent(worktree).agentsFiles)); - assert.equal(structuredContent(checkout).mode, "checkout"); - assert.ok(Array.isArray(structuredContent(checkout).agentsFiles)); - assert.equal(structuredContent(checkoutAgain).workspaceId, structuredContent(checkout).workspaceId); - assert.equal(structuredContent(checkoutAgain).agentsFiles, undefined); -}); - -test("a host without conversation metadata receives normal explicit-workspace behavior", async (t) => { - const context = await fixture(t); - const first = await callOpen(context.client, context.project); - const second = await callOpen(context.client, context.project); - - assert.notEqual(structuredContent(first).workspaceId, structuredContent(second).workspaceId); - assert.ok(Array.isArray(structuredContent(first).agentsFiles)); - assert.ok(Array.isArray(structuredContent(second).agentsFiles)); - assert.doesNotMatch(responseText(first), /conversation metadata/i); - assert.doesNotMatch(responseText(second), /conversation metadata/i); -}); - -test("checkout reuse and context suppression survive a registry restart", async (t) => { +test("open_workspace scopes checkout reuse to OpenAI session metadata", async (t) => { const context = await fixture(t); const first = await callOpen(context.client, context.project, "chat-1"); - const firstWorkspaceId = structuredContent(first).workspaceId; - - await context.close(); - - const restoredStore = new SqliteWorkspaceStore(context.stateDir); - const restoredServer = createMcpServer( - context.config, - new WorkspaceRegistry(context.config, restoredStore), - createReviewCheckpointManager(), - new ProcessSessionManager(), - () => [], - [], - ); - const [restoredClientTransport, restoredServerTransport] = InMemoryTransport.createLinkedPair(); - const restoredClient = new Client({ name: "devspace-restored-test-client", version: "1.0.0" }); - let restoredClosed = false; - const closeRestored = async () => { - if (restoredClosed) return; - restoredClosed = true; - await restoredClient.close(); - await restoredServer.close(); - restoredStore.close(); - }; - t.after(closeRestored); - - try { - await Promise.all([ - restoredClient.connect(restoredClientTransport), - restoredServer.connect(restoredServerTransport), - ]); - - const restored = await callOpen(restoredClient, context.project, "chat-1"); - assert.equal(structuredContent(restored).workspaceId, firstWorkspaceId); - assert.equal(structuredContent(restored).agentsFiles, undefined); - } finally { - await closeRestored(); - } + const repeated = await callOpen(context.client, context.project, "chat-1"); + const otherSession = await callOpen(context.client, context.project, "chat-2"); + const unscoped = await callOpen(context.client, context.project); + + assert.equal(structuredContent(repeated).workspaceId, structuredContent(first).workspaceId); + assert.equal(structuredContent(repeated).agentsFiles, undefined); + assert.notEqual(structuredContent(otherSession).workspaceId, structuredContent(first).workspaceId); + assert.notEqual(structuredContent(unscoped).workspaceId, structuredContent(first).workspaceId); + assert.ok(Array.isArray(structuredContent(otherSession).agentsFiles)); + assert.ok(Array.isArray(structuredContent(unscoped).agentsFiles)); }); interface ServerFixture { client: Client; project: string; - config: ServerConfig; - stateDir: string; - close: () => Promise; } async function fixture( @@ -491,7 +394,7 @@ async function fixture( await rm(root, { recursive: true, force: true }); }); - return { client, project, config, stateDir, close }; + return { client, project }; } async function git(cwd: string, args: string[]): Promise { @@ -502,14 +405,10 @@ async function callOpen( client: Client, path: string, conversationScopeId?: string, - mode?: "checkout" | "worktree", ): Promise>> { const params = { name: "open_workspace", - arguments: { - path, - ...(mode ? { mode } : {}), - }, + arguments: { path }, ...(conversationScopeId ? { _meta: { "openai/session": conversationScopeId } } : {}), @@ -522,15 +421,6 @@ function structuredContent(result: Awaited>): Rec return result.structuredContent as Record; } -function responseText(result: Awaited>): string { - const content = (result as { content?: unknown }).content; - assert.ok(Array.isArray(content)); - const first = content[0] as { type?: unknown; text?: unknown } | undefined; - assert.equal(first?.type, "text"); - assert.equal(typeof first?.text, "string"); - return first?.text as string; -} - function responseCard(result: Awaited>): Record { const metadata = result._meta; assert.ok(metadata && typeof metadata === "object"); From 36d01ee1a67f63ff1cfbf6ae1a2579fff25a9d79 Mon Sep 17 00:00:00 2001 From: Waishnav <86405648+Waishnav@users.noreply.github.com> Date: Mon, 31 Aug 2026 07:58:45 +0530 Subject: [PATCH 56/75] test: verify provider availability at snapshot seam --- src/local-agent-availability.test.ts | 49 +++++++--------------------- src/local-agent-availability.ts | 2 +- 2 files changed, 12 insertions(+), 39 deletions(-) diff --git a/src/local-agent-availability.test.ts b/src/local-agent-availability.test.ts index 08a73a10e..7e0ebc1ce 100644 --- a/src/local-agent-availability.test.ts +++ b/src/local-agent-availability.test.ts @@ -1,39 +1,12 @@ import assert from "node:assert/strict"; -import { - checkLocalAgentProviderAvailability, - getLocalAgentProviderAvailabilitySnapshot, -} from "./local-agent-availability.js"; - -{ - const availability = checkLocalAgentProviderAvailability("codex"); - assert.equal(availability.name, "codex"); - assert.equal(typeof availability.available, "boolean"); - if (availability.available) { - assert.equal(availability.note, "available"); - } -} - -{ - const availability = checkLocalAgentProviderAvailability("codex", { - ...process.env, - CODEX_COMMAND: "/definitely/missing/devspace-codex", - }); - assert.equal(availability.available, false); - assert.match(availability.reason ?? "", /executable not found/); -} - -{ - assert.equal(checkLocalAgentProviderAvailability("pi").available, true); -} - -{ - const snapshot = getLocalAgentProviderAvailabilitySnapshot({ - ...process.env, - CODEX_COMMAND: "/definitely/missing/devspace-codex", - }); - assert.deepEqual( - snapshot.map((provider) => provider.name), - ["codex", "claude", "opencode", "pi", "cursor", "copilot", "grok"], - ); - assert.equal(snapshot.find((provider) => provider.name === "pi")?.available, true); -} +import { getLocalAgentProviderAvailabilitySnapshot } from "./local-agent-availability.js"; + +const snapshot = getLocalAgentProviderAvailabilitySnapshot({ + ...process.env, + CODEX_COMMAND: "/definitely/missing/devspace-codex", +}); +assert.deepEqual(snapshot.find((provider) => provider.name === "codex"), { + name: "codex", + available: false, + reason: "/definitely/missing/devspace-codex executable not found", +}); diff --git a/src/local-agent-availability.ts b/src/local-agent-availability.ts index a021dbde9..3a67b98f7 100644 --- a/src/local-agent-availability.ts +++ b/src/local-agent-availability.ts @@ -18,7 +18,7 @@ export function getLocalAgentProviderAvailabilitySnapshot( return LOCAL_AGENT_PROVIDERS.map((provider) => checkLocalAgentProviderAvailability(provider, env)); } -export function checkLocalAgentProviderAvailability( +function checkLocalAgentProviderAvailability( provider: LocalAgentProvider, env: NodeJS.ProcessEnv = process.env, ): LocalAgentProviderAvailability { From 8e625cdaf71365eb108b8eb26626536e5ddbb579 Mon Sep 17 00:00:00 2001 From: Waishnav <86405648+Waishnav@users.noreply.github.com> Date: Mon, 31 Aug 2026 07:59:02 +0530 Subject: [PATCH 57/75] test: drop trivial helper assertions --- src/config-schema.test.ts | 6 ------ src/local-agent-daemon-protocol.test.ts | 2 -- src/onboarding.test.ts | 6 ------ src/ui/card-types.test.ts | 8 ++------ 4 files changed, 2 insertions(+), 20 deletions(-) diff --git a/src/config-schema.test.ts b/src/config-schema.test.ts index 78438f63c..1b6ce9883 100644 --- a/src/config-schema.test.ts +++ b/src/config-schema.test.ts @@ -1,16 +1,10 @@ import assert from "node:assert/strict"; import { readFileSync } from "node:fs"; import { - defaultDevspaceConfig, devspaceConfigJsonSchema, devspaceConfigSchema, } from "./config-schema.js"; -const defaults = defaultDevspaceConfig(); -assert.equal(defaults.configVersion, 1); -assert.equal(defaults.tools.mode, "codex"); -assert.equal(defaults.ui.enabled, true); - assert.throws( () => devspaceConfigSchema.parse({ configVersion: 1, typo: true }), /Unrecognized key/, diff --git a/src/local-agent-daemon-protocol.test.ts b/src/local-agent-daemon-protocol.test.ts index a6dfe4cab..708180987 100644 --- a/src/local-agent-daemon-protocol.test.ts +++ b/src/local-agent-daemon-protocol.test.ts @@ -3,7 +3,6 @@ import { decodeAgentRecord, decodeLocalAgentDaemonRequest, decodeLocalAgentDaemonResponse, - encodeLocalAgentDaemonRequest, encodeLocalAgentDaemonResponse, LocalAgentDaemonProtocolError, } from "./local-agent-daemon-protocol.js"; @@ -24,7 +23,6 @@ const request = decodeLocalAgentDaemonRequest({ assert.equal(request.method, "agent.start"); if (request.method !== "agent.start") throw new Error("expected agent.start request"); assert.equal(request.params.writeMode, "read_only"); -assert.match(encodeLocalAgentDaemonRequest(request), /"method":"agent.start"/); const whitespaceRequest = decodeLocalAgentDaemonRequest({ requestId: "req_whitespace", diff --git a/src/onboarding.test.ts b/src/onboarding.test.ts index ac26f78ca..b4236e9c0 100644 --- a/src/onboarding.test.ts +++ b/src/onboarding.test.ts @@ -2,8 +2,6 @@ import assert from "node:assert/strict"; import { resolveOnboardingUsage, updateOnboardingSubagentsConfig, - usesChatGpt, - usesCodingAgents, } from "./onboarding.js"; for (const [selections, expected] of [ @@ -13,10 +11,6 @@ for (const [selections, expected] of [ ] as const) { assert.equal(resolveOnboardingUsage(selections), expected); } -assert.equal(usesChatGpt("both"), true); -assert.equal(usesCodingAgents("both"), true); -assert.equal(usesChatGpt("coding-agents"), false); -assert.equal(usesCodingAgents("chatgpt"), false); assert.throws(() => resolveOnboardingUsage([]), /Choose ChatGPT, Coding Agents, or both/); assert.deepEqual( diff --git a/src/ui/card-types.test.ts b/src/ui/card-types.test.ts index 19e5b582a..0e1a387c9 100644 --- a/src/ui/card-types.test.ts +++ b/src/ui/card-types.test.ts @@ -1,9 +1,6 @@ import assert from "node:assert/strict"; import test from "node:test"; -import { - isExpandableCard, - isInitiallyExpandedCard, -} from "./card-types.js"; +import { isExpandableCard } from "./card-types.js"; test("aggregate review opens when a patch is available", () => { const card = { @@ -12,12 +9,11 @@ test("aggregate review opens when a patch is available", () => { payload: { patch: "diff --git a/src/a.ts b/src/a.ts" }, }; assert.equal(isExpandableCard(card), true); - assert.equal(isInitiallyExpandedCard(card), true); }); test("workspace details open only when there is useful context", () => { assert.equal(isExpandableCard({ tool: "open_workspace" }), false); - assert.equal(isInitiallyExpandedCard({ + assert.equal(isExpandableCard({ tool: "open_workspace", skills: [{ name: "research" }], }), true); From 750dc844cce7ea727c46b0bfd2bbd3ce55ffe654 Mon Sep 17 00:00:00 2001 From: Waishnav <86405648+Waishnav@users.noreply.github.com> Date: Mon, 31 Aug 2026 07:59:37 +0530 Subject: [PATCH 58/75] test: stop asserting runtime key internals --- src/local-agent-acp.test.ts | 15 --------------- src/local-agent-codex.test.ts | 14 +------------- 2 files changed, 1 insertion(+), 28 deletions(-) diff --git a/src/local-agent-acp.test.ts b/src/local-agent-acp.test.ts index c4f58d482..39227fa66 100644 --- a/src/local-agent-acp.test.ts +++ b/src/local-agent-acp.test.ts @@ -254,11 +254,6 @@ if (completedOverlappingTurn.isErr()) throw completedOverlappingTurn.error; assert.equal(completedOverlappingTurn.value.finalResponse, "overlap response"); await overlapRuntime.close(); -let resolverCalls = 0; -const cachedDriver = new AcpLocalAgentDriver("cursor", {}, () => { - resolverCalls += 1; - return "/usr/local/bin/cursor-agent"; -}); const cachedContext = { agentId: "agt_acp", provider: "cursor" as const, @@ -266,16 +261,6 @@ const cachedContext = { writeMode: "allowed" as const, }; const resolvedProject = resolve("/tmp/project"); -assert.equal(cachedDriver.runtimeKey(cachedContext), `acp:cursor:/usr/local/bin/cursor-agent:allowed:${resolvedProject}`); -assert.equal(cachedDriver.runtimeKey(cachedContext), `acp:cursor:/usr/local/bin/cursor-agent:allowed:${resolvedProject}`); -for (const writeMode of ["read_only", "allowed", "full_access"] as const) { - assert.notEqual( - cachedDriver.runtimeKey({ ...cachedContext, writeMode, workspaceRoot: "/tmp/other-project" }), - cachedDriver.runtimeKey({ ...cachedContext, writeMode }), - `${writeMode} ACP runtimes are scoped to one workspace root`, - ); -} -assert.equal(resolverCalls, 1, "ACP executable identity is resolved once per driver lifecycle"); assert.deepEqual(acpCommandArgs("cursor", cachedContext), [ "acp", "--sandbox", "enabled", "--workspace", resolvedProject, ]); diff --git a/src/local-agent-codex.test.ts b/src/local-agent-codex.test.ts index 15a510f53..b5862d2b8 100644 --- a/src/local-agent-codex.test.ts +++ b/src/local-agent-codex.test.ts @@ -1,6 +1,6 @@ import assert from "node:assert/strict"; import { chmod, mkdir, mkdtemp, rm, writeFile } from "node:fs/promises"; -import { join, resolve } from "node:path"; +import { join } from "node:path"; import { tmpdir } from "node:os"; import { CodexAppServerRuntime, @@ -12,19 +12,7 @@ import { } from "./local-agent-codex.js"; import { toAgentErrorPayload } from "./local-agent-errors.js"; -let resolverCalls = 0; -const cachedDriver = new CodexLocalAgentDriver( - { CODEX_HOME: "/tmp/codex-home" }, - () => { - resolverCalls += 1; - return { executable: "/usr/local/bin/codex", version: "1.2.3" }; - }, -); const cachedContext = { agentId: "agt_test", provider: "codex" as const, workspaceRoot: "/tmp/project" }; -const resolvedCodexHome = resolve("/tmp/codex-home"); -assert.equal(cachedDriver.runtimeKey(cachedContext), `codex:/usr/local/bin/codex:${resolvedCodexHome}`); -assert.equal(cachedDriver.runtimeKey(cachedContext), `codex:/usr/local/bin/codex:${resolvedCodexHome}`); -assert.equal(resolverCalls, 1, "Codex executable identity is resolved once per driver lifecycle"); assert.equal(parseCodexVersion("codex-cli 0.9.1"), "0.9.1"); assert.equal(sandboxFor("read_only"), "read-only"); From cd0209d03e583a737e00a5c96b172aae9441528b Mon Sep 17 00:00:00 2001 From: Waishnav <86405648+Waishnav@users.noreply.github.com> Date: Mon, 31 Aug 2026 08:00:04 +0530 Subject: [PATCH 59/75] test: keep agent formatting at CLI seam --- src/local-agent-presentation.test.ts | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/local-agent-presentation.test.ts b/src/local-agent-presentation.test.ts index cd12d54e3..ea1a4d6e6 100644 --- a/src/local-agent-presentation.test.ts +++ b/src/local-agent-presentation.test.ts @@ -1,9 +1,6 @@ import assert from "node:assert/strict"; import type { LocalAgentCatalog } from "./local-agent-catalog.js"; import { - formatAgentObservation, - formatAgentSummary, - formatAgentTargetCatalog, presentAgentObservation, presentAgentReceipt, presentAgentSummary, @@ -35,7 +32,6 @@ assert.deepEqual(presentAgentSummary({ ...record, status: "idle" }), { status: "completed", target: "reviewer", }); -assert.equal(formatAgentSummary(presentAgentSummary(record)), "agt_test running reviewer"); const completed = presentAgentObservation({ ...record, @@ -47,7 +43,6 @@ assert.deepEqual(completed, { status: "completed", response: "Found one issue.", }); -assert.equal(formatAgentObservation(completed), "agt_test completed\n\nFound one issue."); const failed = presentAgentObservation({ ...record, @@ -66,10 +61,6 @@ assert.deepEqual(failed, { retryable: true, }, }); -assert.equal( - formatAgentObservation(failed), - "agt_test failed PROVIDER_EXECUTION_ERROR: Provider disconnected. [retryable]", -); const catalog: LocalAgentCatalog = { enabled: true, @@ -105,10 +96,3 @@ assert.deepEqual(targetCatalog, { }, ], }); -assert.equal( - formatAgentTargetCatalog(targetCatalog), - [ - "codex [provider] model=gpt-5.4 effort=high", - "reviewer [profile, codex] model=gpt-5.4 effort=high - Review changes.", - ].join("\n"), -); From a031c7819936e29afd96c3a9e8e8b62946169e0b Mon Sep 17 00:00:00 2001 From: Waishnav <86405648+Waishnav@users.noreply.github.com> Date: Mon, 31 Aug 2026 08:16:01 +0530 Subject: [PATCH 60/75] test: discover test files automatically --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 7679a5507..e2f4cb5b6 100644 --- a/package.json +++ b/package.json @@ -33,8 +33,8 @@ "prepack": "npm run build", "schema:config": "tsx scripts/generate-config-schema.ts", "start": "node dist/cli.js serve", - "test": "tsx src/user-config.test.ts && tsx src/config.test.ts && tsx src/onboarding.test.ts && tsx src/cli-workspace.test.ts && tsx src/request-meta.test.ts && tsx src/incoming-artifacts.test.ts && tsx src/artifact-download.test.ts && tsx src/ui/card-types.test.ts && tsx src/ui/tool-result.test.ts && tsx src/ui/patch-display.test.ts && tsx src/apply-patch.test.ts && tsx src/process-platform.test.ts && tsx src/process-sessions.test.ts && tsx src/mcp-sessions.test.ts && tsx src/server-shutdown.test.ts && tsx src/local-agent-config.test.ts && tsx src/local-agent-catalog.test.ts && tsx src/local-agent-presentation.test.ts && tsx src/local-agent-runtime.test.ts && tsx src/local-agent-daemon-lifecycle.test.ts && tsx src/local-agent-daemon-protocol.test.ts && tsx src/local-agent-daemon.test.ts && tsx src/local-agent-codex.test.ts && tsx src/local-agent-opencode.test.ts && tsx src/local-agent-acp.test.ts && tsx src/local-agent-grok.test.ts && tsx src/local-agent-pi-sandbox.test.ts && tsx src/local-agent-pi.test.ts && tsx src/local-agent-claude.test.ts && tsx src/local-agent-adapters.test.ts && tsx src/local-agent-availability.test.ts && tsx src/local-agent-profiles.test.ts && tsx src/local-agent-targets.test.ts && tsx src/local-agent-store.test.ts && tsx src/local-agent-manager.test.ts && tsx src/roots.test.ts && tsx src/skills.test.ts && tsx src/workspaces.test.ts && tsx src/workspace-conversation.test.ts && tsx src/review-checkpoints.test.ts && tsx src/server.test.ts && tsx src/oauth-store.test.ts && tsx src/cli-show-changes.test.ts && tsx src/cli.test.ts", - "typecheck": "tsx src/config-schema.test.ts && tsc -p tsconfig.json --noEmit" + "test": "tsx --test --test-concurrency=1 \"src/**/*.test.ts\"", + "typecheck": "tsc -p tsconfig.json --noEmit" }, "keywords": [], "author": "", From 89e2e74dd73262195880812a5877dfee28607e71 Mon Sep 17 00:00:00 2001 From: Waishnav <86405648+Waishnav@users.noreply.github.com> Date: Mon, 31 Aug 2026 08:19:42 +0530 Subject: [PATCH 61/75] chore(dev): use tsx watch for reloads --- package.json | 2 +- scripts/dev-server.mjs | 120 ----------------------------------------- 2 files changed, 1 insertion(+), 121 deletions(-) delete mode 100644 scripts/dev-server.mjs diff --git a/package.json b/package.json index e2f4cb5b6..3dad18389 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "clean": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"", "build": "npm run clean && npm run build:app && tsc -p tsconfig.build.json", "build:app": "vite build", - "dev": "node scripts/dev-server.mjs", + "dev": "tsx watch --clear-screen=false src/cli.ts serve", "postinstall": "node scripts/fix-node-pty-permissions.mjs", "prepack": "npm run build", "schema:config": "tsx scripts/generate-config-schema.ts", diff --git a/scripts/dev-server.mjs b/scripts/dev-server.mjs deleted file mode 100644 index 5585bdae8..000000000 --- a/scripts/dev-server.mjs +++ /dev/null @@ -1,120 +0,0 @@ -import { spawn } from "node:child_process"; -import { readdirSync, statSync, watch } from "node:fs"; -import { join, resolve } from "node:path"; -import { fileURLToPath } from "node:url"; - -const repoRoot = resolve(fileURLToPath(new URL("..", import.meta.url))); -const watchRoots = ["src"].map((entry) => join(repoRoot, entry)); -const restartDelayMs = 750; -const crashDelayMs = 1500; - -let child; -let restartTimer; -let stoppingForRestart = false; -let shuttingDown = false; - -function log(message) { - console.error(`[devspace:dev] ${message}`); -} - -function start() { - stoppingForRestart = false; - child = spawn("npx", ["tsx", "src/cli.ts", "serve"], { - cwd: repoRoot, - env: process.env, - stdio: "inherit", - }); - - child.on("exit", (code, signal) => { - child = undefined; - if (shuttingDown) return; - if (stoppingForRestart) return; - - log(`server exited (${signal ?? code ?? "unknown"}); restarting in ${crashDelayMs}ms`); - scheduleRestart(crashDelayMs); - }); -} - -function scheduleRestart(delayMs = restartDelayMs) { - clearTimeout(restartTimer); - restartTimer = setTimeout(restart, delayMs); -} - -function restart() { - if (shuttingDown) return; - clearTimeout(restartTimer); - - if (!child) { - start(); - return; - } - - stoppingForRestart = true; - child.once("exit", () => { - if (!shuttingDown) start(); - }); - child.kill("SIGTERM"); - - setTimeout(() => { - if (child && stoppingForRestart) child.kill("SIGKILL"); - }, 3000).unref(); -} - -function watchDirectory(root) { - const watchers = []; - const seen = new Set(); - - function addDirectory(dir) { - if (seen.has(dir)) return; - seen.add(dir); - - const watcher = watch(dir, (event, filename) => { - if (!filename) { - scheduleRestart(); - return; - } - - const path = join(dir, filename.toString()); - if (event === "rename") maybeAddDirectory(path); - scheduleRestart(); - }); - watchers.push(watcher); - - for (const entry of readdirSync(dir)) { - maybeAddDirectory(join(dir, entry)); - } - } - - function maybeAddDirectory(path) { - try { - const stats = statSync(path); - if (stats.isDirectory()) addDirectory(path); - } catch { - // The file may have been deleted between the watch event and stat call. - } - } - - addDirectory(root); - return watchers; -} - -function shutdown() { - shuttingDown = true; - clearTimeout(restartTimer); - if (!child) return process.exit(0); - - child.once("exit", () => process.exit(0)); - child.kill("SIGTERM"); - setTimeout(() => process.exit(1), 3000).unref(); -} - -for (const signal of ["SIGINT", "SIGTERM"]) { - process.on(signal, shutdown); -} - -for (const root of watchRoots) { - watchDirectory(root); -} - -log("watching src; server restarts on changes and after crashes"); -start(); From bd1f45134a8b1868006a24f309838839e22e3774 Mon Sep 17 00:00:00 2001 From: Waishnav <86405648+Waishnav@users.noreply.github.com> Date: Mon, 31 Aug 2026 08:50:19 +0530 Subject: [PATCH 62/75] chore: adopt pnpm lockfile --- package-lock.json | 6133 ------------------------------------------- package.json | 1 + pnpm-lock.yaml | 3955 ++++++++++++++++++++++++++++ pnpm-workspace.yaml | 6 + 4 files changed, 3962 insertions(+), 6133 deletions(-) delete mode 100644 package-lock.json create mode 100644 pnpm-lock.yaml create mode 100644 pnpm-workspace.yaml diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index e14a7e90f..000000000 --- a/package-lock.json +++ /dev/null @@ -1,6133 +0,0 @@ -{ - "name": "@waishnav/devspace", - "version": "1.0.8", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "@waishnav/devspace", - "version": "1.0.8", - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "@agentclientprotocol/sdk": "^1.1.0", - "@anthropic-ai/claude-agent-sdk": "0.3.200", - "@anthropic-ai/sandbox-runtime": "0.0.71", - "@clack/prompts": "^1.5.1", - "@earendil-works/pi-coding-agent": "^0.80.3", - "@modelcontextprotocol/ext-apps": "^1.7.2", - "@modelcontextprotocol/sdk": "^1.29.0", - "@opencode-ai/sdk": "^1.17.13", - "@pierre/diffs": "^1.2.5", - "better-result": "^2.10.0", - "better-sqlite3": "^12.10.0", - "cross-spawn": "^7.0.6", - "diff": "^8.0.3", - "drizzle-orm": "^0.45.2", - "express": "^5.2.1", - "jsonc-parser": "^3.3.1", - "lucide": "^1.24.0", - "react": "^19.2.6", - "react-dom": "^19.2.6", - "semver": "^7.8.4", - "yaml": "^2.9.0", - "zod": "^4.4.3" - }, - "bin": { - "devspace": "bin/devspace.js", - "devspace-agentd": "bin/devspace-agentd.js" - }, - "devDependencies": { - "@types/better-sqlite3": "^7.6.13", - "@types/express": "^5.0.6", - "@types/node": "^25.9.1", - "@types/react": "^19.2.15", - "@types/react-dom": "^19.2.3", - "@types/semver": "^7.7.1", - "@vitejs/plugin-react": "^6.0.2", - "tsx": "^4.22.3", - "typescript": "^6.0.3", - "vite": "^8.0.14" - }, - "engines": { - "node": ">=22.19 <27" - }, - "optionalDependencies": { - "node-pty": "^1.1.0" - } - }, - "node_modules/@agentclientprotocol/sdk": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@agentclientprotocol/sdk/-/sdk-1.1.0.tgz", - "integrity": "sha512-NT2KqphUJ3w6EksUL51ZhJgIYgq/ZLGcBPkyMKgRSO5PMVwe9DnKKX+Htnvk6KHh6dUuh34UHK4gKp+4te1Mdg==", - "license": "Apache-2.0", - "peerDependencies": { - "zod": "^3.25.0 || ^4.0.0" - } - }, - "node_modules/@anthropic-ai/claude-agent-sdk": { - "version": "0.3.200", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk/-/claude-agent-sdk-0.3.200.tgz", - "integrity": "sha512-o13TM3boFIJE4oZdQDFw5TQfiev1sBoxwzKM2QGj/NPtxriGTP0PKNAQsGZvTsiEOIIH5rzPr/H81xVkkAw23g==", - "license": "SEE LICENSE IN README.md", - "engines": { - "node": ">=18.0.0" - }, - "optionalDependencies": { - "@anthropic-ai/claude-agent-sdk-darwin-arm64": "0.3.200", - "@anthropic-ai/claude-agent-sdk-darwin-x64": "0.3.200", - "@anthropic-ai/claude-agent-sdk-linux-arm64": "0.3.200", - "@anthropic-ai/claude-agent-sdk-linux-arm64-musl": "0.3.200", - "@anthropic-ai/claude-agent-sdk-linux-x64": "0.3.200", - "@anthropic-ai/claude-agent-sdk-linux-x64-musl": "0.3.200", - "@anthropic-ai/claude-agent-sdk-win32-arm64": "0.3.200", - "@anthropic-ai/claude-agent-sdk-win32-x64": "0.3.200" - }, - "peerDependencies": { - "@anthropic-ai/sdk": ">=0.93.0", - "@modelcontextprotocol/sdk": "^1.29.0", - "zod": "^4.0.0" - } - }, - "node_modules/@anthropic-ai/claude-agent-sdk-darwin-arm64": { - "version": "0.3.200", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-arm64/-/claude-agent-sdk-darwin-arm64-0.3.200.tgz", - "integrity": "sha512-8UzzInVdRPDNIOvrAxYbHHJD/u13WSBx9fvEeuZnsZ6rZh0qnSI1QwU8Due0V2+m+ZnT3cEonmXDvo2ee/icWg==", - "cpu": [ - "arm64" - ], - "license": "SEE LICENSE IN LICENSE.md", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@anthropic-ai/claude-agent-sdk-darwin-x64": { - "version": "0.3.200", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-darwin-x64/-/claude-agent-sdk-darwin-x64-0.3.200.tgz", - "integrity": "sha512-DCwlQoO8HWGuFElE+Q5pYkiBTalXjjMATRAxXyc94fI6m1ZRqyba66dOea+zTmzHPpOb6zSoHYNLiXy7EjNpcg==", - "cpu": [ - "x64" - ], - "license": "SEE LICENSE IN LICENSE.md", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@anthropic-ai/claude-agent-sdk-linux-arm64": { - "version": "0.3.200", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64/-/claude-agent-sdk-linux-arm64-0.3.200.tgz", - "integrity": "sha512-NAEonp086ZOsf+3o/9Y5JRclO6C4n4ceiSuCpSDV6SSUOLBmCRi7r/PJOoMsIWwMshC6fnnkDKZamTpHjr75eg==", - "cpu": [ - "arm64" - ], - "license": "SEE LICENSE IN LICENSE.md", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@anthropic-ai/claude-agent-sdk-linux-arm64-musl": { - "version": "0.3.200", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-arm64-musl/-/claude-agent-sdk-linux-arm64-musl-0.3.200.tgz", - "integrity": "sha512-ak0l+zpz3dKPjnBegUhOs1Y5xFveEQ1AVqmq6s8Q7qd3vO4SrDPiUOpxRkjkqWyGD8r8w+ezG+unf3U9IZ6DRg==", - "cpu": [ - "arm64" - ], - "license": "SEE LICENSE IN LICENSE.md", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@anthropic-ai/claude-agent-sdk-linux-x64": { - "version": "0.3.200", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64/-/claude-agent-sdk-linux-x64-0.3.200.tgz", - "integrity": "sha512-0R/In8G4fZLFFEIA1SqXRRf9mzDGx7roHpMawNdTT1QlG4XftGTlKMxfukt/YcxwzsNPWg4hJSkEDxsb+3J6FA==", - "cpu": [ - "x64" - ], - "license": "SEE LICENSE IN LICENSE.md", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@anthropic-ai/claude-agent-sdk-linux-x64-musl": { - "version": "0.3.200", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-linux-x64-musl/-/claude-agent-sdk-linux-x64-musl-0.3.200.tgz", - "integrity": "sha512-Sf5TTCO3bc5ty7FX5F19WT3xbtU+f1biYD9+dDJ7YHyYFWuiPlWcnCJ8El8RSwCTuvz3OexJLwCqGHRWOC3eBg==", - "cpu": [ - "x64" - ], - "license": "SEE LICENSE IN LICENSE.md", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@anthropic-ai/claude-agent-sdk-win32-arm64": { - "version": "0.3.200", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-arm64/-/claude-agent-sdk-win32-arm64-0.3.200.tgz", - "integrity": "sha512-iJx10bdrk3afa/Oq9QHRh2HaINT/xnsm5OrFNNLbix2CoOEY5lA7f0lk/s0OMiWnfXdv5vvtADpgZ5tvUoQykA==", - "cpu": [ - "arm64" - ], - "license": "SEE LICENSE IN LICENSE.md", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@anthropic-ai/claude-agent-sdk-win32-x64": { - "version": "0.3.200", - "resolved": "https://registry.npmjs.org/@anthropic-ai/claude-agent-sdk-win32-x64/-/claude-agent-sdk-win32-x64-0.3.200.tgz", - "integrity": "sha512-Mka8YDpDIiSJcbrdoBhzX3S0n9DYcoYaEjS7lxwX3GyPi5PvXV4UBuXzj++7ieV/KS4w32Sm3mHQRpeVwnJZ0A==", - "cpu": [ - "x64" - ], - "license": "SEE LICENSE IN LICENSE.md", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@anthropic-ai/sandbox-runtime": { - "version": "0.0.71", - "resolved": "https://registry.npmjs.org/@anthropic-ai/sandbox-runtime/-/sandbox-runtime-0.0.71.tgz", - "integrity": "sha512-/ZMCavpMElD0ku2BlA95vezKUsVN0DD/wVd3WIEAfFjkTF2nsmzQA+MhejIWhuSUS9HpxMtTj57eFL+kdbKZ/A==", - "license": "Apache-2.0", - "dependencies": { - "@pondwader/socks5-server": "^1.0.10", - "commander": "^12.1.0", - "node-forge": "^1.4.0", - "zod": "^3.24.1" - }, - "bin": { - "srt": "dist/cli.js" - }, - "engines": { - "node": ">=20.11.0" - } - }, - "node_modules/@anthropic-ai/sandbox-runtime/node_modules/zod": { - "version": "3.25.76", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", - "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - }, - "node_modules/@anthropic-ai/sdk": { - "version": "0.110.0", - "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.110.0.tgz", - "integrity": "sha512-hOP4bNYXDFHDxxiEgzlILXrxZIYCDnhe8sry0RDRKD/QnsEpvZcQpablCdm9X/WuD/YgOiSIkkqsL1mLLlTqJw==", - "license": "MIT", - "peer": true, - "dependencies": { - "json-schema-to-ts": "^3.1.1", - "standardwebhooks": "^1.0.0" - }, - "bin": { - "anthropic-ai-sdk": "bin/cli" - }, - "peerDependencies": { - "zod": "^3.25.0 || ^4.0.0" - }, - "peerDependenciesMeta": { - "zod": { - "optional": true - } - } - }, - "node_modules/@babel/runtime": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz", - "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@clack/core": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@clack/core/-/core-1.4.1.tgz", - "integrity": "sha512-FILJa1gGKEFTGZAJE9RpVhrjKz3c3h4ar60dSv6cGuDqufQ84YEIS3GAGvZiN+H6yaLbbvTFNejjCC4tXpZEuw==", - "license": "MIT", - "dependencies": { - "fast-wrap-ansi": "^0.2.0", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 20.12.0" - } - }, - "node_modules/@clack/prompts": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/@clack/prompts/-/prompts-1.5.1.tgz", - "integrity": "sha512-zccHj2z2oCCO4yrDiRSlFOxWerGqRiysP7a5jPK6uoI9URKAquwY42Dd/iUP8JWHxEzdRe4TlbvZCo8z1/mhrw==", - "license": "MIT", - "dependencies": { - "@clack/core": "1.4.1", - "fast-string-width": "^3.0.2", - "fast-wrap-ansi": "^0.2.0", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 20.12.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent": { - "version": "0.80.3", - "resolved": "https://registry.npmjs.org/@earendil-works/pi-coding-agent/-/pi-coding-agent-0.80.3.tgz", - "integrity": "sha512-TIggw9gCXpA+Ph7OjdTA7ka2NPwTVuPmy39KDSyUzaKq8VvHfMGR7vtRz4JB7Um/RMRblmzhu4p9tUCk6MTgGA==", - "hasShrinkwrap": true, - "license": "MIT", - "dependencies": { - "@earendil-works/pi-agent-core": "^0.80.3", - "@earendil-works/pi-ai": "^0.80.3", - "@earendil-works/pi-tui": "^0.80.3", - "@silvia-odwyer/photon-node": "0.3.4", - "chalk": "5.6.2", - "cross-spawn": "7.0.6", - "diff": "8.0.4", - "glob": "13.0.6", - "highlight.js": "10.7.3", - "hosted-git-info": "9.0.3", - "ignore": "7.0.5", - "jiti": "2.7.0", - "minimatch": "10.2.5", - "proper-lockfile": "4.1.2", - "semver": "7.8.0", - "typebox": "1.1.38", - "undici": "8.5.0", - "yaml": "2.9.0" - }, - "bin": { - "pi": "dist/cli.js" - }, - "engines": { - "node": ">=22.19.0" - }, - "optionalDependencies": { - "@mariozechner/clipboard": "0.3.9" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@anthropic-ai/sdk": { - "version": "0.91.1", - "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.91.1.tgz", - "integrity": "sha512-LAmu761tSN9r66ixvmciswUj/ZC+1Q4iAfpedTfSVLeswRwnY3n2Nb6Tsk+cLPP28aLOPWeMgIuTuCcMC6W/iw==", - "license": "MIT", - "dependencies": { - "json-schema-to-ts": "^3.1.1" - }, - "bin": { - "anthropic-ai-sdk": "bin/cli" - }, - "peerDependencies": { - "zod": "^3.25.0 || ^4.0.0" - }, - "peerDependenciesMeta": { - "zod": { - "optional": true - } - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-crypto/crc32": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz", - "integrity": "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-crypto/sha256-browser": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", - "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-js": "^5.2.0", - "@aws-crypto/supports-web-crypto": "^5.2.0", - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "@aws-sdk/util-locate-window": "^3.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-crypto/sha256-js": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", - "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-crypto/supports-web-crypto": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", - "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-crypto/util": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", - "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "^3.222.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/client-bedrock-runtime": { - "version": "3.1048.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-bedrock-runtime/-/client-bedrock-runtime-3.1048.0.tgz", - "integrity": "sha512-u+NT61JZEkRFtpL0CAw1N1dwxnaLgwVXQl/zjJxTGgLyS/jTIdg2SdoEoCTHxgDyCnqa1HEi9QOoE9/pYRNpOQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "^3.974.11", - "@aws-sdk/credential-provider-node": "^3.972.42", - "@aws-sdk/eventstream-handler-node": "^3.972.16", - "@aws-sdk/middleware-eventstream": "^3.972.12", - "@aws-sdk/middleware-websocket": "^3.972.19", - "@aws-sdk/token-providers": "3.1048.0", - "@aws-sdk/types": "^3.973.8", - "@smithy/core": "^3.24.2", - "@smithy/fetch-http-handler": "^5.4.2", - "@smithy/node-http-handler": "^4.7.2", - "@smithy/types": "^4.14.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/core": { - "version": "3.974.11", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.974.11.tgz", - "integrity": "sha512-QpnINq5FZH6EOaDEkmHdT7eUunbvD27pDNQypaWjFyYz7Zl1q3UCMQErBZxpmfGfI7MvI2TlK8KTkgNpv8b1ug==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "^3.973.8", - "@aws-sdk/xml-builder": "^3.972.24", - "@aws/lambda-invoke-store": "^0.2.2", - "@smithy/core": "^3.24.2", - "@smithy/signature-v4": "^5.4.2", - "@smithy/types": "^4.14.1", - "bowser": "^2.11.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/credential-provider-env": { - "version": "3.972.37", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.37.tgz", - "integrity": "sha512-/jpPvEh6f7ntmIzf7dNxoNX6Q8vt8UpesCjbW6mFfk4V1NW6bIy9qxcQ6WbA8As5yQhsZOe+xeNd4xHX8kdY2Q==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "^3.974.11", - "@aws-sdk/types": "^3.973.8", - "@smithy/core": "^3.24.2", - "@smithy/types": "^4.14.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/credential-provider-http": { - "version": "3.972.39", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.39.tgz", - "integrity": "sha512-pIgTpisWyWg7X1bUbzSjuUYosYTD0Ghz2M0hkSTmb3a6i3qV3uU+NYJPI/E2XSC0HcsZh5rsLPzeXrkb2DS0Cg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "^3.974.11", - "@aws-sdk/types": "^3.973.8", - "@smithy/core": "^3.24.2", - "@smithy/fetch-http-handler": "^5.4.2", - "@smithy/node-http-handler": "^4.7.2", - "@smithy/types": "^4.14.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.972.41", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.972.41.tgz", - "integrity": "sha512-u2tyjaxJJzW8UtW4SM1ZcPMDwO6y+kV+llvou+Adts0FAKyzes5jG4izQN+KX3yE8ZROpS5y1LJ//xL2iSf76w==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "^3.974.11", - "@aws-sdk/credential-provider-env": "^3.972.37", - "@aws-sdk/credential-provider-http": "^3.972.39", - "@aws-sdk/credential-provider-login": "^3.972.41", - "@aws-sdk/credential-provider-process": "^3.972.37", - "@aws-sdk/credential-provider-sso": "^3.972.41", - "@aws-sdk/credential-provider-web-identity": "^3.972.41", - "@aws-sdk/nested-clients": "^3.997.9", - "@aws-sdk/types": "^3.973.8", - "@smithy/core": "^3.24.2", - "@smithy/credential-provider-imds": "^4.3.2", - "@smithy/types": "^4.14.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/credential-provider-login": { - "version": "3.972.41", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.41.tgz", - "integrity": "sha512-0LBitxXiAiaE5nlFPfpNIww/8FRY/I7WIndWsc9GmNFOM7cE1wNpVNQEGEk9Outg5l8xl+3vybxFyUy4l9q/LQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "^3.974.11", - "@aws-sdk/nested-clients": "^3.997.9", - "@aws-sdk/types": "^3.973.8", - "@smithy/core": "^3.24.2", - "@smithy/types": "^4.14.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/credential-provider-node": { - "version": "3.972.42", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.42.tgz", - "integrity": "sha512-D4oon2zbqqsWOJUM99Gm3/ZyJ0IJvTXVN3PyloGb3kQEyI36fjCZheZj422lAgTWWd6TSHgiImLt3RIaLdv3dQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/credential-provider-env": "^3.972.37", - "@aws-sdk/credential-provider-http": "^3.972.39", - "@aws-sdk/credential-provider-ini": "^3.972.41", - "@aws-sdk/credential-provider-process": "^3.972.37", - "@aws-sdk/credential-provider-sso": "^3.972.41", - "@aws-sdk/credential-provider-web-identity": "^3.972.41", - "@aws-sdk/types": "^3.973.8", - "@smithy/core": "^3.24.2", - "@smithy/credential-provider-imds": "^4.3.2", - "@smithy/types": "^4.14.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/credential-provider-process": { - "version": "3.972.37", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.37.tgz", - "integrity": "sha512-7nVaHBUaWIddASYfVaA9O4D5ZVjewU3sCol9WqZPGfW0nR+0WqE0xHZnD/U2L33PlOB8KNXGKZ6wOES/QijKzg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "^3.974.11", - "@aws-sdk/types": "^3.973.8", - "@smithy/core": "^3.24.2", - "@smithy/types": "^4.14.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.972.41", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.972.41.tgz", - "integrity": "sha512-IOWAWEHe5LkjSKkkUUX9ciV6Y1scHTsnfEkdt5yyC4Slrc7AGbkLPrpntjqh18ksJAMOaVhoBsO8p2WyTcY2wQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "^3.974.11", - "@aws-sdk/nested-clients": "^3.997.9", - "@aws-sdk/token-providers": "3.1048.0", - "@aws-sdk/types": "^3.973.8", - "@smithy/core": "^3.24.2", - "@smithy/types": "^4.14.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.972.41", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.41.tgz", - "integrity": "sha512-mbACk9Yypa8nm4iGZLs0PofOXEcTDOUw6wDnsPXNDNSd2WNXs1tSo+6nc/fh0jLYdfVZThhBL98PHW4aXFsG5A==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "^3.974.11", - "@aws-sdk/nested-clients": "^3.997.9", - "@aws-sdk/types": "^3.973.8", - "@smithy/core": "^3.24.2", - "@smithy/types": "^4.14.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/eventstream-handler-node": { - "version": "3.972.16", - "resolved": "https://registry.npmjs.org/@aws-sdk/eventstream-handler-node/-/eventstream-handler-node-3.972.16.tgz", - "integrity": "sha512-yedpPgKftqjU5SlPFHfqWpOw6xSCRieWRG1euWOlXn4WJxt2VX92VprCa2PpSOXjVCAeK6dTjW9eJRXVig9yGA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "^3.973.8", - "@smithy/core": "^3.24.2", - "@smithy/types": "^4.14.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/middleware-eventstream": { - "version": "3.972.12", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-eventstream/-/middleware-eventstream-3.972.12.tgz", - "integrity": "sha512-tHTHHCHNrq6XklQvlzHBDJG4Iuhh7NVPRdtmvP+nHFA+5sxPlIDzlAHHgfoYHGvT3NXP1yVP/L5c3opUn6T3Qg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "^3.973.8", - "@smithy/core": "^3.24.2", - "@smithy/types": "^4.14.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/middleware-websocket": { - "version": "3.972.19", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-websocket/-/middleware-websocket-3.972.19.tgz", - "integrity": "sha512-mkEhOGYozqKQkbFaVrjwr0faiwwZza1v5/jSY6Tucm3bD+uKTazIUH/4Yo6aMnQD2ua2W9cMP6s8mvwTcjtqHw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "^3.974.11", - "@aws-sdk/types": "^3.973.8", - "@smithy/core": "^3.24.2", - "@smithy/fetch-http-handler": "^5.4.2", - "@smithy/signature-v4": "^5.4.2", - "@smithy/types": "^4.14.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/nested-clients": { - "version": "3.997.9", - "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.997.9.tgz", - "integrity": "sha512-jPR3rnmRI4hWYyzfmTGBr7NblMp8QYYeflHXba1H6+7CGrWVqWKQzaXFQ4qbExqPRsXN3T3L3JxFhr6aouXUGQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "^3.974.11", - "@aws-sdk/signature-v4-multi-region": "^3.996.27", - "@aws-sdk/types": "^3.973.8", - "@smithy/core": "^3.24.2", - "@smithy/fetch-http-handler": "^5.4.2", - "@smithy/node-http-handler": "^4.7.2", - "@smithy/types": "^4.14.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/signature-v4-multi-region": { - "version": "3.996.27", - "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.996.27.tgz", - "integrity": "sha512-0Phbz4t6HI3D3skxvG2uI+VWU034/nSIw1T8d+FPzzQG9EQTrw94o9mOKO2Gv3n3Oc8P7JD7RAUxkoneLWv5Eg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "^3.973.8", - "@smithy/core": "^3.24.2", - "@smithy/signature-v4": "^5.4.2", - "@smithy/types": "^4.14.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/token-providers": { - "version": "3.1048.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1048.0.tgz", - "integrity": "sha512-k0y/GcuesuSfWyUM0WamrGyeZmltRYaPbHO82UDA6mZ/doB+FOHKutikPAtSXMn/hDz970cF+iRuuiYO9VEbAA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "^3.974.11", - "@aws-sdk/nested-clients": "^3.997.9", - "@aws-sdk/types": "^3.973.8", - "@smithy/core": "^3.24.2", - "@smithy/types": "^4.14.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/types": { - "version": "3.973.8", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.973.8.tgz", - "integrity": "sha512-gjlAdtHMbtR9X5iIhVUvbVcy55KnznpC6bkDUWW9z915bi0ckdUr5cjf16Kp6xq0bP5HBD2xzgbL9F9Quv5vUw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.14.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/util-locate-window": { - "version": "3.965.5", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.965.5.tgz", - "integrity": "sha512-WhlJNNINQB+9qtLtZJcpQdgZw3SCDCpXdUJP7cToGwHbCWCnRckGlc6Bx/OhWwIYFNAn+FIydY8SZ0QmVu3xTQ==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/xml-builder": { - "version": "3.972.24", - "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.24.tgz", - "integrity": "sha512-V8z5YcDPfsvzrBlj0xR1vhRtocblhYbqdreCJB/voGd4Sr5zjNAeWxexbnqVtskTJe0vFb5KMqbSL++ePl+zRw==", - "license": "Apache-2.0", - "dependencies": { - "@nodable/entities": "2.1.0", - "@smithy/types": "^4.14.1", - "fast-xml-parser": "5.7.3", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws/lambda-invoke-store": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.2.4.tgz", - "integrity": "sha512-iY8yvjE0y651BixKNPgmv1WrQc+GZ142sb0z4gYnChDDY2YqI4P/jsSopBWrKfAt7LOJAkOXt7rC/hms+WclQQ==", - "license": "Apache-2.0", - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@babel/runtime": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", - "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@earendil-works/pi-agent-core": { - "version": "0.80.3", - "resolved": "https://registry.npmjs.org/@earendil-works/pi-agent-core/-/pi-agent-core-0.80.3.tgz", - "license": "MIT", - "dependencies": { - "@earendil-works/pi-ai": "^0.80.3", - "ignore": "7.0.5", - "typebox": "1.1.38", - "yaml": "2.9.0" - }, - "engines": { - "node": ">=22.19.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@earendil-works/pi-ai": { - "version": "0.80.3", - "resolved": "https://registry.npmjs.org/@earendil-works/pi-ai/-/pi-ai-0.80.3.tgz", - "license": "MIT", - "dependencies": { - "@anthropic-ai/sdk": "0.91.1", - "@aws-sdk/client-bedrock-runtime": "3.1048.0", - "@google/genai": "1.52.0", - "@mistralai/mistralai": "2.2.6", - "@opentelemetry/api": "1.9.0", - "@smithy/node-http-handler": "4.7.3", - "http-proxy-agent": "7.0.2", - "https-proxy-agent": "7.0.6", - "openai": "6.26.0", - "partial-json": "0.1.7", - "typebox": "1.1.38" - }, - "bin": { - "pi-ai": "./dist/cli.js" - }, - "engines": { - "node": ">=22.19.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@earendil-works/pi-tui": { - "version": "0.80.3", - "resolved": "https://registry.npmjs.org/@earendil-works/pi-tui/-/pi-tui-0.80.3.tgz", - "license": "MIT", - "dependencies": { - "get-east-asian-width": "1.6.0", - "marked": "18.0.5" - }, - "engines": { - "node": ">=22.19.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@google/genai": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/@google/genai/-/genai-1.52.0.tgz", - "integrity": "sha512-gwSvbpiN/17O9TbsqSsE/OzZcpv5Fo4RQjdngGgogtuB9RsyJ8ZHhX5KjHj1bp5N9snN2eK8LDGXSaWW2hof8Q==", - "hasInstallScript": true, - "license": "Apache-2.0", - "dependencies": { - "google-auth-library": "^10.3.0", - "p-retry": "^4.6.2", - "protobufjs": "^7.5.4", - "ws": "^8.18.0" - }, - "engines": { - "node": ">=20.0.0" - }, - "peerDependencies": { - "@modelcontextprotocol/sdk": "^1.25.2" - }, - "peerDependenciesMeta": { - "@modelcontextprotocol/sdk": { - "optional": true - } - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@mariozechner/clipboard": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@mariozechner/clipboard/-/clipboard-0.3.9.tgz", - "integrity": "sha512-ABnA53mdfkGZwOFUdZNv2S0CWGO/EIuPj8Vv9xmBFmSYg/qFc7ihO6q5FcQjvoE67kZpWkEc4AhD6B/os04yuA==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">= 10" - }, - "optionalDependencies": { - "@mariozechner/clipboard-darwin-arm64": "0.3.9", - "@mariozechner/clipboard-darwin-universal": "0.3.9", - "@mariozechner/clipboard-darwin-x64": "0.3.9", - "@mariozechner/clipboard-linux-arm64-gnu": "0.3.9", - "@mariozechner/clipboard-linux-arm64-musl": "0.3.9", - "@mariozechner/clipboard-linux-riscv64-gnu": "0.3.9", - "@mariozechner/clipboard-linux-x64-gnu": "0.3.9", - "@mariozechner/clipboard-linux-x64-musl": "0.3.9", - "@mariozechner/clipboard-win32-arm64-msvc": "0.3.9", - "@mariozechner/clipboard-win32-x64-msvc": "0.3.9" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@mariozechner/clipboard-darwin-arm64": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-darwin-arm64/-/clipboard-darwin-arm64-0.3.9.tgz", - "integrity": "sha512-BfgV7vCEWZwJwZJw03r6bP5+tf0iI/ANuQYCxi9RNn7FrWB3yzGuMKCrNLRl6V761vXRdL8+OqZ0wd4TqlsNOQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@mariozechner/clipboard-darwin-universal": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-darwin-universal/-/clipboard-darwin-universal-0.3.9.tgz", - "integrity": "sha512-BGGR4iA9Z2shAjI65eI5xtyb3LYNlDW9X3gxKxDbqtbnREohsrqznov6zpKoIrsRWpzlYVEdKphS7ksJ0/ndSQ==", - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@mariozechner/clipboard-darwin-x64": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-darwin-x64/-/clipboard-darwin-x64-0.3.9.tgz", - "integrity": "sha512-4kURmCbS6nt8uYhtmWpUcJWyPHfmAr5dTpXD1nO3pIfa+TSQ9DbrGOYCKH+aEFW47XhQ4Vp8ZTszie+wfFvDKg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@mariozechner/clipboard-linux-arm64-gnu": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-linux-arm64-gnu/-/clipboard-linux-arm64-gnu-0.3.9.tgz", - "integrity": "sha512-g59OkUGP2DDfCOIKypHeYgv2M55u/cKvXa5dSxFbEJ34XvIQMdcVmpKCkGUro3ZgefXiGVdwguvTMQGpHWzIXw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@mariozechner/clipboard-linux-arm64-musl": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-linux-arm64-musl/-/clipboard-linux-arm64-musl-0.3.9.tgz", - "integrity": "sha512-AGuJdgKsmJdm4Pych7kv3sqe591ERRaAHW3xjLooiFzn8J+PxUyof++7YZrB5Y5tpnTO+K18Og3taj2NpluCRQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@mariozechner/clipboard-linux-riscv64-gnu": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-linux-riscv64-gnu/-/clipboard-linux-riscv64-gnu-0.3.9.tgz", - "integrity": "sha512-DXBEAiuMpk7dhS1a9NzNxVAFi1vaKoPu7rQNgY8LIDLGrK3lnIp3nT10DUum+PKVJoJppIP+NAA8IZe4DMNDPw==", - "cpu": [ - "riscv64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@mariozechner/clipboard-linux-x64-gnu": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-linux-x64-gnu/-/clipboard-linux-x64-gnu-0.3.9.tgz", - "integrity": "sha512-WORrMLd6EpElEME7JRKfSaY34nW1P5LbdgK5YNCS1ncG2LqmITsSMEJ8nh2mpvxb3TxqbOOKgY7k9eMJYlW9Mw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@mariozechner/clipboard-linux-x64-musl": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-linux-x64-musl/-/clipboard-linux-x64-musl-0.3.9.tgz", - "integrity": "sha512-/DHn+1DrfL6oRaPPWXaOKvonFFrni666fxd+zFqiQEfvBH0tsHVWjq9iqBk0oDp0qaPA72lIMy5BptxISBEhZQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@mariozechner/clipboard-win32-arm64-msvc": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-win32-arm64-msvc/-/clipboard-win32-arm64-msvc-0.3.9.tgz", - "integrity": "sha512-O5FHD3ErkMwMhNzAfu3ggy0ug4z7btZuoQgwwxlzPrwV2bxlD6WDpqBY4NCgICAgZdDKdp+loUEKVAVt8aYnhQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@mariozechner/clipboard-win32-x64-msvc": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-win32-x64-msvc/-/clipboard-win32-x64-msvc-0.3.9.tgz", - "integrity": "sha512-ihQC3EufqEY81vhXBgVBtK4prL+wc62zJsSvxrgz7K1hsdt6OObz6v9p3Rn1OG3GJksTTKMJF0u/guMISHPhSA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@mistralai/mistralai": { - "version": "2.2.6", - "resolved": "https://registry.npmjs.org/@mistralai/mistralai/-/mistralai-2.2.6.tgz", - "integrity": "sha512-W8pX7zHxjJvMIpw8JMxeJEleapXX0Q9NPszdNzqkM3MIEoIGPObdodujj+WHteXEvGfaP/AMwlNyRfEzSY6dQQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/semantic-conventions": "^1.40.0", - "ws": "^8.18.0", - "zod": "^3.25.0 || ^4.0.0", - "zod-to-json-schema": "^3.25.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.9.0" - }, - "peerDependenciesMeta": { - "@opentelemetry/api": { - "optional": true - } - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@nodable/entities": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@nodable/entities/-/entities-2.1.0.tgz", - "integrity": "sha512-nyT7T3nbMyBI/lvr6L5TyWbFJAI9FTgVRakNoBqCD+PmID8DzFrrNdLLtHMwMszOtqZa8PAOV24ZqDnQrhQINA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/nodable" - } - ], - "license": "MIT" - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@opentelemetry/api": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", - "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", - "license": "Apache-2.0", - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@opentelemetry/semantic-conventions": { - "version": "1.41.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.41.1.tgz", - "integrity": "sha512-/UhIkaZgPutTFmQ7RnIJGgDXZmtEJ7Dvi86xNTFWcnRxVRNk/aotsqDJYeEvDP+FSMB2SdW+pQzNMcWP0rwuNA==", - "license": "Apache-2.0", - "engines": { - "node": ">=14" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@protobufjs/aspromise": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", - "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", - "license": "BSD-3-Clause" - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@protobufjs/base64": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", - "license": "BSD-3-Clause" - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@protobufjs/codegen": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.5.tgz", - "integrity": "sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==", - "license": "BSD-3-Clause" - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@protobufjs/eventemitter": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.1.tgz", - "integrity": "sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg==", - "license": "BSD-3-Clause" - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@protobufjs/fetch": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.1.tgz", - "integrity": "sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==", - "license": "BSD-3-Clause", - "dependencies": { - "@protobufjs/aspromise": "^1.1.1" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@protobufjs/float": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", - "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", - "license": "BSD-3-Clause" - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@protobufjs/path": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", - "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", - "license": "BSD-3-Clause" - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@protobufjs/pool": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", - "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", - "license": "BSD-3-Clause" - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@protobufjs/utf8": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.1.tgz", - "integrity": "sha512-oOAWABowe8EAbMyWKM0tYDKi8Yaox52D+HWZhAIJqQXbqe0xI/GV7FhLWqlEKreMkfDjshR5FKgi3mnle0h6Eg==", - "license": "BSD-3-Clause" - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@silvia-odwyer/photon-node": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@silvia-odwyer/photon-node/-/photon-node-0.3.4.tgz", - "integrity": "sha512-bnly4BKB3KDTFxrUIcgCLbaeVVS8lrAkri1pEzskpmxu9MdfGQTy8b8EgcD83ywD3RPMsIulY8xJH5Awa+t9fA==", - "license": "Apache-2.0" - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@smithy/core": { - "version": "3.24.3", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.24.3.tgz", - "integrity": "sha512-Ep/7tPamGY8mgESE3LyLKtxJyy6U52WWAqr/3wial47Sj4u3PiIF73AOGI27UyLy9duTkhZbgzodOfLV4TduZg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/crc32": "5.2.0", - "@smithy/types": "^4.14.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@smithy/credential-provider-imds": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.3.3.tgz", - "integrity": "sha512-I2Bti0DKFo2IJyN28ijCsx51BAumEYR4/1yZ1FXyBygy9MqbnMqCev4JPth/MbpRfBSRAX35hITSnAdJRo1u5w==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.24.3", - "@smithy/types": "^4.14.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@smithy/fetch-http-handler": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.4.3.tgz", - "integrity": "sha512-F+DRf8IJazRJgYog2A/yJK7eYVc0rqTlRzO+5ZxjJd4WkZoKz0IJRncf7G6t1pdVT3kryJcwuTFhN1c5m6N47A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.24.3", - "@smithy/types": "^4.14.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@smithy/is-array-buffer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", - "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@smithy/node-http-handler": { - "version": "4.7.3", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.7.3.tgz", - "integrity": "sha512-/jPhevcTFPMVl6KNjbaI47iOg1zxC7IsnX4PQDGVZKMFceOXtB8IEYaB7a9VvkP/3oC60WzTeKocvSI7vLT0vA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.24.3", - "@smithy/types": "^4.14.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@smithy/signature-v4": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.4.3.tgz", - "integrity": "sha512-53+75QuPl6DL+ct6vVEB51FDO5oulXr20TPV46VvJZg76lIlXNWfxi8j+G2V/t0I2qxCBOa3vX/8bmjrpFVo9g==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.24.3", - "@smithy/types": "^4.14.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@smithy/types": { - "version": "4.14.2", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.14.2.tgz", - "integrity": "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@smithy/util-buffer-from": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", - "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/is-array-buffer": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@smithy/util-utf8": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", - "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-buffer-from": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@types/node": { - "version": "22.19.19", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.19.tgz", - "integrity": "sha512-dyh/xO2Fh5bYrfWaaqGrRQQGkNdmYw6AmaAUvYeUMNTWQtvb796ikLdmTchRmOlOiIJ1TDXfWgVx1QkUlQ6Hew==", - "license": "MIT", - "dependencies": { - "undici-types": "~6.21.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/agent-base": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", - "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", - "license": "MIT", - "engines": { - "node": ">= 14" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", - "license": "MIT", - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/bignumber.js": { - "version": "9.3.1", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", - "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==", - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/bowser": { - "version": "2.14.1", - "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.14.1.tgz", - "integrity": "sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==", - "license": "MIT" - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/brace-expansion": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", - "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", - "license": "MIT", - "dependencies": { - "balanced-match": "^4.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/buffer-equal-constant-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", - "license": "BSD-3-Clause" - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/chalk": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", - "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/data-uri-to-buffer": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", - "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", - "license": "MIT", - "engines": { - "node": ">= 12" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/diff": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.4.tgz", - "integrity": "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/ecdsa-sig-formatter": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", - "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", - "license": "Apache-2.0", - "dependencies": { - "safe-buffer": "^5.0.1" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "license": "MIT" - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/fast-xml-builder": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.2.0.tgz", - "integrity": "sha512-00aAWieqff+ZJhsXA4g1g7M8k+7AYoMUUHF+/zFb5U6Uv/P0Vl4QZo84/IcufzYalLuEj9928bXN9PbbFzMF0Q==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT", - "dependencies": { - "path-expression-matcher": "^1.5.0", - "xml-naming": "^0.1.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/fast-xml-parser": { - "version": "5.7.3", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.7.3.tgz", - "integrity": "sha512-C0AaNuC+mscy6vrAQKAc/rMq+zAPHodfHGZu4sGVehvAQt/JLG1O5zEcYcXSY5zSqr4YVgxsB+pHXTq0i7eDlg==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT", - "dependencies": { - "@nodable/entities": "^2.1.0", - "fast-xml-builder": "^1.1.7", - "path-expression-matcher": "^1.5.0", - "strnum": "^2.2.3" - }, - "bin": { - "fxparser": "src/cli/cli.js" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/fetch-blob": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", - "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/jimmywarting" - }, - { - "type": "paypal", - "url": "https://paypal.me/jimmywarting" - } - ], - "license": "MIT", - "dependencies": { - "node-domexception": "^1.0.0", - "web-streams-polyfill": "^3.0.3" - }, - "engines": { - "node": "^12.20 || >= 14.13" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/formdata-polyfill": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", - "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", - "license": "MIT", - "dependencies": { - "fetch-blob": "^3.1.2" - }, - "engines": { - "node": ">=12.20.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/gaxios": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-7.1.4.tgz", - "integrity": "sha512-bTIgTsM2bWn3XklZISBTQX7ZSddGW+IO3bMdGaemHZ3tbqExMENHLx6kKZ/KlejgrMtj8q7wBItt51yegqalrA==", - "license": "Apache-2.0", - "dependencies": { - "extend": "^3.0.2", - "https-proxy-agent": "^7.0.1", - "node-fetch": "^3.3.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/gcp-metadata": { - "version": "8.1.2", - "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-8.1.2.tgz", - "integrity": "sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg==", - "license": "Apache-2.0", - "dependencies": { - "gaxios": "^7.0.0", - "google-logging-utils": "^1.0.0", - "json-bigint": "^1.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/get-east-asian-width": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz", - "integrity": "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/glob": { - "version": "13.0.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", - "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", - "license": "BlueOak-1.0.0", - "dependencies": { - "minimatch": "^10.2.2", - "minipass": "^7.1.3", - "path-scurry": "^2.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/google-auth-library": { - "version": "10.6.2", - "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-10.6.2.tgz", - "integrity": "sha512-e27Z6EThmVNNvtYASwQxose/G57rkRuaRbQyxM2bvYLLX/GqWZ5chWq2EBoUchJbCc57eC9ArzO5wMsEmWftCw==", - "license": "Apache-2.0", - "dependencies": { - "base64-js": "^1.3.0", - "ecdsa-sig-formatter": "^1.0.11", - "gaxios": "^7.1.4", - "gcp-metadata": "8.1.2", - "google-logging-utils": "1.1.3", - "jws": "^4.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/google-logging-utils": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-1.1.3.tgz", - "integrity": "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA==", - "license": "Apache-2.0", - "engines": { - "node": ">=14" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "license": "ISC" - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/highlight.js": { - "version": "10.7.3", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", - "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", - "license": "BSD-3-Clause", - "engines": { - "node": "*" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/hosted-git-info": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-9.0.3.tgz", - "integrity": "sha512-Hc+ghLoSt6QaYZUv0WBiIvmMDZuZZ7oaDvdH8MbfOO4lOsxdXLEvuC6ePoGs9H1X9oCLyq6+NVN0MKqD+ydxyg==", - "license": "ISC", - "dependencies": { - "lru-cache": "^11.1.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/http-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", - "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.0", - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/https-proxy-agent": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", - "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/ignore": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", - "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "license": "ISC" - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/jiti": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", - "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", - "license": "MIT", - "bin": { - "jiti": "lib/jiti-cli.mjs" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/json-bigint": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", - "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", - "license": "MIT", - "dependencies": { - "bignumber.js": "^9.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/json-schema-to-ts": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/json-schema-to-ts/-/json-schema-to-ts-3.1.1.tgz", - "integrity": "sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.18.3", - "ts-algebra": "^2.0.0" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/jwa": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", - "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", - "license": "MIT", - "dependencies": { - "buffer-equal-constant-time": "^1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/jws": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", - "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", - "license": "MIT", - "dependencies": { - "jwa": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/long": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", - "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", - "license": "Apache-2.0" - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/lru-cache": { - "version": "11.4.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.4.0.tgz", - "integrity": "sha512-W+R+kFL4HgVxONq2bhXPi3bGpzGe/yEhVOp233qw9wCRtgncJ15P3bC+e4zZMu4Cq7d+WAJjXGW0uUkifhcatA==", - "license": "BlueOak-1.0.0", - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/marked": { - "version": "18.0.5", - "resolved": "https://registry.npmjs.org/marked/-/marked-18.0.5.tgz", - "integrity": "sha512-S6GcvALHg6K4ohtu4E7x0a1AqhAjp6cV8KhLSyN9qVapnzJkusVBxZRcIU9AeYsbe6P1hKDusSbEOzGyyuce6w==", - "license": "MIT", - "bin": { - "marked": "bin/marked.js" - }, - "engines": { - "node": ">= 20" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/minimatch": { - "version": "10.2.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", - "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", - "license": "BlueOak-1.0.0", - "dependencies": { - "brace-expansion": "^5.0.5" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/minipass": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", - "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/node-domexception": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", - "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", - "deprecated": "Use your platform's native DOMException instead", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/jimmywarting" - }, - { - "type": "github", - "url": "https://paypal.me/jimmywarting" - } - ], - "license": "MIT", - "engines": { - "node": ">=10.5.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/node-fetch": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", - "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", - "license": "MIT", - "dependencies": { - "data-uri-to-buffer": "^4.0.0", - "fetch-blob": "^3.1.4", - "formdata-polyfill": "^4.0.10" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/node-fetch" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/openai": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/openai/-/openai-6.26.0.tgz", - "integrity": "sha512-zd23dbWTjiJ6sSAX6s0HrCZi41JwTA1bQVs0wLQPZ2/5o2gxOJA5wh7yOAUgwYybfhDXyhwlpeQf7Mlgx8EOCA==", - "license": "Apache-2.0", - "bin": { - "openai": "bin/cli" - }, - "peerDependencies": { - "ws": "^8.18.0", - "zod": "^3.25 || ^4.0" - }, - "peerDependenciesMeta": { - "ws": { - "optional": true - }, - "zod": { - "optional": true - } - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/p-retry": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", - "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", - "license": "MIT", - "dependencies": { - "@types/retry": "0.12.0", - "retry": "^0.13.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/p-retry/node_modules/@types/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", - "license": "MIT" - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/partial-json": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/partial-json/-/partial-json-0.1.7.tgz", - "integrity": "sha512-Njv/59hHaokb/hRUjce3Hdv12wd60MtM9Z5Olmn+nehe0QDAsRtRbJPvJ0Z91TusF0SuZRIvnM+S4l6EIP8leA==", - "license": "MIT" - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/path-expression-matcher": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.5.0.tgz", - "integrity": "sha512-cbrerZV+6rvdQrrD+iGMcZFEiiSrbv9Tfdkvnusy6y0x0GKBXREFg/Y65GhIfm0tnLntThhzCnfKwp1WRjeCyQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/path-scurry": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", - "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^11.0.0", - "minipass": "^7.1.2" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/proper-lockfile": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", - "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==", - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.4", - "retry": "^0.12.0", - "signal-exit": "^3.0.2" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/proper-lockfile/node_modules/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/protobufjs": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.6.4.tgz", - "integrity": "sha512-RJJPTTpvFfHcWLkIa2JFWK4XvtSzS0yEWDmunqHXli1h3JlkbcQZXDZdcWxv+JK3Xsl5/UFDPZ0iGm7DAengYw==", - "hasInstallScript": true, - "license": "BSD-3-Clause", - "dependencies": { - "@protobufjs/aspromise": "^1.1.2", - "@protobufjs/base64": "^1.1.2", - "@protobufjs/codegen": "^2.0.5", - "@protobufjs/eventemitter": "^1.1.1", - "@protobufjs/fetch": "^1.1.1", - "@protobufjs/float": "^1.0.2", - "@protobufjs/path": "^1.1.2", - "@protobufjs/pool": "^1.1.0", - "@protobufjs/utf8": "^1.1.1", - "@types/node": ">=13.7.0", - "long": "^5.3.2" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/retry": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", - "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/semver": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz", - "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "license": "ISC" - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/strnum": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.3.0.tgz", - "integrity": "sha512-ums3KNd42PGyx5xaoVTO1mjU1bH3NpY4vsrVlnv9PNGqQj8wd7rJ6nEypLrJ7z5vxK5RP0yMLo6J/Gsm62DI5Q==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT" - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/ts-algebra": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ts-algebra/-/ts-algebra-2.0.0.tgz", - "integrity": "sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==", - "license": "MIT" - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/typebox": { - "version": "1.1.38", - "resolved": "https://registry.npmjs.org/typebox/-/typebox-1.1.38.tgz", - "integrity": "sha512-pZ0aQPmMmXoUvSbeuWf/Hzsc+avNw/Zd6VeE8CFgkVGWyuHPJvqeJJDeJqLve+K70LvjYIoleGcoJHPT17cWoA==", - "license": "MIT" - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/undici": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-8.5.0.tgz", - "integrity": "sha512-xamtWoB1EshgjpmlXd7GGm2VfdDtw1+rD8uhry8pSNW3If6S8E0m2T2+orSKeZXEn/aPJMviCpDBA65WJt8zhg==", - "license": "MIT", - "engines": { - "node": ">=22.19.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "license": "MIT" - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/web-streams-polyfill": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", - "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/ws": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz", - "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/xml-naming": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/xml-naming/-/xml-naming-0.1.0.tgz", - "integrity": "sha512-k8KO9hrMyNk6tUWqUfkTEZbezRRpONVOzUTnc97VnCvyj6Tf9lyUR9EDAIeiVLv56jsMcoXEwjW8Kv5yPY52lw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT", - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/yaml": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", - "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", - "license": "ISC", - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14.6" - }, - "funding": { - "url": "https://github.com/sponsors/eemeli" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/zod": { - "version": "3.25.76", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", - "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/zod-to-json-schema": { - "version": "3.25.2", - "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.2.tgz", - "integrity": "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==", - "license": "ISC", - "peerDependencies": { - "zod": "^3.25.28 || ^4" - } - }, - "node_modules/@emnapi/core": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", - "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.2.1", - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", - "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/wasi-threads": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", - "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz", - "integrity": "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.1.tgz", - "integrity": "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz", - "integrity": "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.1.tgz", - "integrity": "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz", - "integrity": "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz", - "integrity": "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz", - "integrity": "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz", - "integrity": "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz", - "integrity": "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz", - "integrity": "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz", - "integrity": "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz", - "integrity": "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz", - "integrity": "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz", - "integrity": "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz", - "integrity": "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz", - "integrity": "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz", - "integrity": "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz", - "integrity": "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz", - "integrity": "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz", - "integrity": "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz", - "integrity": "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openharmony-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz", - "integrity": "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz", - "integrity": "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz", - "integrity": "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz", - "integrity": "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz", - "integrity": "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@hono/node-server": { - "version": "1.19.14", - "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.14.tgz", - "integrity": "sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw==", - "license": "MIT", - "engines": { - "node": ">=18.14.1" - }, - "peerDependencies": { - "hono": "^4" - } - }, - "node_modules/@modelcontextprotocol/ext-apps": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/ext-apps/-/ext-apps-1.7.2.tgz", - "integrity": "sha512-OOWKDxdAjYDcgHkmzVzccyyag3FK+jBWPaWu4WvTxFsU4R/cgOX4eep66zPRA5n4v6WfxUNibPyvX4iJ7egYTg==", - "license": "MIT", - "workspaces": [ - "examples/*" - ], - "dependencies": { - "@standard-schema/spec": "^1.1.0" - }, - "engines": { - "node": ">=20" - }, - "peerDependencies": { - "@modelcontextprotocol/sdk": "^1.29.0", - "react": "^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0", - "zod": "^3.25.0 || ^4.0.0" - }, - "peerDependenciesMeta": { - "react": { - "optional": true - }, - "react-dom": { - "optional": true - } - } - }, - "node_modules/@modelcontextprotocol/sdk": { - "version": "1.29.0", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.29.0.tgz", - "integrity": "sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==", - "license": "MIT", - "dependencies": { - "@hono/node-server": "^1.19.9", - "ajv": "^8.17.1", - "ajv-formats": "^3.0.1", - "content-type": "^1.0.5", - "cors": "^2.8.5", - "cross-spawn": "^7.0.5", - "eventsource": "^3.0.2", - "eventsource-parser": "^3.0.0", - "express": "^5.2.1", - "express-rate-limit": "^8.2.1", - "hono": "^4.11.4", - "jose": "^6.1.3", - "json-schema-typed": "^8.0.2", - "pkce-challenge": "^5.0.0", - "raw-body": "^3.0.0", - "zod": "^3.25 || ^4.0", - "zod-to-json-schema": "^3.25.1" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@cfworker/json-schema": "^4.1.1", - "zod": "^3.25 || ^4.0" - }, - "peerDependenciesMeta": { - "@cfworker/json-schema": { - "optional": true - }, - "zod": { - "optional": false - } - } - }, - "node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.5.tgz", - "integrity": "sha512-AWPoBRJ9tsnVhor4sjO7rkni+7p+2IAEFj6cx06UgP10jkQHqay/36uRV/bFkgrh18D9vb4cr8Q0Pthskgzy+Q==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@tybys/wasm-util": "^0.10.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - }, - "peerDependencies": { - "@emnapi/core": "^1.7.1", - "@emnapi/runtime": "^1.7.1" - } - }, - "node_modules/@opencode-ai/sdk": { - "version": "1.17.13", - "resolved": "https://registry.npmjs.org/@opencode-ai/sdk/-/sdk-1.17.13.tgz", - "integrity": "sha512-VItOGjMzRQx3zypwmeFLNhCiIx32kxS7FqzIJvVZLfyNGCifs3rfGC9qzNKWcxQo4SjNvAw++v4gWWU6Inv+JQ==", - "license": "MIT", - "dependencies": { - "cross-spawn": "7.0.6" - } - }, - "node_modules/@oxc-project/types": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.133.0.tgz", - "integrity": "sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/Boshen" - } - }, - "node_modules/@pierre/diffs": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/@pierre/diffs/-/diffs-1.2.5.tgz", - "integrity": "sha512-uYOz3Kfs5ED0qY0VraUXzylsEKvZPTVdexboM3QKPx/qBZmTT9F3lKAFuPpY5aIrV04sdHtoFCKStyzEu99U2A==", - "license": "apache-2.0", - "dependencies": { - "@pierre/theme": "1.0.3", - "@shikijs/transformers": "^3.0.0", - "diff": "8.0.3", - "hast-util-to-html": "9.0.5", - "lru_map": "0.4.1", - "shiki": "^3.0.0" - }, - "peerDependencies": { - "react": "^18.3.1 || ^19.0.0", - "react-dom": "^18.3.1 || ^19.0.0" - } - }, - "node_modules/@pierre/theme": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@pierre/theme/-/theme-1.0.3.tgz", - "integrity": "sha512-sWHv11TMoqKxKDgTIk5VbhQjdPhs8DCcBxbjh3mRlS3YOM/OcrWoGX6MM8eBGn9cUu3M46Py0JnxsG2nJaFTuA==", - "license": "MIT", - "engines": { - "vscode": "^1.0.0" - } - }, - "node_modules/@pondwader/socks5-server": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/@pondwader/socks5-server/-/socks5-server-1.0.10.tgz", - "integrity": "sha512-bQY06wzzR8D2+vVCUoBsr5QS2U6UgPUQRmErNwtsuI6vLcyRKkafjkr3KxbtGFf9aBBIV2mcvlsKD1UYaIV+sg==", - "license": "MIT" - }, - "node_modules/@rolldown/binding-android-arm64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.3.tgz", - "integrity": "sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.3.tgz", - "integrity": "sha512-PcAhP+ynjURNyy8SKGl5DQP94aGuB/7JrXJb/t7P+hanXvQVMWzUvRRhBAcg/lNRadBhoUPqSoP4xw5tR/KBEA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.3.tgz", - "integrity": "sha512-9YpfeUvSE2RS7wysJ81uOZkXJz7f7Q55H2Gvp3VEw/EsahqDtrphrZ0EwDLK5vvKOzaCrBsjF8JmnMLcUt78Gg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.3.tgz", - "integrity": "sha512-yB1IlAsSNHncV6SCTL27/MVGR5htvQsoGxIv5KMGXALp+Ll1wYsn+x98M9MW7qa+NdSbvrrY7ANI4wLJ0n1e6g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.3.tgz", - "integrity": "sha512-Yi30IVAAfLUCy2MseFjbB1jAMDl1VMCAas5StnYp8da9+CKvMd2H2cbEjWcw5NPaPqzvYkVIaF1nNUG+b7u/sw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.3.tgz", - "integrity": "sha512-jsO7R8To+AdlYgUmN5sHSCZbfhtMBkO0WUx8iORQnPcMMdgr7qM2DQmMwgabs3GhNztdmoKkMKQFHD6DTMCIQw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.3.tgz", - "integrity": "sha512-VWkUHwWriDciit80wleYwKILoR/KMvxh/IdwS/paX+ZgpuRpCrKLUdadJbc0NpBEiyhpYawsJ73j9aCvOH+f7Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.3.tgz", - "integrity": "sha512-5f1laC0SlIR0yDbFCd8acUhvJIag6N3zC5P7oUPN6wX0aOma+uKJ0wBDH5aq7I1PVI2ttTlhJwzwRIBnLiSGEg==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.3.tgz", - "integrity": "sha512-Iq4ko0r4XsgbrF/LunNgHtAGLRRVE2kXonAXQ/MV0mC6jQpMOhW1SvtZja2EhC/kd05++bP78dsqBeIQyYJ6Yg==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.3.tgz", - "integrity": "sha512-B8m6tD5+/N5FeNQFbKlLA/2yVq9ycQP1SeedyEYYKWBNR3ZQbkvIUcNnDNM03lO1l5F2roiiFJGgvoLLyZXtSg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.3.tgz", - "integrity": "sha512-pSdpdUJHkuCxun9LE7jvgUB9qsRgaiyNNCX7m/AvHTcq67AiT/Yhoxvw5zPfhrM8k/BfP8ce/hMOpthKDpEUow==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.3.tgz", - "integrity": "sha512-OXXS3RKJgX2uLwM+gYyuH5omcH8fL1LJs96pZGgtetVCahON57+d4SJHzTgZiOjxgGkSnpXpOsWuPDGAKAigEg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.3.tgz", - "integrity": "sha512-JTtb8BWFynicNSoPrehsCzBtOKjZ6jhMiPFEmOiuXg1Fl8dn2KHQob+GuPSGR0dryQa1PQJbzjF3dqO/whhjLg==", - "cpu": [ - "wasm32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "1.10.0", - "@emnapi/runtime": "1.10.0", - "@napi-rs/wasm-runtime": "^1.1.4" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.3.tgz", - "integrity": "sha512-gEdFFEN70A/jxb2svrWsN3aDL7OUtmvlOy+6fa2jxG8K0wQ1ZbdeLGnidov6Yu5/733dI5ySfzFlQ/cb0bSz1g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.3.tgz", - "integrity": "sha512-eXB7CHuaQdqmJcc3koCNtNPmT/bj2gc999kUFgBxG8Ac0NdgXc4rkCHhqrgrhN3zddvvvrgzj1e90SuSfmyIXA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/pluginutils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", - "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@shikijs/core": { - "version": "3.23.0", - "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-3.23.0.tgz", - "integrity": "sha512-NSWQz0riNb67xthdm5br6lAkvpDJRTgB36fxlo37ZzM2yq0PQFFzbd8psqC2XMPgCzo1fW6cVi18+ArJ44wqgA==", - "license": "MIT", - "dependencies": { - "@shikijs/types": "3.23.0", - "@shikijs/vscode-textmate": "^10.0.2", - "@types/hast": "^3.0.4", - "hast-util-to-html": "^9.0.5" - } - }, - "node_modules/@shikijs/engine-javascript": { - "version": "3.23.0", - "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-3.23.0.tgz", - "integrity": "sha512-aHt9eiGFobmWR5uqJUViySI1bHMqrAgamWE1TYSUoftkAeCCAiGawPMwM+VCadylQtF4V3VNOZ5LmfItH5f3yA==", - "license": "MIT", - "dependencies": { - "@shikijs/types": "3.23.0", - "@shikijs/vscode-textmate": "^10.0.2", - "oniguruma-to-es": "^4.3.4" - } - }, - "node_modules/@shikijs/engine-oniguruma": { - "version": "3.23.0", - "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.23.0.tgz", - "integrity": "sha512-1nWINwKXxKKLqPibT5f4pAFLej9oZzQTsby8942OTlsJzOBZ0MWKiwzMsd+jhzu8YPCHAswGnnN1YtQfirL35g==", - "license": "MIT", - "dependencies": { - "@shikijs/types": "3.23.0", - "@shikijs/vscode-textmate": "^10.0.2" - } - }, - "node_modules/@shikijs/langs": { - "version": "3.23.0", - "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.23.0.tgz", - "integrity": "sha512-2Ep4W3Re5aB1/62RSYQInK9mM3HsLeB91cHqznAJMuylqjzNVAVCMnNWRHFtcNHXsoNRayP9z1qj4Sq3nMqYXg==", - "license": "MIT", - "dependencies": { - "@shikijs/types": "3.23.0" - } - }, - "node_modules/@shikijs/themes": { - "version": "3.23.0", - "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.23.0.tgz", - "integrity": "sha512-5qySYa1ZgAT18HR/ypENL9cUSGOeI2x+4IvYJu4JgVJdizn6kG4ia5Q1jDEOi7gTbN4RbuYtmHh0W3eccOrjMA==", - "license": "MIT", - "dependencies": { - "@shikijs/types": "3.23.0" - } - }, - "node_modules/@shikijs/transformers": { - "version": "3.23.0", - "resolved": "https://registry.npmjs.org/@shikijs/transformers/-/transformers-3.23.0.tgz", - "integrity": "sha512-F9msZVxdF+krQNSdQ4V+Ja5QemeAoTQ2jxt7nJCwhDsdF1JWS3KxIQXA3lQbyKwS3J61oHRUSv4jYWv3CkaKTQ==", - "license": "MIT", - "dependencies": { - "@shikijs/core": "3.23.0", - "@shikijs/types": "3.23.0" - } - }, - "node_modules/@shikijs/types": { - "version": "3.23.0", - "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.23.0.tgz", - "integrity": "sha512-3JZ5HXOZfYjsYSk0yPwBrkupyYSLpAE26Qc0HLghhZNGTZg/SKxXIIgoxOpmmeQP0RRSDJTk1/vPfw9tbw+jSQ==", - "license": "MIT", - "dependencies": { - "@shikijs/vscode-textmate": "^10.0.2", - "@types/hast": "^3.0.4" - } - }, - "node_modules/@shikijs/vscode-textmate": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz", - "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==", - "license": "MIT" - }, - "node_modules/@stablelib/base64": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@stablelib/base64/-/base64-1.0.1.tgz", - "integrity": "sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==", - "license": "MIT", - "peer": true - }, - "node_modules/@standard-schema/spec": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", - "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", - "license": "MIT" - }, - "node_modules/@tybys/wasm-util": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", - "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@types/better-sqlite3": { - "version": "7.6.13", - "resolved": "https://registry.npmjs.org/@types/better-sqlite3/-/better-sqlite3-7.6.13.tgz", - "integrity": "sha512-NMv9ASNARoKksWtsq/SHakpYAYnhBrQgGD8zkLYk/jaK8jUGn08CfEdTRgYhMypUQAfzSP8W6gNLe0q19/t4VA==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/body-parser": { - "version": "1.19.6", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", - "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "node_modules/@types/connect": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", - "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/express": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.6.tgz", - "integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^5.0.0", - "@types/serve-static": "^2" - } - }, - "node_modules/@types/express-serve-static-core": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.1.tgz", - "integrity": "sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" - } - }, - "node_modules/@types/hast": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", - "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", - "license": "MIT", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/@types/http-errors": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", - "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/mdast": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", - "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", - "license": "MIT", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/@types/node": { - "version": "25.9.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.1.tgz", - "integrity": "sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "undici-types": ">=7.24.0 <7.24.7" - } - }, - "node_modules/@types/qs": { - "version": "6.15.1", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.15.1.tgz", - "integrity": "sha512-GZHUBZR9hckSUhrxmp1nG6NwdpM9fCunJwyThLW1X3AyHgd9IlHb6VANpQQqDr2o/qQp6McZ3y/IA2rVzKzSbw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/range-parser": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", - "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/react": { - "version": "19.2.15", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.15.tgz", - "integrity": "sha512-eRwcGNHve+E8qtEQSSRl6urh+rFop4v8gm6O8rGv25CodbvFdLjA1vVQ1KkiFE0w0UPOnb8tDiFKL5lp0rtY5Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "csstype": "^3.2.2" - } - }, - "node_modules/@types/react-dom": { - "version": "19.2.3", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", - "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "@types/react": "^19.2.0" - } - }, - "node_modules/@types/semver": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/send": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", - "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/serve-static": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-2.2.0.tgz", - "integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/http-errors": "*", - "@types/node": "*" - } - }, - "node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", - "license": "MIT" - }, - "node_modules/@ungap/structured-clone": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.1.tgz", - "integrity": "sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ==", - "license": "ISC" - }, - "node_modules/@vitejs/plugin-react": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.2.tgz", - "integrity": "sha512-DlSMqo4WhThw4vB8Mpn0Woe9J+Jfq1geJ61AKW0QEgLzGMNwtIMdxbDUzLxcun8W7NbJO0e2Jg/Nxm3cCSVzzg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@rolldown/pluginutils": "^1.0.0" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "peerDependencies": { - "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", - "babel-plugin-react-compiler": "^1.0.0", - "vite": "^8.0.0" - }, - "peerDependenciesMeta": { - "@rolldown/plugin-babel": { - "optional": true - }, - "babel-plugin-react-compiler": { - "optional": true - } - } - }, - "node_modules/accepts": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", - "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", - "license": "MIT", - "dependencies": { - "mime-types": "^3.0.0", - "negotiator": "^1.0.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/ajv": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", - "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", - "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", - "license": "MIT", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/better-result": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/better-result/-/better-result-2.10.0.tgz", - "integrity": "sha512-oQhh0y1qo2/ZKdAAEvHZAqKKiHOFU5k/bW96fE2ScgQOVkJRiHwB+nOS1SgFsYqRlxMDWvefXi9Q3px7QvgNDw==", - "license": "MIT" - }, - "node_modules/better-sqlite3": { - "version": "12.10.0", - "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-12.10.0.tgz", - "integrity": "sha512-CyzaZRQKyHkB2ZInfTTl2nvT33EbDpjkLEbE8/Zck3Ll6O0qqvuGdrJ45HgtH+HykRg88ITY3AdreBGN70aBSQ==", - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "bindings": "^1.5.0", - "prebuild-install": "^7.1.1" - }, - "engines": { - "node": "20.x || 22.x || 23.x || 24.x || 25.x || 26.x" - } - }, - "node_modules/bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "license": "MIT", - "dependencies": { - "file-uri-to-path": "1.0.0" - } - }, - "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "license": "MIT", - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/body-parser": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", - "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", - "license": "MIT", - "dependencies": { - "bytes": "^3.1.2", - "content-type": "^1.0.5", - "debug": "^4.4.3", - "http-errors": "^2.0.0", - "iconv-lite": "^0.7.0", - "on-finished": "^2.4.1", - "qs": "^6.14.1", - "raw-body": "^3.0.1", - "type-is": "^2.0.1" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/ccount": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", - "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-entities-html4": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", - "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-entities-legacy": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", - "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "license": "ISC" - }, - "node_modules/comma-separated-tokens": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", - "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/commander": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", - "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/content-disposition": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz", - "integrity": "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", - "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", - "license": "MIT", - "engines": { - "node": ">=6.6.0" - } - }, - "node_modules/cors": { - "version": "2.8.6", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", - "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", - "license": "MIT", - "dependencies": { - "object-assign": "^4", - "vary": "^1" - }, - "engines": { - "node": ">= 0.10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/csstype": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", - "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "license": "MIT", - "dependencies": { - "mimic-response": "^3.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "license": "MIT", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/detect-libc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", - "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", - "license": "Apache-2.0", - "engines": { - "node": ">=8" - } - }, - "node_modules/devlop": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", - "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", - "license": "MIT", - "dependencies": { - "dequal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/diff": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.3.tgz", - "integrity": "sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/drizzle-orm": { - "version": "0.45.2", - "resolved": "https://registry.npmjs.org/drizzle-orm/-/drizzle-orm-0.45.2.tgz", - "integrity": "sha512-kY0BSaTNYWnoDMVoyY8uxmyHjpJW1geOmBMdSSicKo9CIIWkSxMIj2rkeSR51b8KAPB7m+qysjuHme5nKP+E5Q==", - "license": "Apache-2.0", - "peerDependencies": { - "@aws-sdk/client-rds-data": ">=3", - "@cloudflare/workers-types": ">=4", - "@electric-sql/pglite": ">=0.2.0", - "@libsql/client": ">=0.10.0", - "@libsql/client-wasm": ">=0.10.0", - "@neondatabase/serverless": ">=0.10.0", - "@op-engineering/op-sqlite": ">=2", - "@opentelemetry/api": "^1.4.1", - "@planetscale/database": ">=1.13", - "@prisma/client": "*", - "@tidbcloud/serverless": "*", - "@types/better-sqlite3": "*", - "@types/pg": "*", - "@types/sql.js": "*", - "@upstash/redis": ">=1.34.7", - "@vercel/postgres": ">=0.8.0", - "@xata.io/client": "*", - "better-sqlite3": ">=7", - "bun-types": "*", - "expo-sqlite": ">=14.0.0", - "gel": ">=2", - "knex": "*", - "kysely": "*", - "mysql2": ">=2", - "pg": ">=8", - "postgres": ">=3", - "sql.js": ">=1", - "sqlite3": ">=5" - }, - "peerDependenciesMeta": { - "@aws-sdk/client-rds-data": { - "optional": true - }, - "@cloudflare/workers-types": { - "optional": true - }, - "@electric-sql/pglite": { - "optional": true - }, - "@libsql/client": { - "optional": true - }, - "@libsql/client-wasm": { - "optional": true - }, - "@neondatabase/serverless": { - "optional": true - }, - "@op-engineering/op-sqlite": { - "optional": true - }, - "@opentelemetry/api": { - "optional": true - }, - "@planetscale/database": { - "optional": true - }, - "@prisma/client": { - "optional": true - }, - "@tidbcloud/serverless": { - "optional": true - }, - "@types/better-sqlite3": { - "optional": true - }, - "@types/pg": { - "optional": true - }, - "@types/sql.js": { - "optional": true - }, - "@upstash/redis": { - "optional": true - }, - "@vercel/postgres": { - "optional": true - }, - "@xata.io/client": { - "optional": true - }, - "better-sqlite3": { - "optional": true - }, - "bun-types": { - "optional": true - }, - "expo-sqlite": { - "optional": true - }, - "gel": { - "optional": true - }, - "knex": { - "optional": true - }, - "kysely": { - "optional": true - }, - "mysql2": { - "optional": true - }, - "pg": { - "optional": true - }, - "postgres": { - "optional": true - }, - "prisma": { - "optional": true - }, - "sql.js": { - "optional": true - }, - "sqlite3": { - "optional": true - } - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "license": "MIT" - }, - "node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/end-of-stream": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", - "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", - "license": "MIT", - "dependencies": { - "once": "^1.4.0" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-object-atoms": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", - "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/esbuild": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz", - "integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.28.1", - "@esbuild/android-arm": "0.28.1", - "@esbuild/android-arm64": "0.28.1", - "@esbuild/android-x64": "0.28.1", - "@esbuild/darwin-arm64": "0.28.1", - "@esbuild/darwin-x64": "0.28.1", - "@esbuild/freebsd-arm64": "0.28.1", - "@esbuild/freebsd-x64": "0.28.1", - "@esbuild/linux-arm": "0.28.1", - "@esbuild/linux-arm64": "0.28.1", - "@esbuild/linux-ia32": "0.28.1", - "@esbuild/linux-loong64": "0.28.1", - "@esbuild/linux-mips64el": "0.28.1", - "@esbuild/linux-ppc64": "0.28.1", - "@esbuild/linux-riscv64": "0.28.1", - "@esbuild/linux-s390x": "0.28.1", - "@esbuild/linux-x64": "0.28.1", - "@esbuild/netbsd-arm64": "0.28.1", - "@esbuild/netbsd-x64": "0.28.1", - "@esbuild/openbsd-arm64": "0.28.1", - "@esbuild/openbsd-x64": "0.28.1", - "@esbuild/openharmony-arm64": "0.28.1", - "@esbuild/sunos-x64": "0.28.1", - "@esbuild/win32-arm64": "0.28.1", - "@esbuild/win32-ia32": "0.28.1", - "@esbuild/win32-x64": "0.28.1" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "license": "MIT" - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/eventsource": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", - "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==", - "license": "MIT", - "dependencies": { - "eventsource-parser": "^3.0.1" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/eventsource-parser": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.1.0.tgz", - "integrity": "sha512-kJezFj9YFAMLeORyi7aCLxLbD5/qWMQnoMVlVPyHIll7lgRJCc3JVln9Vgl9nwQi0YkMnhdGTMNn7CkRRAptMg==", - "license": "MIT", - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/expand-template": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", - "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", - "license": "(MIT OR WTFPL)", - "engines": { - "node": ">=6" - } - }, - "node_modules/express": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", - "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", - "license": "MIT", - "dependencies": { - "accepts": "^2.0.0", - "body-parser": "^2.2.1", - "content-disposition": "^1.0.0", - "content-type": "^1.0.5", - "cookie": "^0.7.1", - "cookie-signature": "^1.2.1", - "debug": "^4.4.0", - "depd": "^2.0.0", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "etag": "^1.8.1", - "finalhandler": "^2.1.0", - "fresh": "^2.0.0", - "http-errors": "^2.0.0", - "merge-descriptors": "^2.0.0", - "mime-types": "^3.0.0", - "on-finished": "^2.4.1", - "once": "^1.4.0", - "parseurl": "^1.3.3", - "proxy-addr": "^2.0.7", - "qs": "^6.14.0", - "range-parser": "^1.2.1", - "router": "^2.2.0", - "send": "^1.1.0", - "serve-static": "^2.2.0", - "statuses": "^2.0.1", - "type-is": "^2.0.1", - "vary": "^1.1.2" - }, - "engines": { - "node": ">= 18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/express-rate-limit": { - "version": "8.5.2", - "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.5.2.tgz", - "integrity": "sha512-5Kb34ipNX694DH48vN9irak1Qx30nb0PLYHXfJgw4YEjiC3ZEmZJhwOp+VfiCYwFzvFTdB9QkArYS5kXa2cx2A==", - "license": "MIT", - "dependencies": { - "ip-address": "^10.2.0" - }, - "engines": { - "node": ">= 16" - }, - "funding": { - "url": "https://github.com/sponsors/express-rate-limit" - }, - "peerDependencies": { - "express": ">= 4.11" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "license": "MIT" - }, - "node_modules/fast-sha256": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/fast-sha256/-/fast-sha256-1.3.0.tgz", - "integrity": "sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==", - "license": "Unlicense", - "peer": true - }, - "node_modules/fast-string-truncated-width": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/fast-string-truncated-width/-/fast-string-truncated-width-3.0.3.tgz", - "integrity": "sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==", - "license": "MIT" - }, - "node_modules/fast-string-width": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/fast-string-width/-/fast-string-width-3.0.2.tgz", - "integrity": "sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==", - "license": "MIT", - "dependencies": { - "fast-string-truncated-width": "^3.0.2" - } - }, - "node_modules/fast-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", - "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/fast-wrap-ansi": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/fast-wrap-ansi/-/fast-wrap-ansi-0.2.2.tgz", - "integrity": "sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==", - "license": "MIT", - "dependencies": { - "fast-string-width": "^3.0.2" - } - }, - "node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "license": "MIT" - }, - "node_modules/finalhandler": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", - "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", - "license": "MIT", - "dependencies": { - "debug": "^4.4.0", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "on-finished": "^2.4.1", - "parseurl": "^1.3.3", - "statuses": "^2.0.1" - }, - "engines": { - "node": ">= 18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fresh": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", - "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "license": "MIT" - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/github-from-package": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", - "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", - "license": "MIT" - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", - "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/hast-util-to-html": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz", - "integrity": "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "ccount": "^2.0.0", - "comma-separated-tokens": "^2.0.0", - "hast-util-whitespace": "^3.0.0", - "html-void-elements": "^3.0.0", - "mdast-util-to-hast": "^13.0.0", - "property-information": "^7.0.0", - "space-separated-tokens": "^2.0.0", - "stringify-entities": "^4.0.0", - "zwitch": "^2.0.4" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-whitespace": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", - "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hono": { - "version": "4.12.25", - "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.25.tgz", - "integrity": "sha512-2NFaIyNVgJmBs/ecmtGzlmluTFs5cHEWGTdu0t1HBwYzoGXOL5nUQBRMXsXWla5i4KkG//QMzVP88m1+I3fdAQ==", - "license": "MIT", - "engines": { - "node": ">=16.9.0" - } - }, - "node_modules/html-void-elements": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", - "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/http-errors": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", - "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", - "license": "MIT", - "dependencies": { - "depd": "~2.0.0", - "inherits": "~2.0.4", - "setprototypeof": "~1.2.0", - "statuses": "~2.0.2", - "toidentifier": "~1.0.1" - }, - "engines": { - "node": ">= 0.8" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/iconv-lite": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", - "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC" - }, - "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "license": "ISC" - }, - "node_modules/ip-address": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.2.0.tgz", - "integrity": "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==", - "license": "MIT", - "engines": { - "node": ">= 12" - } - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/is-promise": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", - "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", - "license": "MIT" - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "license": "ISC" - }, - "node_modules/jose": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.3.tgz", - "integrity": "sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/panva" - } - }, - "node_modules/json-schema-to-ts": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/json-schema-to-ts/-/json-schema-to-ts-3.1.1.tgz", - "integrity": "sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g==", - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/runtime": "^7.18.3", - "ts-algebra": "^2.0.0" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "license": "MIT" - }, - "node_modules/json-schema-typed": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz", - "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==", - "license": "BSD-2-Clause" - }, - "node_modules/jsonc-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", - "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", - "license": "MIT" - }, - "node_modules/lightningcss": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", - "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", - "dev": true, - "license": "MPL-2.0", - "dependencies": { - "detect-libc": "^2.0.3" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - }, - "optionalDependencies": { - "lightningcss-android-arm64": "1.32.0", - "lightningcss-darwin-arm64": "1.32.0", - "lightningcss-darwin-x64": "1.32.0", - "lightningcss-freebsd-x64": "1.32.0", - "lightningcss-linux-arm-gnueabihf": "1.32.0", - "lightningcss-linux-arm64-gnu": "1.32.0", - "lightningcss-linux-arm64-musl": "1.32.0", - "lightningcss-linux-x64-gnu": "1.32.0", - "lightningcss-linux-x64-musl": "1.32.0", - "lightningcss-win32-arm64-msvc": "1.32.0", - "lightningcss-win32-x64-msvc": "1.32.0" - } - }, - "node_modules/lightningcss-android-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", - "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-darwin-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", - "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-darwin-x64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", - "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-freebsd-x64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", - "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", - "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", - "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-musl": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", - "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", - "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-x64-musl": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", - "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", - "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", - "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lru_map": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/lru_map/-/lru_map-0.4.1.tgz", - "integrity": "sha512-I+lBvqMMFfqaV8CJCISjI3wbjmwVu/VyOoU7+qtu9d7ioW5klMgsTTiUOUp+DJvfTTzKXoPbyC6YfgkNcyPSOg==", - "license": "MIT" - }, - "node_modules/lucide": { - "version": "1.24.0", - "resolved": "https://registry.npmjs.org/lucide/-/lucide-1.24.0.tgz", - "integrity": "sha512-oMAaeuNDc5VCnBb3IjwKYGRT56tqanUm1fyDFT5Tl8hWSZND59gztgjvXje08jKLPVAq0gHJcwZUE8GCQxzBeg==", - "license": "ISC" - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/mdast-util-to-hast": { - "version": "13.2.1", - "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", - "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "@ungap/structured-clone": "^1.0.0", - "devlop": "^1.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "trim-lines": "^3.0.0", - "unist-util-position": "^5.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/media-typer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", - "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/merge-descriptors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", - "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-encode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", - "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-sanitize-uri": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", - "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-encode": "^2.0.0", - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-types": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", - "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/mime-db": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", - "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", - "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", - "license": "MIT", - "dependencies": { - "mime-db": "^1.54.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/mkdirp-classic": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", - "license": "MIT" - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/nanoid": { - "version": "3.3.12", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", - "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/napi-build-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", - "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", - "license": "MIT" - }, - "node_modules/negotiator": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", - "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/node-abi": { - "version": "3.92.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.92.0.tgz", - "integrity": "sha512-KdHvFWZjEKDf0cakgFjebl371GPsISX2oZHcuyKqM7DtogIsHrqKeLTo8wBHxaXRAQlY2PsPlZmfo+9ZCxEREQ==", - "license": "MIT", - "dependencies": { - "semver": "^7.3.5" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/node-addon-api": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", - "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", - "license": "MIT", - "optional": true - }, - "node_modules/node-forge": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.4.0.tgz", - "integrity": "sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ==", - "license": "(BSD-3-Clause OR GPL-2.0)", - "engines": { - "node": ">= 6.13.0" - } - }, - "node_modules/node-pty": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/node-pty/-/node-pty-1.1.0.tgz", - "integrity": "sha512-20JqtutY6JPXTUnL0ij1uad7Qe1baT46lyolh2sSENDd4sTzKZ4nmAFkeAARDKwmlLjPx6XKRlwRUxwjOy+lUg==", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "dependencies": { - "node-addon-api": "^7.1.0" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-inspect": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/oniguruma-parser": { - "version": "0.12.2", - "resolved": "https://registry.npmjs.org/oniguruma-parser/-/oniguruma-parser-0.12.2.tgz", - "integrity": "sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw==", - "license": "MIT" - }, - "node_modules/oniguruma-to-es": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-4.3.6.tgz", - "integrity": "sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA==", - "license": "MIT", - "dependencies": { - "oniguruma-parser": "^0.12.2", - "regex": "^6.1.0", - "regex-recursion": "^6.0.2" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-to-regexp": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", - "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pkce-challenge": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz", - "integrity": "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==", - "license": "MIT", - "engines": { - "node": ">=16.20.0" - } - }, - "node_modules/postcss": { - "version": "8.5.15", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", - "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.12", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/prebuild-install": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", - "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", - "deprecated": "No longer maintained. Please contact the author of the relevant native addon; alternatives are available.", - "license": "MIT", - "dependencies": { - "detect-libc": "^2.0.0", - "expand-template": "^2.0.3", - "github-from-package": "0.0.0", - "minimist": "^1.2.3", - "mkdirp-classic": "^0.5.3", - "napi-build-utils": "^2.0.0", - "node-abi": "^3.3.0", - "pump": "^3.0.0", - "rc": "^1.2.7", - "simple-get": "^4.0.0", - "tar-fs": "^2.0.0", - "tunnel-agent": "^0.6.0" - }, - "bin": { - "prebuild-install": "bin.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/property-information": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", - "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "license": "MIT", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/pump": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", - "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", - "license": "MIT", - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/qs": { - "version": "6.15.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz", - "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==", - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", - "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", - "license": "MIT", - "dependencies": { - "bytes": "~3.1.2", - "http-errors": "~2.0.1", - "iconv-lite": "~0.7.0", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", - "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "bin": { - "rc": "cli.js" - } - }, - "node_modules/react": { - "version": "19.2.6", - "resolved": "https://registry.npmjs.org/react/-/react-19.2.6.tgz", - "integrity": "sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-dom": { - "version": "19.2.6", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.6.tgz", - "integrity": "sha512-0prMI+hvBbPjsWnxDLxlCGyM8PN6UuWjEUCYmZhO67xIV9Xasa/r/vDnq+Xyq4Lo27g8QSbO5YzARu0D1Sps3g==", - "license": "MIT", - "dependencies": { - "scheduler": "^0.27.0" - }, - "peerDependencies": { - "react": "^19.2.6" - } - }, - "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/regex/-/regex-6.1.0.tgz", - "integrity": "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==", - "license": "MIT", - "dependencies": { - "regex-utilities": "^2.3.0" - } - }, - "node_modules/regex-recursion": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/regex-recursion/-/regex-recursion-6.0.2.tgz", - "integrity": "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==", - "license": "MIT", - "dependencies": { - "regex-utilities": "^2.3.0" - } - }, - "node_modules/regex-utilities": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/regex-utilities/-/regex-utilities-2.3.0.tgz", - "integrity": "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==", - "license": "MIT" - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/rolldown": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.3.tgz", - "integrity": "sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@oxc-project/types": "=0.133.0", - "@rolldown/pluginutils": "^1.0.0" - }, - "bin": { - "rolldown": "bin/cli.mjs" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.0.3", - "@rolldown/binding-darwin-arm64": "1.0.3", - "@rolldown/binding-darwin-x64": "1.0.3", - "@rolldown/binding-freebsd-x64": "1.0.3", - "@rolldown/binding-linux-arm-gnueabihf": "1.0.3", - "@rolldown/binding-linux-arm64-gnu": "1.0.3", - "@rolldown/binding-linux-arm64-musl": "1.0.3", - "@rolldown/binding-linux-ppc64-gnu": "1.0.3", - "@rolldown/binding-linux-s390x-gnu": "1.0.3", - "@rolldown/binding-linux-x64-gnu": "1.0.3", - "@rolldown/binding-linux-x64-musl": "1.0.3", - "@rolldown/binding-openharmony-arm64": "1.0.3", - "@rolldown/binding-wasm32-wasi": "1.0.3", - "@rolldown/binding-win32-arm64-msvc": "1.0.3", - "@rolldown/binding-win32-x64-msvc": "1.0.3" - } - }, - "node_modules/router": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", - "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", - "license": "MIT", - "dependencies": { - "debug": "^4.4.0", - "depd": "^2.0.0", - "is-promise": "^4.0.0", - "parseurl": "^1.3.3", - "path-to-regexp": "^8.0.0" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "license": "MIT" - }, - "node_modules/scheduler": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", - "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", - "license": "MIT" - }, - "node_modules/semver": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz", - "integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/send": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", - "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", - "license": "MIT", - "dependencies": { - "debug": "^4.4.3", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "etag": "^1.8.1", - "fresh": "^2.0.0", - "http-errors": "^2.0.1", - "mime-types": "^3.0.2", - "ms": "^2.1.3", - "on-finished": "^2.4.1", - "range-parser": "^1.2.1", - "statuses": "^2.0.2" - }, - "engines": { - "node": ">= 18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/serve-static": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", - "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", - "license": "MIT", - "dependencies": { - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "parseurl": "^1.3.3", - "send": "^1.2.0" - }, - "engines": { - "node": ">= 18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "license": "ISC" - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/shiki": { - "version": "3.23.0", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-3.23.0.tgz", - "integrity": "sha512-55Dj73uq9ZXL5zyeRPzHQsK7Nbyt6Y10k5s7OjuFZGMhpp4r/rsLBH0o/0fstIzX1Lep9VxefWljK/SKCzygIA==", - "license": "MIT", - "dependencies": { - "@shikijs/core": "3.23.0", - "@shikijs/engine-javascript": "3.23.0", - "@shikijs/engine-oniguruma": "3.23.0", - "@shikijs/langs": "3.23.0", - "@shikijs/themes": "3.23.0", - "@shikijs/types": "3.23.0", - "@shikijs/vscode-textmate": "^10.0.2", - "@types/hast": "^3.0.4" - } - }, - "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-list": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", - "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-weakmap": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3", - "side-channel-map": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/simple-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", - "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/simple-get": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", - "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "decompress-response": "^6.0.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - } - }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "license": "MIT" - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/space-separated-tokens": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", - "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/standardwebhooks": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/standardwebhooks/-/standardwebhooks-1.0.0.tgz", - "integrity": "sha512-BbHGOQK9olHPMvQNHWul6MYlrRTAOKn03rOe4A8O3CLWhNf4YHBqq2HJKKC+sfqpxiBY52pNeesD6jIiLDz8jg==", - "license": "MIT", - "peer": true, - "dependencies": { - "@stablelib/base64": "^1.0.0", - "fast-sha256": "^1.3.0" - } - }, - "node_modules/statuses": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", - "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/stringify-entities": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", - "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", - "license": "MIT", - "dependencies": { - "character-entities-html4": "^2.0.0", - "character-entities-legacy": "^3.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/tar-fs": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", - "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", - "license": "MIT", - "dependencies": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^2.1.4" - } - }, - "node_modules/tar-stream": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", - "license": "MIT", - "dependencies": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/tinyglobby": { - "version": "0.2.17", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", - "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fdir": "^6.5.0", - "picomatch": "^4.0.4" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "license": "MIT", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/trim-lines": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", - "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/ts-algebra": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ts-algebra/-/ts-algebra-2.0.0.tgz", - "integrity": "sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==", - "license": "MIT", - "peer": true - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, - "license": "0BSD", - "optional": true - }, - "node_modules/tsx": { - "version": "4.22.3", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.22.3.tgz", - "integrity": "sha512-mdoNxBC/cSQObGGVQ5Bpn5i+yv7j68gk3Nfm3wFjcJg3Z0Mix9jzAFfP12prmm5eVGmDKtp0yyArrs0Q+8gZHg==", - "dev": true, - "license": "MIT", - "dependencies": { - "esbuild": "~0.28.0" - }, - "bin": { - "tsx": "dist/cli.mjs" - }, - "engines": { - "node": ">=18.0.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - } - }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "license": "Apache-2.0", - "dependencies": { - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": "*" - } - }, - "node_modules/type-is": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz", - "integrity": "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==", - "license": "MIT", - "dependencies": { - "content-type": "^2.0.0", - "media-typer": "^1.1.0", - "mime-types": "^3.0.0" - }, - "engines": { - "node": ">= 18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/type-is/node_modules/content-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", - "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/typescript": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", - "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/undici-types": { - "version": "7.24.6", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", - "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/unist-util-is": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", - "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-position": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", - "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-stringify-position": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", - "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.1.0.tgz", - "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit-parents": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", - "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "license": "MIT" - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/vfile": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", - "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vfile-message": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", - "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-stringify-position": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vite": { - "version": "8.0.16", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.16.tgz", - "integrity": "sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==", - "dev": true, - "license": "MIT", - "dependencies": { - "lightningcss": "^1.32.0", - "picomatch": "^4.0.4", - "postcss": "^8.5.15", - "rolldown": "1.0.3", - "tinyglobby": "^0.2.17" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^20.19.0 || >=22.12.0", - "@vitejs/devtools": "^0.1.18", - "esbuild": "^0.27.0 || ^0.28.0", - "jiti": ">=1.21.0", - "less": "^4.0.0", - "sass": "^1.70.0", - "sass-embedded": "^1.70.0", - "stylus": ">=0.54.8", - "sugarss": "^5.0.0", - "terser": "^5.16.0", - "tsx": "^4.8.1", - "yaml": "^2.4.2" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "@vitejs/devtools": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "jiti": { - "optional": true - }, - "less": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - }, - "tsx": { - "optional": true - }, - "yaml": { - "optional": true - } - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/wrappy": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "license": "ISC" - }, - "node_modules/yaml": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", - "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", - "license": "ISC", - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14.6" - }, - "funding": { - "url": "https://github.com/sponsors/eemeli" - } - }, - "node_modules/zod": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", - "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - }, - "node_modules/zod-to-json-schema": { - "version": "3.25.2", - "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.2.tgz", - "integrity": "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==", - "license": "ISC", - "peerDependencies": { - "zod": "^3.25.28 || ^4" - } - }, - "node_modules/zwitch": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", - "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - } - } -} diff --git a/package.json b/package.json index 3dad18389..45bb446ce 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "engines": { "node": ">=22.19 <27" }, + "packageManager": "pnpm@11.25.0", "bin": { "devspace": "bin/devspace.js", "devspace-agentd": "bin/devspace-agentd.js" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 000000000..26f7684ef --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,3955 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@agentclientprotocol/sdk': + specifier: ^1.1.0 + version: 1.1.0(zod@4.4.3) + '@anthropic-ai/claude-agent-sdk': + specifier: 0.3.200 + version: 0.3.200(@anthropic-ai/sdk@0.110.0(zod@4.4.3))(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(zod@4.4.3) + '@anthropic-ai/sandbox-runtime': + specifier: 0.0.71 + version: 0.0.71 + '@clack/prompts': + specifier: ^1.5.1 + version: 1.5.1 + '@earendil-works/pi-coding-agent': + specifier: ^0.80.3 + version: 0.80.3(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(ws@8.21.0)(zod@4.4.3) + '@modelcontextprotocol/ext-apps': + specifier: ^1.7.2 + version: 1.7.2(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(zod@4.4.3) + '@modelcontextprotocol/sdk': + specifier: ^1.29.0 + version: 1.29.0(zod@4.4.3) + '@opencode-ai/sdk': + specifier: ^1.17.13 + version: 1.17.13 + '@pierre/diffs': + specifier: ^1.2.5 + version: 1.2.5(react-dom@19.2.6(react@19.2.6))(react@19.2.6) + better-result: + specifier: ^2.10.0 + version: 2.10.0 + better-sqlite3: + specifier: ^12.10.0 + version: 12.10.0 + cross-spawn: + specifier: ^7.0.6 + version: 7.0.6 + diff: + specifier: ^8.0.3 + version: 8.0.3 + drizzle-orm: + specifier: ^0.45.2 + version: 0.45.2(@opentelemetry/api@1.9.0)(@types/better-sqlite3@7.6.13)(better-sqlite3@12.10.0) + express: + specifier: ^5.2.1 + version: 5.2.1 + jsonc-parser: + specifier: ^3.3.1 + version: 3.3.1 + lucide: + specifier: ^1.24.0 + version: 1.24.0 + react: + specifier: ^19.2.6 + version: 19.2.6 + react-dom: + specifier: ^19.2.6 + version: 19.2.6(react@19.2.6) + semver: + specifier: ^7.8.4 + version: 7.8.4 + yaml: + specifier: ^2.9.0 + version: 2.9.0 + zod: + specifier: ^4.4.3 + version: 4.4.3 + devDependencies: + '@types/better-sqlite3': + specifier: ^7.6.13 + version: 7.6.13 + '@types/express': + specifier: ^5.0.6 + version: 5.0.6 + '@types/node': + specifier: ^25.9.1 + version: 25.9.1 + '@types/react': + specifier: ^19.2.15 + version: 19.2.15 + '@types/react-dom': + specifier: ^19.2.3 + version: 19.2.3(@types/react@19.2.15) + '@types/semver': + specifier: ^7.7.1 + version: 7.7.1 + '@vitejs/plugin-react': + specifier: ^6.0.2 + version: 6.0.2(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.3)(yaml@2.9.0)) + tsx: + specifier: ^4.22.3 + version: 4.22.3 + typescript: + specifier: ^6.0.3 + version: 6.0.3 + vite: + specifier: ^8.0.14 + version: 8.0.16(@types/node@25.9.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.3)(yaml@2.9.0) + optionalDependencies: + node-pty: + specifier: ^1.1.0 + version: 1.1.0 + +packages: + + '@agentclientprotocol/sdk@1.1.0': + resolution: {integrity: sha512-NT2KqphUJ3w6EksUL51ZhJgIYgq/ZLGcBPkyMKgRSO5PMVwe9DnKKX+Htnvk6KHh6dUuh34UHK4gKp+4te1Mdg==} + peerDependencies: + zod: ^3.25.0 || ^4.0.0 + + '@anthropic-ai/claude-agent-sdk-darwin-arm64@0.3.200': + resolution: {integrity: sha512-8UzzInVdRPDNIOvrAxYbHHJD/u13WSBx9fvEeuZnsZ6rZh0qnSI1QwU8Due0V2+m+ZnT3cEonmXDvo2ee/icWg==} + cpu: [arm64] + os: [darwin] + + '@anthropic-ai/claude-agent-sdk-darwin-x64@0.3.200': + resolution: {integrity: sha512-DCwlQoO8HWGuFElE+Q5pYkiBTalXjjMATRAxXyc94fI6m1ZRqyba66dOea+zTmzHPpOb6zSoHYNLiXy7EjNpcg==} + cpu: [x64] + os: [darwin] + + '@anthropic-ai/claude-agent-sdk-linux-arm64-musl@0.3.200': + resolution: {integrity: sha512-ak0l+zpz3dKPjnBegUhOs1Y5xFveEQ1AVqmq6s8Q7qd3vO4SrDPiUOpxRkjkqWyGD8r8w+ezG+unf3U9IZ6DRg==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@anthropic-ai/claude-agent-sdk-linux-arm64@0.3.200': + resolution: {integrity: sha512-NAEonp086ZOsf+3o/9Y5JRclO6C4n4ceiSuCpSDV6SSUOLBmCRi7r/PJOoMsIWwMshC6fnnkDKZamTpHjr75eg==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@anthropic-ai/claude-agent-sdk-linux-x64-musl@0.3.200': + resolution: {integrity: sha512-Sf5TTCO3bc5ty7FX5F19WT3xbtU+f1biYD9+dDJ7YHyYFWuiPlWcnCJ8El8RSwCTuvz3OexJLwCqGHRWOC3eBg==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@anthropic-ai/claude-agent-sdk-linux-x64@0.3.200': + resolution: {integrity: sha512-0R/In8G4fZLFFEIA1SqXRRf9mzDGx7roHpMawNdTT1QlG4XftGTlKMxfukt/YcxwzsNPWg4hJSkEDxsb+3J6FA==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@anthropic-ai/claude-agent-sdk-win32-arm64@0.3.200': + resolution: {integrity: sha512-iJx10bdrk3afa/Oq9QHRh2HaINT/xnsm5OrFNNLbix2CoOEY5lA7f0lk/s0OMiWnfXdv5vvtADpgZ5tvUoQykA==} + cpu: [arm64] + os: [win32] + + '@anthropic-ai/claude-agent-sdk-win32-x64@0.3.200': + resolution: {integrity: sha512-Mka8YDpDIiSJcbrdoBhzX3S0n9DYcoYaEjS7lxwX3GyPi5PvXV4UBuXzj++7ieV/KS4w32Sm3mHQRpeVwnJZ0A==} + cpu: [x64] + os: [win32] + + '@anthropic-ai/claude-agent-sdk@0.3.200': + resolution: {integrity: sha512-o13TM3boFIJE4oZdQDFw5TQfiev1sBoxwzKM2QGj/NPtxriGTP0PKNAQsGZvTsiEOIIH5rzPr/H81xVkkAw23g==} + engines: {node: '>=18.0.0'} + peerDependencies: + '@anthropic-ai/sdk': '>=0.93.0' + '@modelcontextprotocol/sdk': ^1.29.0 + zod: ^4.0.0 + + '@anthropic-ai/sandbox-runtime@0.0.71': + resolution: {integrity: sha512-/ZMCavpMElD0ku2BlA95vezKUsVN0DD/wVd3WIEAfFjkTF2nsmzQA+MhejIWhuSUS9HpxMtTj57eFL+kdbKZ/A==} + engines: {node: '>=20.11.0'} + hasBin: true + + '@anthropic-ai/sdk@0.110.0': + resolution: {integrity: sha512-hOP4bNYXDFHDxxiEgzlILXrxZIYCDnhe8sry0RDRKD/QnsEpvZcQpablCdm9X/WuD/YgOiSIkkqsL1mLLlTqJw==} + hasBin: true + peerDependencies: + zod: ^3.25.0 || ^4.0.0 + peerDependenciesMeta: + zod: + optional: true + + '@anthropic-ai/sdk@0.91.1': + resolution: {integrity: sha512-LAmu761tSN9r66ixvmciswUj/ZC+1Q4iAfpedTfSVLeswRwnY3n2Nb6Tsk+cLPP28aLOPWeMgIuTuCcMC6W/iw==} + hasBin: true + peerDependencies: + zod: ^3.25.0 || ^4.0.0 + peerDependenciesMeta: + zod: + optional: true + + '@aws-crypto/crc32@5.2.0': + resolution: {integrity: sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==} + engines: {node: '>=16.0.0'} + + '@aws-crypto/sha256-browser@5.2.0': + resolution: {integrity: sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==} + + '@aws-crypto/sha256-js@5.2.0': + resolution: {integrity: sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==} + engines: {node: '>=16.0.0'} + + '@aws-crypto/supports-web-crypto@5.2.0': + resolution: {integrity: sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==} + + '@aws-crypto/util@5.2.0': + resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==} + + '@aws-sdk/client-bedrock-runtime@3.1048.0': + resolution: {integrity: sha512-u+NT61JZEkRFtpL0CAw1N1dwxnaLgwVXQl/zjJxTGgLyS/jTIdg2SdoEoCTHxgDyCnqa1HEi9QOoE9/pYRNpOQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/core@3.974.11': + resolution: {integrity: sha512-QpnINq5FZH6EOaDEkmHdT7eUunbvD27pDNQypaWjFyYz7Zl1q3UCMQErBZxpmfGfI7MvI2TlK8KTkgNpv8b1ug==} + engines: {node: '>=20.0.0'} + deprecated: Deprecated due to an error deserialization bug in JSON 1.0 protocol services, see https://github.com/aws/aws-sdk-js-v3/pull/8031. Newer version available. + + '@aws-sdk/credential-provider-env@3.972.37': + resolution: {integrity: sha512-/jpPvEh6f7ntmIzf7dNxoNX6Q8vt8UpesCjbW6mFfk4V1NW6bIy9qxcQ6WbA8As5yQhsZOe+xeNd4xHX8kdY2Q==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-http@3.972.39': + resolution: {integrity: sha512-pIgTpisWyWg7X1bUbzSjuUYosYTD0Ghz2M0hkSTmb3a6i3qV3uU+NYJPI/E2XSC0HcsZh5rsLPzeXrkb2DS0Cg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-ini@3.972.41': + resolution: {integrity: sha512-u2tyjaxJJzW8UtW4SM1ZcPMDwO6y+kV+llvou+Adts0FAKyzes5jG4izQN+KX3yE8ZROpS5y1LJ//xL2iSf76w==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-login@3.972.41': + resolution: {integrity: sha512-0LBitxXiAiaE5nlFPfpNIww/8FRY/I7WIndWsc9GmNFOM7cE1wNpVNQEGEk9Outg5l8xl+3vybxFyUy4l9q/LQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-node@3.972.42': + resolution: {integrity: sha512-D4oon2zbqqsWOJUM99Gm3/ZyJ0IJvTXVN3PyloGb3kQEyI36fjCZheZj422lAgTWWd6TSHgiImLt3RIaLdv3dQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-process@3.972.37': + resolution: {integrity: sha512-7nVaHBUaWIddASYfVaA9O4D5ZVjewU3sCol9WqZPGfW0nR+0WqE0xHZnD/U2L33PlOB8KNXGKZ6wOES/QijKzg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-sso@3.972.41': + resolution: {integrity: sha512-IOWAWEHe5LkjSKkkUUX9ciV6Y1scHTsnfEkdt5yyC4Slrc7AGbkLPrpntjqh18ksJAMOaVhoBsO8p2WyTcY2wQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-web-identity@3.972.41': + resolution: {integrity: sha512-mbACk9Yypa8nm4iGZLs0PofOXEcTDOUw6wDnsPXNDNSd2WNXs1tSo+6nc/fh0jLYdfVZThhBL98PHW4aXFsG5A==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/eventstream-handler-node@3.972.16': + resolution: {integrity: sha512-yedpPgKftqjU5SlPFHfqWpOw6xSCRieWRG1euWOlXn4WJxt2VX92VprCa2PpSOXjVCAeK6dTjW9eJRXVig9yGA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-eventstream@3.972.12': + resolution: {integrity: sha512-tHTHHCHNrq6XklQvlzHBDJG4Iuhh7NVPRdtmvP+nHFA+5sxPlIDzlAHHgfoYHGvT3NXP1yVP/L5c3opUn6T3Qg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-websocket@3.972.19': + resolution: {integrity: sha512-mkEhOGYozqKQkbFaVrjwr0faiwwZza1v5/jSY6Tucm3bD+uKTazIUH/4Yo6aMnQD2ua2W9cMP6s8mvwTcjtqHw==} + engines: {node: '>= 14.0.0'} + + '@aws-sdk/nested-clients@3.997.9': + resolution: {integrity: sha512-jPR3rnmRI4hWYyzfmTGBr7NblMp8QYYeflHXba1H6+7CGrWVqWKQzaXFQ4qbExqPRsXN3T3L3JxFhr6aouXUGQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/signature-v4-multi-region@3.996.27': + resolution: {integrity: sha512-0Phbz4t6HI3D3skxvG2uI+VWU034/nSIw1T8d+FPzzQG9EQTrw94o9mOKO2Gv3n3Oc8P7JD7RAUxkoneLWv5Eg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/token-providers@3.1048.0': + resolution: {integrity: sha512-k0y/GcuesuSfWyUM0WamrGyeZmltRYaPbHO82UDA6mZ/doB+FOHKutikPAtSXMn/hDz970cF+iRuuiYO9VEbAA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/types@3.973.8': + resolution: {integrity: sha512-gjlAdtHMbtR9X5iIhVUvbVcy55KnznpC6bkDUWW9z915bi0ckdUr5cjf16Kp6xq0bP5HBD2xzgbL9F9Quv5vUw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/util-locate-window@3.965.5': + resolution: {integrity: sha512-WhlJNNINQB+9qtLtZJcpQdgZw3SCDCpXdUJP7cToGwHbCWCnRckGlc6Bx/OhWwIYFNAn+FIydY8SZ0QmVu3xTQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/xml-builder@3.972.24': + resolution: {integrity: sha512-V8z5YcDPfsvzrBlj0xR1vhRtocblhYbqdreCJB/voGd4Sr5zjNAeWxexbnqVtskTJe0vFb5KMqbSL++ePl+zRw==} + engines: {node: '>=20.0.0'} + + '@aws/lambda-invoke-store@0.2.4': + resolution: {integrity: sha512-iY8yvjE0y651BixKNPgmv1WrQc+GZ142sb0z4gYnChDDY2YqI4P/jsSopBWrKfAt7LOJAkOXt7rC/hms+WclQQ==} + engines: {node: '>=18.0.0'} + + '@babel/runtime@7.29.7': + resolution: {integrity: sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==} + engines: {node: '>=6.9.0'} + + '@clack/core@1.4.1': + resolution: {integrity: sha512-FILJa1gGKEFTGZAJE9RpVhrjKz3c3h4ar60dSv6cGuDqufQ84YEIS3GAGvZiN+H6yaLbbvTFNejjCC4tXpZEuw==} + engines: {node: '>= 20.12.0'} + + '@clack/prompts@1.5.1': + resolution: {integrity: sha512-zccHj2z2oCCO4yrDiRSlFOxWerGqRiysP7a5jPK6uoI9URKAquwY42Dd/iUP8JWHxEzdRe4TlbvZCo8z1/mhrw==} + engines: {node: '>= 20.12.0'} + + '@earendil-works/pi-agent-core@0.80.3': + resolution: {integrity: sha512-3qw0/GeRQBU/nlGjDe5Yb7ePKTmoxefx2YxyKMFAviFUMXpFexBG/hS7mBtwFahFvzrrTPPoRT6sFIDjwoDWPQ==} + engines: {node: '>=22.19.0'} + + '@earendil-works/pi-ai@0.80.3': + resolution: {integrity: sha512-jPZLMeGL5kkMSEAwAklfXTMHqZvfhsJtCCpKGIr5Duk7mc0n4skjB1dugk7y0z3z8ZHIUCmPAWHdyDqgUz5vdA==} + engines: {node: '>=22.19.0'} + hasBin: true + + '@earendil-works/pi-coding-agent@0.80.3': + resolution: {integrity: sha512-TIggw9gCXpA+Ph7OjdTA7ka2NPwTVuPmy39KDSyUzaKq8VvHfMGR7vtRz4JB7Um/RMRblmzhu4p9tUCk6MTgGA==} + engines: {node: '>=22.19.0'} + hasBin: true + + '@earendil-works/pi-tui@0.80.3': + resolution: {integrity: sha512-2BJI6qwRQfnM0Q7seL1+SbacU/jRRjBnN7Hu3n9BjAn7/s5FaBNnvdD1qBQYRsFTHfjqMaDsjYqanPyqwXj99w==} + engines: {node: '>=22.19.0'} + + '@emnapi/core@1.10.0': + resolution: {integrity: sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==} + + '@emnapi/runtime@1.10.0': + resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==} + + '@emnapi/wasi-threads@1.2.1': + resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} + + '@esbuild/aix-ppc64@0.28.1': + resolution: {integrity: sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.28.1': + resolution: {integrity: sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.28.1': + resolution: {integrity: sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.28.1': + resolution: {integrity: sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.28.1': + resolution: {integrity: sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.28.1': + resolution: {integrity: sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.28.1': + resolution: {integrity: sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.28.1': + resolution: {integrity: sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.28.1': + resolution: {integrity: sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.28.1': + resolution: {integrity: sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.28.1': + resolution: {integrity: sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.28.1': + resolution: {integrity: sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.28.1': + resolution: {integrity: sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.28.1': + resolution: {integrity: sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.28.1': + resolution: {integrity: sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.28.1': + resolution: {integrity: sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.28.1': + resolution: {integrity: sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.28.1': + resolution: {integrity: sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.28.1': + resolution: {integrity: sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.28.1': + resolution: {integrity: sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.28.1': + resolution: {integrity: sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.28.1': + resolution: {integrity: sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.28.1': + resolution: {integrity: sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.28.1': + resolution: {integrity: sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.28.1': + resolution: {integrity: sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.28.1': + resolution: {integrity: sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@google/genai@1.52.0': + resolution: {integrity: sha512-gwSvbpiN/17O9TbsqSsE/OzZcpv5Fo4RQjdngGgogtuB9RsyJ8ZHhX5KjHj1bp5N9snN2eK8LDGXSaWW2hof8Q==} + engines: {node: '>=20.0.0'} + peerDependencies: + '@modelcontextprotocol/sdk': ^1.25.2 + peerDependenciesMeta: + '@modelcontextprotocol/sdk': + optional: true + + '@hono/node-server@1.19.14': + resolution: {integrity: sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw==} + engines: {node: '>=18.14.1'} + peerDependencies: + hono: ^4 + + '@mariozechner/clipboard-darwin-arm64@0.3.9': + resolution: {integrity: sha512-BfgV7vCEWZwJwZJw03r6bP5+tf0iI/ANuQYCxi9RNn7FrWB3yzGuMKCrNLRl6V761vXRdL8+OqZ0wd4TqlsNOQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@mariozechner/clipboard-darwin-universal@0.3.9': + resolution: {integrity: sha512-BGGR4iA9Z2shAjI65eI5xtyb3LYNlDW9X3gxKxDbqtbnREohsrqznov6zpKoIrsRWpzlYVEdKphS7ksJ0/ndSQ==} + engines: {node: '>= 10'} + os: [darwin] + + '@mariozechner/clipboard-darwin-x64@0.3.9': + resolution: {integrity: sha512-4kURmCbS6nt8uYhtmWpUcJWyPHfmAr5dTpXD1nO3pIfa+TSQ9DbrGOYCKH+aEFW47XhQ4Vp8ZTszie+wfFvDKg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@mariozechner/clipboard-linux-arm64-gnu@0.3.9': + resolution: {integrity: sha512-g59OkUGP2DDfCOIKypHeYgv2M55u/cKvXa5dSxFbEJ34XvIQMdcVmpKCkGUro3ZgefXiGVdwguvTMQGpHWzIXw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@mariozechner/clipboard-linux-arm64-musl@0.3.9': + resolution: {integrity: sha512-AGuJdgKsmJdm4Pych7kv3sqe591ERRaAHW3xjLooiFzn8J+PxUyof++7YZrB5Y5tpnTO+K18Og3taj2NpluCRQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@mariozechner/clipboard-linux-riscv64-gnu@0.3.9': + resolution: {integrity: sha512-DXBEAiuMpk7dhS1a9NzNxVAFi1vaKoPu7rQNgY8LIDLGrK3lnIp3nT10DUum+PKVJoJppIP+NAA8IZe4DMNDPw==} + engines: {node: '>= 10'} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@mariozechner/clipboard-linux-x64-gnu@0.3.9': + resolution: {integrity: sha512-WORrMLd6EpElEME7JRKfSaY34nW1P5LbdgK5YNCS1ncG2LqmITsSMEJ8nh2mpvxb3TxqbOOKgY7k9eMJYlW9Mw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@mariozechner/clipboard-linux-x64-musl@0.3.9': + resolution: {integrity: sha512-/DHn+1DrfL6oRaPPWXaOKvonFFrni666fxd+zFqiQEfvBH0tsHVWjq9iqBk0oDp0qaPA72lIMy5BptxISBEhZQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@mariozechner/clipboard-win32-arm64-msvc@0.3.9': + resolution: {integrity: sha512-O5FHD3ErkMwMhNzAfu3ggy0ug4z7btZuoQgwwxlzPrwV2bxlD6WDpqBY4NCgICAgZdDKdp+loUEKVAVt8aYnhQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@mariozechner/clipboard-win32-x64-msvc@0.3.9': + resolution: {integrity: sha512-ihQC3EufqEY81vhXBgVBtK4prL+wc62zJsSvxrgz7K1hsdt6OObz6v9p3Rn1OG3GJksTTKMJF0u/guMISHPhSA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@mariozechner/clipboard@0.3.9': + resolution: {integrity: sha512-ABnA53mdfkGZwOFUdZNv2S0CWGO/EIuPj8Vv9xmBFmSYg/qFc7ihO6q5FcQjvoE67kZpWkEc4AhD6B/os04yuA==} + engines: {node: '>= 10'} + + '@mistralai/mistralai@2.2.6': + resolution: {integrity: sha512-W8pX7zHxjJvMIpw8JMxeJEleapXX0Q9NPszdNzqkM3MIEoIGPObdodujj+WHteXEvGfaP/AMwlNyRfEzSY6dQQ==} + peerDependencies: + '@opentelemetry/api': ^1.9.0 + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + + '@modelcontextprotocol/ext-apps@1.7.2': + resolution: {integrity: sha512-OOWKDxdAjYDcgHkmzVzccyyag3FK+jBWPaWu4WvTxFsU4R/cgOX4eep66zPRA5n4v6WfxUNibPyvX4iJ7egYTg==} + engines: {node: '>=20'} + peerDependencies: + '@modelcontextprotocol/sdk': ^1.29.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0 + zod: ^3.25.0 || ^4.0.0 + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + + '@modelcontextprotocol/sdk@1.29.0': + resolution: {integrity: sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==} + engines: {node: '>=18'} + peerDependencies: + '@cfworker/json-schema': ^4.1.1 + zod: ^3.25 || ^4.0 + peerDependenciesMeta: + '@cfworker/json-schema': + optional: true + + '@napi-rs/wasm-runtime@1.1.5': + resolution: {integrity: sha512-AWPoBRJ9tsnVhor4sjO7rkni+7p+2IAEFj6cx06UgP10jkQHqay/36uRV/bFkgrh18D9vb4cr8Q0Pthskgzy+Q==} + peerDependencies: + '@emnapi/core': ^1.7.1 + '@emnapi/runtime': ^1.7.1 + + '@nodable/entities@2.1.0': + resolution: {integrity: sha512-nyT7T3nbMyBI/lvr6L5TyWbFJAI9FTgVRakNoBqCD+PmID8DzFrrNdLLtHMwMszOtqZa8PAOV24ZqDnQrhQINA==} + + '@opencode-ai/sdk@1.17.13': + resolution: {integrity: sha512-VItOGjMzRQx3zypwmeFLNhCiIx32kxS7FqzIJvVZLfyNGCifs3rfGC9qzNKWcxQo4SjNvAw++v4gWWU6Inv+JQ==} + + '@opentelemetry/api@1.9.0': + resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} + engines: {node: '>=8.0.0'} + + '@opentelemetry/semantic-conventions@1.41.1': + resolution: {integrity: sha512-/UhIkaZgPutTFmQ7RnIJGgDXZmtEJ7Dvi86xNTFWcnRxVRNk/aotsqDJYeEvDP+FSMB2SdW+pQzNMcWP0rwuNA==} + engines: {node: '>=14'} + + '@oxc-project/types@0.133.0': + resolution: {integrity: sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==} + + '@pierre/diffs@1.2.5': + resolution: {integrity: sha512-uYOz3Kfs5ED0qY0VraUXzylsEKvZPTVdexboM3QKPx/qBZmTT9F3lKAFuPpY5aIrV04sdHtoFCKStyzEu99U2A==} + peerDependencies: + react: ^18.3.1 || ^19.0.0 + react-dom: ^18.3.1 || ^19.0.0 + + '@pierre/theme@1.0.3': + resolution: {integrity: sha512-sWHv11TMoqKxKDgTIk5VbhQjdPhs8DCcBxbjh3mRlS3YOM/OcrWoGX6MM8eBGn9cUu3M46Py0JnxsG2nJaFTuA==} + engines: {vscode: ^1.0.0} + + '@pondwader/socks5-server@1.0.10': + resolution: {integrity: sha512-bQY06wzzR8D2+vVCUoBsr5QS2U6UgPUQRmErNwtsuI6vLcyRKkafjkr3KxbtGFf9aBBIV2mcvlsKD1UYaIV+sg==} + + '@protobufjs/aspromise@1.1.2': + resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} + + '@protobufjs/base64@1.1.2': + resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==} + + '@protobufjs/codegen@2.0.5': + resolution: {integrity: sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==} + + '@protobufjs/eventemitter@1.1.1': + resolution: {integrity: sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg==} + + '@protobufjs/fetch@1.1.1': + resolution: {integrity: sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==} + + '@protobufjs/float@1.0.2': + resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==} + + '@protobufjs/path@1.1.2': + resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==} + + '@protobufjs/pool@1.1.0': + resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==} + + '@protobufjs/utf8@1.1.1': + resolution: {integrity: sha512-oOAWABowe8EAbMyWKM0tYDKi8Yaox52D+HWZhAIJqQXbqe0xI/GV7FhLWqlEKreMkfDjshR5FKgi3mnle0h6Eg==} + + '@rolldown/binding-android-arm64@1.0.3': + resolution: {integrity: sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@rolldown/binding-darwin-arm64@1.0.3': + resolution: {integrity: sha512-PcAhP+ynjURNyy8SKGl5DQP94aGuB/7JrXJb/t7P+hanXvQVMWzUvRRhBAcg/lNRadBhoUPqSoP4xw5tR/KBEA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@rolldown/binding-darwin-x64@1.0.3': + resolution: {integrity: sha512-9YpfeUvSE2RS7wysJ81uOZkXJz7f7Q55H2Gvp3VEw/EsahqDtrphrZ0EwDLK5vvKOzaCrBsjF8JmnMLcUt78Gg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@rolldown/binding-freebsd-x64@1.0.3': + resolution: {integrity: sha512-yB1IlAsSNHncV6SCTL27/MVGR5htvQsoGxIv5KMGXALp+Ll1wYsn+x98M9MW7qa+NdSbvrrY7ANI4wLJ0n1e6g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@rolldown/binding-linux-arm-gnueabihf@1.0.3': + resolution: {integrity: sha512-Yi30IVAAfLUCy2MseFjbB1jAMDl1VMCAas5StnYp8da9+CKvMd2H2cbEjWcw5NPaPqzvYkVIaF1nNUG+b7u/sw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@rolldown/binding-linux-arm64-gnu@1.0.3': + resolution: {integrity: sha512-jsO7R8To+AdlYgUmN5sHSCZbfhtMBkO0WUx8iORQnPcMMdgr7qM2DQmMwgabs3GhNztdmoKkMKQFHD6DTMCIQw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-arm64-musl@1.0.3': + resolution: {integrity: sha512-VWkUHwWriDciit80wleYwKILoR/KMvxh/IdwS/paX+ZgpuRpCrKLUdadJbc0NpBEiyhpYawsJ73j9aCvOH+f7Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rolldown/binding-linux-ppc64-gnu@1.0.3': + resolution: {integrity: sha512-5f1laC0SlIR0yDbFCd8acUhvJIag6N3zC5P7oUPN6wX0aOma+uKJ0wBDH5aq7I1PVI2ttTlhJwzwRIBnLiSGEg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-s390x-gnu@1.0.3': + resolution: {integrity: sha512-Iq4ko0r4XsgbrF/LunNgHtAGLRRVE2kXonAXQ/MV0mC6jQpMOhW1SvtZja2EhC/kd05++bP78dsqBeIQyYJ6Yg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-gnu@1.0.3': + resolution: {integrity: sha512-B8m6tD5+/N5FeNQFbKlLA/2yVq9ycQP1SeedyEYYKWBNR3ZQbkvIUcNnDNM03lO1l5F2roiiFJGgvoLLyZXtSg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-musl@1.0.3': + resolution: {integrity: sha512-pSdpdUJHkuCxun9LE7jvgUB9qsRgaiyNNCX7m/AvHTcq67AiT/Yhoxvw5zPfhrM8k/BfP8ce/hMOpthKDpEUow==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rolldown/binding-openharmony-arm64@1.0.3': + resolution: {integrity: sha512-OXXS3RKJgX2uLwM+gYyuH5omcH8fL1LJs96pZGgtetVCahON57+d4SJHzTgZiOjxgGkSnpXpOsWuPDGAKAigEg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@rolldown/binding-wasm32-wasi@1.0.3': + resolution: {integrity: sha512-JTtb8BWFynicNSoPrehsCzBtOKjZ6jhMiPFEmOiuXg1Fl8dn2KHQob+GuPSGR0dryQa1PQJbzjF3dqO/whhjLg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [wasm32] + + '@rolldown/binding-win32-arm64-msvc@1.0.3': + resolution: {integrity: sha512-gEdFFEN70A/jxb2svrWsN3aDL7OUtmvlOy+6fa2jxG8K0wQ1ZbdeLGnidov6Yu5/733dI5ySfzFlQ/cb0bSz1g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@rolldown/binding-win32-x64-msvc@1.0.3': + resolution: {integrity: sha512-eXB7CHuaQdqmJcc3koCNtNPmT/bj2gc999kUFgBxG8Ac0NdgXc4rkCHhqrgrhN3zddvvvrgzj1e90SuSfmyIXA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@rolldown/pluginutils@1.0.1': + resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} + + '@shikijs/core@3.23.0': + resolution: {integrity: sha512-NSWQz0riNb67xthdm5br6lAkvpDJRTgB36fxlo37ZzM2yq0PQFFzbd8psqC2XMPgCzo1fW6cVi18+ArJ44wqgA==} + + '@shikijs/engine-javascript@3.23.0': + resolution: {integrity: sha512-aHt9eiGFobmWR5uqJUViySI1bHMqrAgamWE1TYSUoftkAeCCAiGawPMwM+VCadylQtF4V3VNOZ5LmfItH5f3yA==} + + '@shikijs/engine-oniguruma@3.23.0': + resolution: {integrity: sha512-1nWINwKXxKKLqPibT5f4pAFLej9oZzQTsby8942OTlsJzOBZ0MWKiwzMsd+jhzu8YPCHAswGnnN1YtQfirL35g==} + + '@shikijs/langs@3.23.0': + resolution: {integrity: sha512-2Ep4W3Re5aB1/62RSYQInK9mM3HsLeB91cHqznAJMuylqjzNVAVCMnNWRHFtcNHXsoNRayP9z1qj4Sq3nMqYXg==} + + '@shikijs/themes@3.23.0': + resolution: {integrity: sha512-5qySYa1ZgAT18HR/ypENL9cUSGOeI2x+4IvYJu4JgVJdizn6kG4ia5Q1jDEOi7gTbN4RbuYtmHh0W3eccOrjMA==} + + '@shikijs/transformers@3.23.0': + resolution: {integrity: sha512-F9msZVxdF+krQNSdQ4V+Ja5QemeAoTQ2jxt7nJCwhDsdF1JWS3KxIQXA3lQbyKwS3J61oHRUSv4jYWv3CkaKTQ==} + + '@shikijs/types@3.23.0': + resolution: {integrity: sha512-3JZ5HXOZfYjsYSk0yPwBrkupyYSLpAE26Qc0HLghhZNGTZg/SKxXIIgoxOpmmeQP0RRSDJTk1/vPfw9tbw+jSQ==} + + '@shikijs/vscode-textmate@10.0.2': + resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} + + '@silvia-odwyer/photon-node@0.3.4': + resolution: {integrity: sha512-bnly4BKB3KDTFxrUIcgCLbaeVVS8lrAkri1pEzskpmxu9MdfGQTy8b8EgcD83ywD3RPMsIulY8xJH5Awa+t9fA==} + + '@smithy/core@3.24.3': + resolution: {integrity: sha512-Ep/7tPamGY8mgESE3LyLKtxJyy6U52WWAqr/3wial47Sj4u3PiIF73AOGI27UyLy9duTkhZbgzodOfLV4TduZg==} + engines: {node: '>=18.0.0'} + + '@smithy/credential-provider-imds@4.3.3': + resolution: {integrity: sha512-I2Bti0DKFo2IJyN28ijCsx51BAumEYR4/1yZ1FXyBygy9MqbnMqCev4JPth/MbpRfBSRAX35hITSnAdJRo1u5w==} + engines: {node: '>=18.0.0'} + + '@smithy/fetch-http-handler@5.4.3': + resolution: {integrity: sha512-F+DRf8IJazRJgYog2A/yJK7eYVc0rqTlRzO+5ZxjJd4WkZoKz0IJRncf7G6t1pdVT3kryJcwuTFhN1c5m6N47A==} + engines: {node: '>=18.0.0'} + + '@smithy/is-array-buffer@2.2.0': + resolution: {integrity: sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==} + engines: {node: '>=14.0.0'} + + '@smithy/node-http-handler@4.7.3': + resolution: {integrity: sha512-/jPhevcTFPMVl6KNjbaI47iOg1zxC7IsnX4PQDGVZKMFceOXtB8IEYaB7a9VvkP/3oC60WzTeKocvSI7vLT0vA==} + engines: {node: '>=18.0.0'} + + '@smithy/signature-v4@5.4.3': + resolution: {integrity: sha512-53+75QuPl6DL+ct6vVEB51FDO5oulXr20TPV46VvJZg76lIlXNWfxi8j+G2V/t0I2qxCBOa3vX/8bmjrpFVo9g==} + engines: {node: '>=18.0.0'} + + '@smithy/types@4.14.2': + resolution: {integrity: sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw==} + engines: {node: '>=18.0.0'} + + '@smithy/util-buffer-from@2.2.0': + resolution: {integrity: sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==} + engines: {node: '>=14.0.0'} + + '@smithy/util-utf8@2.3.0': + resolution: {integrity: sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==} + engines: {node: '>=14.0.0'} + + '@stablelib/base64@1.0.1': + resolution: {integrity: sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==} + + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + + '@tybys/wasm-util@0.10.2': + resolution: {integrity: sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==} + + '@types/better-sqlite3@7.6.13': + resolution: {integrity: sha512-NMv9ASNARoKksWtsq/SHakpYAYnhBrQgGD8zkLYk/jaK8jUGn08CfEdTRgYhMypUQAfzSP8W6gNLe0q19/t4VA==} + + '@types/body-parser@1.19.6': + resolution: {integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==} + + '@types/connect@3.4.38': + resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} + + '@types/express-serve-static-core@5.1.1': + resolution: {integrity: sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==} + + '@types/express@5.0.6': + resolution: {integrity: sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==} + + '@types/hast@3.0.4': + resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} + + '@types/http-errors@2.0.5': + resolution: {integrity: sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==} + + '@types/mdast@4.0.4': + resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} + + '@types/node@25.9.1': + resolution: {integrity: sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg==} + + '@types/qs@6.15.1': + resolution: {integrity: sha512-GZHUBZR9hckSUhrxmp1nG6NwdpM9fCunJwyThLW1X3AyHgd9IlHb6VANpQQqDr2o/qQp6McZ3y/IA2rVzKzSbw==} + + '@types/range-parser@1.2.7': + resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} + + '@types/react-dom@19.2.3': + resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} + peerDependencies: + '@types/react': ^19.2.0 + + '@types/react@19.2.15': + resolution: {integrity: sha512-eRwcGNHve+E8qtEQSSRl6urh+rFop4v8gm6O8rGv25CodbvFdLjA1vVQ1KkiFE0w0UPOnb8tDiFKL5lp0rtY5Q==} + + '@types/retry@0.12.0': + resolution: {integrity: sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==} + + '@types/semver@7.7.1': + resolution: {integrity: sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==} + + '@types/send@1.2.1': + resolution: {integrity: sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==} + + '@types/serve-static@2.2.0': + resolution: {integrity: sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==} + + '@types/unist@3.0.3': + resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + + '@ungap/structured-clone@1.3.1': + resolution: {integrity: sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ==} + + '@vitejs/plugin-react@6.0.2': + resolution: {integrity: sha512-DlSMqo4WhThw4vB8Mpn0Woe9J+Jfq1geJ61AKW0QEgLzGMNwtIMdxbDUzLxcun8W7NbJO0e2Jg/Nxm3cCSVzzg==} + engines: {node: ^20.19.0 || >=22.12.0} + peerDependencies: + '@rolldown/plugin-babel': ^0.1.7 || ^0.2.0 + babel-plugin-react-compiler: ^1.0.0 + vite: ^8.0.0 + peerDependenciesMeta: + '@rolldown/plugin-babel': + optional: true + babel-plugin-react-compiler: + optional: true + + accepts@2.0.0: + resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} + engines: {node: '>= 0.6'} + + agent-base@7.1.4: + resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} + engines: {node: '>= 14'} + + ajv-formats@3.0.1: + resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + + ajv@8.20.0: + resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==} + + balanced-match@4.0.4: + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} + + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + better-result@2.10.0: + resolution: {integrity: sha512-oQhh0y1qo2/ZKdAAEvHZAqKKiHOFU5k/bW96fE2ScgQOVkJRiHwB+nOS1SgFsYqRlxMDWvefXi9Q3px7QvgNDw==} + + better-sqlite3@12.10.0: + resolution: {integrity: sha512-CyzaZRQKyHkB2ZInfTTl2nvT33EbDpjkLEbE8/Zck3Ll6O0qqvuGdrJ45HgtH+HykRg88ITY3AdreBGN70aBSQ==} + engines: {node: 20.x || 22.x || 23.x || 24.x || 25.x || 26.x} + + bignumber.js@9.3.1: + resolution: {integrity: sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==} + + bindings@1.5.0: + resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==} + + bl@4.1.0: + resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + + body-parser@2.2.2: + resolution: {integrity: sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==} + engines: {node: '>=18'} + + bowser@2.14.1: + resolution: {integrity: sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==} + + brace-expansion@5.0.6: + resolution: {integrity: sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==} + engines: {node: 18 || 20 || >=22} + + buffer-equal-constant-time@1.0.1: + resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} + + buffer@5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + + bytes@3.1.2: + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + + ccount@2.0.1: + resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + + chalk@5.6.2: + resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + + character-entities-html4@2.1.0: + resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} + + character-entities-legacy@3.0.0: + resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} + + chownr@1.1.4: + resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} + + comma-separated-tokens@2.0.3: + resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + + commander@12.1.0: + resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} + engines: {node: '>=18'} + + content-disposition@1.1.0: + resolution: {integrity: sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==} + engines: {node: '>=18'} + + content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + + content-type@2.0.0: + resolution: {integrity: sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==} + engines: {node: '>=18'} + + cookie-signature@1.2.2: + resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} + engines: {node: '>=6.6.0'} + + cookie@0.7.2: + resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} + engines: {node: '>= 0.6'} + + cors@2.8.6: + resolution: {integrity: sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==} + engines: {node: '>= 0.10'} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + + data-uri-to-buffer@4.0.1: + resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} + engines: {node: '>= 12'} + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decompress-response@6.0.0: + resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} + engines: {node: '>=10'} + + deep-extend@0.6.0: + resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} + engines: {node: '>=4.0.0'} + + depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + devlop@1.1.0: + resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + + diff@8.0.3: + resolution: {integrity: sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ==} + engines: {node: '>=0.3.1'} + + diff@8.0.4: + resolution: {integrity: sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==} + engines: {node: '>=0.3.1'} + + drizzle-orm@0.45.2: + resolution: {integrity: sha512-kY0BSaTNYWnoDMVoyY8uxmyHjpJW1geOmBMdSSicKo9CIIWkSxMIj2rkeSR51b8KAPB7m+qysjuHme5nKP+E5Q==} + peerDependencies: + '@aws-sdk/client-rds-data': '>=3' + '@cloudflare/workers-types': '>=4' + '@electric-sql/pglite': '>=0.2.0' + '@libsql/client': '>=0.10.0' + '@libsql/client-wasm': '>=0.10.0' + '@neondatabase/serverless': '>=0.10.0' + '@op-engineering/op-sqlite': '>=2' + '@opentelemetry/api': ^1.4.1 + '@planetscale/database': '>=1.13' + '@prisma/client': '*' + '@tidbcloud/serverless': '*' + '@types/better-sqlite3': '*' + '@types/pg': '*' + '@types/sql.js': '*' + '@upstash/redis': '>=1.34.7' + '@vercel/postgres': '>=0.8.0' + '@xata.io/client': '*' + better-sqlite3: '>=7' + bun-types: '*' + expo-sqlite: '>=14.0.0' + gel: '>=2' + knex: '*' + kysely: '*' + mysql2: '>=2' + pg: '>=8' + postgres: '>=3' + prisma: '*' + sql.js: '>=1' + sqlite3: '>=5' + peerDependenciesMeta: + '@aws-sdk/client-rds-data': + optional: true + '@cloudflare/workers-types': + optional: true + '@electric-sql/pglite': + optional: true + '@libsql/client': + optional: true + '@libsql/client-wasm': + optional: true + '@neondatabase/serverless': + optional: true + '@op-engineering/op-sqlite': + optional: true + '@opentelemetry/api': + optional: true + '@planetscale/database': + optional: true + '@prisma/client': + optional: true + '@tidbcloud/serverless': + optional: true + '@types/better-sqlite3': + optional: true + '@types/pg': + optional: true + '@types/sql.js': + optional: true + '@upstash/redis': + optional: true + '@vercel/postgres': + optional: true + '@xata.io/client': + optional: true + better-sqlite3: + optional: true + bun-types: + optional: true + expo-sqlite: + optional: true + gel: + optional: true + knex: + optional: true + kysely: + optional: true + mysql2: + optional: true + pg: + optional: true + postgres: + optional: true + prisma: + optional: true + sql.js: + optional: true + sqlite3: + optional: true + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + ecdsa-sig-formatter@1.0.11: + resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} + + ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + + encodeurl@2.0.0: + resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} + engines: {node: '>= 0.8'} + + end-of-stream@1.4.5: + resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-object-atoms@1.1.2: + resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==} + engines: {node: '>= 0.4'} + + esbuild@0.28.1: + resolution: {integrity: sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==} + engines: {node: '>=18'} + hasBin: true + + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + + etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + + eventsource-parser@3.1.0: + resolution: {integrity: sha512-kJezFj9YFAMLeORyi7aCLxLbD5/qWMQnoMVlVPyHIll7lgRJCc3JVln9Vgl9nwQi0YkMnhdGTMNn7CkRRAptMg==} + engines: {node: '>=18.0.0'} + + eventsource@3.0.7: + resolution: {integrity: sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==} + engines: {node: '>=18.0.0'} + + expand-template@2.0.3: + resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} + engines: {node: '>=6'} + + express-rate-limit@8.5.2: + resolution: {integrity: sha512-5Kb34ipNX694DH48vN9irak1Qx30nb0PLYHXfJgw4YEjiC3ZEmZJhwOp+VfiCYwFzvFTdB9QkArYS5kXa2cx2A==} + engines: {node: '>= 16'} + peerDependencies: + express: '>= 4.11' + + express@5.2.1: + resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==} + engines: {node: '>= 18'} + + extend@3.0.2: + resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-sha256@1.3.0: + resolution: {integrity: sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==} + + fast-string-truncated-width@3.0.3: + resolution: {integrity: sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==} + + fast-string-width@3.0.2: + resolution: {integrity: sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==} + + fast-uri@3.1.2: + resolution: {integrity: sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==} + + fast-wrap-ansi@0.2.2: + resolution: {integrity: sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==} + + fast-xml-builder@1.2.0: + resolution: {integrity: sha512-00aAWieqff+ZJhsXA4g1g7M8k+7AYoMUUHF+/zFb5U6Uv/P0Vl4QZo84/IcufzYalLuEj9928bXN9PbbFzMF0Q==} + + fast-xml-parser@5.7.3: + resolution: {integrity: sha512-C0AaNuC+mscy6vrAQKAc/rMq+zAPHodfHGZu4sGVehvAQt/JLG1O5zEcYcXSY5zSqr4YVgxsB+pHXTq0i7eDlg==} + hasBin: true + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + fetch-blob@3.2.0: + resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} + engines: {node: ^12.20 || >= 14.13} + + file-uri-to-path@1.0.0: + resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} + + finalhandler@2.1.1: + resolution: {integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==} + engines: {node: '>= 18.0.0'} + + formdata-polyfill@4.0.10: + resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} + engines: {node: '>=12.20.0'} + + forwarded@0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} + + fresh@2.0.0: + resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} + engines: {node: '>= 0.8'} + + fs-constants@1.0.0: + resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + gaxios@7.1.4: + resolution: {integrity: sha512-bTIgTsM2bWn3XklZISBTQX7ZSddGW+IO3bMdGaemHZ3tbqExMENHLx6kKZ/KlejgrMtj8q7wBItt51yegqalrA==} + engines: {node: '>=18'} + + gcp-metadata@8.1.2: + resolution: {integrity: sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg==} + engines: {node: '>=18'} + + get-east-asian-width@1.6.0: + resolution: {integrity: sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==} + engines: {node: '>=18'} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + github-from-package@0.0.0: + resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==} + + glob@13.0.6: + resolution: {integrity: sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==} + engines: {node: 18 || 20 || >=22} + + google-auth-library@10.6.2: + resolution: {integrity: sha512-e27Z6EThmVNNvtYASwQxose/G57rkRuaRbQyxM2bvYLLX/GqWZ5chWq2EBoUchJbCc57eC9ArzO5wMsEmWftCw==} + engines: {node: '>=18'} + + google-logging-utils@1.1.3: + resolution: {integrity: sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA==} + engines: {node: '>=14'} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + hasown@2.0.4: + resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} + engines: {node: '>= 0.4'} + + hast-util-to-html@9.0.5: + resolution: {integrity: sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==} + + hast-util-whitespace@3.0.0: + resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} + + highlight.js@10.7.3: + resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==} + + hono@4.12.25: + resolution: {integrity: sha512-2NFaIyNVgJmBs/ecmtGzlmluTFs5cHEWGTdu0t1HBwYzoGXOL5nUQBRMXsXWla5i4KkG//QMzVP88m1+I3fdAQ==} + engines: {node: '>=16.9.0'} + + hosted-git-info@9.0.3: + resolution: {integrity: sha512-Hc+ghLoSt6QaYZUv0WBiIvmMDZuZZ7oaDvdH8MbfOO4lOsxdXLEvuC6ePoGs9H1X9oCLyq6+NVN0MKqD+ydxyg==} + engines: {node: ^20.17.0 || >=22.9.0} + + html-void-elements@3.0.0: + resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} + + http-errors@2.0.1: + resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} + engines: {node: '>= 0.8'} + + http-proxy-agent@7.0.2: + resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} + engines: {node: '>= 14'} + + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} + + iconv-lite@0.7.2: + resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==} + engines: {node: '>=0.10.0'} + + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + + ignore@7.0.5: + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + engines: {node: '>= 4'} + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + ini@1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + + ip-address@10.2.0: + resolution: {integrity: sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==} + engines: {node: '>= 12'} + + ipaddr.js@1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} + + is-promise@4.0.0: + resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + jiti@2.7.0: + resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} + hasBin: true + + jose@6.2.3: + resolution: {integrity: sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw==} + + json-bigint@1.0.0: + resolution: {integrity: sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==} + + json-schema-to-ts@3.1.1: + resolution: {integrity: sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g==} + engines: {node: '>=16'} + + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + + json-schema-typed@8.0.2: + resolution: {integrity: sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==} + + jsonc-parser@3.3.1: + resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} + + jwa@2.0.1: + resolution: {integrity: sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==} + + jws@4.0.1: + resolution: {integrity: sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==} + + lightningcss-android-arm64@1.32.0: + resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.32.0: + resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.32.0: + resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.32.0: + resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.32.0: + resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.32.0: + resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + lightningcss-linux-arm64-musl@1.32.0: + resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + lightningcss-linux-x64-gnu@1.32.0: + resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + lightningcss-linux-x64-musl@1.32.0: + resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + lightningcss-win32-arm64-msvc@1.32.0: + resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.32.0: + resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.32.0: + resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} + engines: {node: '>= 12.0.0'} + + long@5.3.2: + resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==} + + lru-cache@11.4.0: + resolution: {integrity: sha512-W+R+kFL4HgVxONq2bhXPi3bGpzGe/yEhVOp233qw9wCRtgncJ15P3bC+e4zZMu4Cq7d+WAJjXGW0uUkifhcatA==} + engines: {node: 20 || >=22} + + lru_map@0.4.1: + resolution: {integrity: sha512-I+lBvqMMFfqaV8CJCISjI3wbjmwVu/VyOoU7+qtu9d7ioW5klMgsTTiUOUp+DJvfTTzKXoPbyC6YfgkNcyPSOg==} + + lucide@1.24.0: + resolution: {integrity: sha512-oMAaeuNDc5VCnBb3IjwKYGRT56tqanUm1fyDFT5Tl8hWSZND59gztgjvXje08jKLPVAq0gHJcwZUE8GCQxzBeg==} + + marked@18.0.5: + resolution: {integrity: sha512-S6GcvALHg6K4ohtu4E7x0a1AqhAjp6cV8KhLSyN9qVapnzJkusVBxZRcIU9AeYsbe6P1hKDusSbEOzGyyuce6w==} + engines: {node: '>= 20'} + hasBin: true + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + mdast-util-to-hast@13.2.1: + resolution: {integrity: sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==} + + media-typer@1.1.0: + resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} + engines: {node: '>= 0.8'} + + merge-descriptors@2.0.0: + resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} + engines: {node: '>=18'} + + micromark-util-character@2.1.1: + resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} + + micromark-util-encode@2.0.1: + resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} + + micromark-util-sanitize-uri@2.0.1: + resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} + + micromark-util-symbol@2.0.1: + resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} + + micromark-util-types@2.0.2: + resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==} + + mime-db@1.54.0: + resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} + engines: {node: '>= 0.6'} + + mime-types@3.0.2: + resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==} + engines: {node: '>=18'} + + mimic-response@3.1.0: + resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} + engines: {node: '>=10'} + + minimatch@10.2.5: + resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==} + engines: {node: 18 || 20 || >=22} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + minipass@7.1.3: + resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} + engines: {node: '>=16 || 14 >=14.17'} + + mkdirp-classic@0.5.3: + resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + nanoid@3.3.12: + resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + napi-build-utils@2.0.0: + resolution: {integrity: sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==} + + negotiator@1.0.0: + resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} + engines: {node: '>= 0.6'} + + node-abi@3.92.0: + resolution: {integrity: sha512-KdHvFWZjEKDf0cakgFjebl371GPsISX2oZHcuyKqM7DtogIsHrqKeLTo8wBHxaXRAQlY2PsPlZmfo+9ZCxEREQ==} + engines: {node: '>=10'} + + node-addon-api@7.1.1: + resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} + + node-domexception@1.0.0: + resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} + engines: {node: '>=10.5.0'} + deprecated: Use your platform's native DOMException instead + + node-fetch@3.3.2: + resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + node-forge@1.4.0: + resolution: {integrity: sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ==} + engines: {node: '>= 6.13.0'} + + node-pty@1.1.0: + resolution: {integrity: sha512-20JqtutY6JPXTUnL0ij1uad7Qe1baT46lyolh2sSENDd4sTzKZ4nmAFkeAARDKwmlLjPx6XKRlwRUxwjOy+lUg==} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + + on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + oniguruma-parser@0.12.2: + resolution: {integrity: sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw==} + + oniguruma-to-es@4.3.6: + resolution: {integrity: sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA==} + + openai@6.26.0: + resolution: {integrity: sha512-zd23dbWTjiJ6sSAX6s0HrCZi41JwTA1bQVs0wLQPZ2/5o2gxOJA5wh7yOAUgwYybfhDXyhwlpeQf7Mlgx8EOCA==} + hasBin: true + peerDependencies: + ws: ^8.18.0 + zod: ^3.25 || ^4.0 + peerDependenciesMeta: + ws: + optional: true + zod: + optional: true + + p-retry@4.6.2: + resolution: {integrity: sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==} + engines: {node: '>=8'} + + parseurl@1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + + partial-json@0.1.7: + resolution: {integrity: sha512-Njv/59hHaokb/hRUjce3Hdv12wd60MtM9Z5Olmn+nehe0QDAsRtRbJPvJ0Z91TusF0SuZRIvnM+S4l6EIP8leA==} + + path-expression-matcher@1.5.0: + resolution: {integrity: sha512-cbrerZV+6rvdQrrD+iGMcZFEiiSrbv9Tfdkvnusy6y0x0GKBXREFg/Y65GhIfm0tnLntThhzCnfKwp1WRjeCyQ==} + engines: {node: '>=14.0.0'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-scurry@2.0.2: + resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==} + engines: {node: 18 || 20 || >=22} + + path-to-regexp@8.4.2: + resolution: {integrity: sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@4.0.4: + resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} + engines: {node: '>=12'} + + pkce-challenge@5.0.1: + resolution: {integrity: sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==} + engines: {node: '>=16.20.0'} + + postcss@8.5.15: + resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==} + engines: {node: ^10 || ^12 || >=14} + + prebuild-install@7.1.3: + resolution: {integrity: sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==} + engines: {node: '>=10'} + deprecated: No longer maintained. Please contact the author of the relevant native addon; alternatives are available. + hasBin: true + + proper-lockfile@4.1.2: + resolution: {integrity: sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==} + + property-information@7.1.0: + resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} + + protobufjs@7.6.4: + resolution: {integrity: sha512-RJJPTTpvFfHcWLkIa2JFWK4XvtSzS0yEWDmunqHXli1h3JlkbcQZXDZdcWxv+JK3Xsl5/UFDPZ0iGm7DAengYw==} + engines: {node: '>=12.0.0'} + + proxy-addr@2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} + + pump@3.0.4: + resolution: {integrity: sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==} + + qs@6.15.2: + resolution: {integrity: sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==} + engines: {node: '>=0.6'} + + range-parser@1.2.1: + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} + + raw-body@3.0.2: + resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==} + engines: {node: '>= 0.10'} + + rc@1.2.8: + resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} + hasBin: true + + react-dom@19.2.6: + resolution: {integrity: sha512-0prMI+hvBbPjsWnxDLxlCGyM8PN6UuWjEUCYmZhO67xIV9Xasa/r/vDnq+Xyq4Lo27g8QSbO5YzARu0D1Sps3g==} + peerDependencies: + react: ^19.2.6 + + react@19.2.6: + resolution: {integrity: sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q==} + engines: {node: '>=0.10.0'} + + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + + regex-recursion@6.0.2: + resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==} + + regex-utilities@2.3.0: + resolution: {integrity: sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==} + + regex@6.1.0: + resolution: {integrity: sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==} + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + + retry@0.12.0: + resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} + engines: {node: '>= 4'} + + retry@0.13.1: + resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} + engines: {node: '>= 4'} + + rolldown@1.0.3: + resolution: {integrity: sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + + router@2.2.0: + resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} + engines: {node: '>= 18'} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + scheduler@0.27.0: + resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} + + semver@7.8.0: + resolution: {integrity: sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==} + engines: {node: '>=10'} + hasBin: true + + semver@7.8.4: + resolution: {integrity: sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==} + engines: {node: '>=10'} + hasBin: true + + send@1.2.1: + resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==} + engines: {node: '>= 18'} + + serve-static@2.2.1: + resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==} + engines: {node: '>= 18'} + + setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + shiki@3.23.0: + resolution: {integrity: sha512-55Dj73uq9ZXL5zyeRPzHQsK7Nbyt6Y10k5s7OjuFZGMhpp4r/rsLBH0o/0fstIzX1Lep9VxefWljK/SKCzygIA==} + + side-channel-list@1.0.1: + resolution: {integrity: sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + + side-channel@1.1.0: + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} + engines: {node: '>= 0.4'} + + signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + + simple-concat@1.0.1: + resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} + + simple-get@4.0.1: + resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==} + + sisteransi@1.0.5: + resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + space-separated-tokens@2.0.2: + resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} + + standardwebhooks@1.0.0: + resolution: {integrity: sha512-BbHGOQK9olHPMvQNHWul6MYlrRTAOKn03rOe4A8O3CLWhNf4YHBqq2HJKKC+sfqpxiBY52pNeesD6jIiLDz8jg==} + + statuses@2.0.2: + resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} + engines: {node: '>= 0.8'} + + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + + stringify-entities@4.0.4: + resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} + + strip-json-comments@2.0.1: + resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} + engines: {node: '>=0.10.0'} + + strnum@2.3.0: + resolution: {integrity: sha512-ums3KNd42PGyx5xaoVTO1mjU1bH3NpY4vsrVlnv9PNGqQj8wd7rJ6nEypLrJ7z5vxK5RP0yMLo6J/Gsm62DI5Q==} + + tar-fs@2.1.4: + resolution: {integrity: sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==} + + tar-stream@2.2.0: + resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} + engines: {node: '>=6'} + + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} + engines: {node: '>=12.0.0'} + + toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + + trim-lines@3.0.1: + resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} + + ts-algebra@2.0.0: + resolution: {integrity: sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + tsx@4.22.3: + resolution: {integrity: sha512-mdoNxBC/cSQObGGVQ5Bpn5i+yv7j68gk3Nfm3wFjcJg3Z0Mix9jzAFfP12prmm5eVGmDKtp0yyArrs0Q+8gZHg==} + engines: {node: '>=18.0.0'} + hasBin: true + + tunnel-agent@0.6.0: + resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} + + type-is@2.1.0: + resolution: {integrity: sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==} + engines: {node: '>= 18'} + + typebox@1.1.38: + resolution: {integrity: sha512-pZ0aQPmMmXoUvSbeuWf/Hzsc+avNw/Zd6VeE8CFgkVGWyuHPJvqeJJDeJqLve+K70LvjYIoleGcoJHPT17cWoA==} + + typescript@6.0.3: + resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@7.24.6: + resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==} + + undici@8.5.0: + resolution: {integrity: sha512-xamtWoB1EshgjpmlXd7GGm2VfdDtw1+rD8uhry8pSNW3If6S8E0m2T2+orSKeZXEn/aPJMviCpDBA65WJt8zhg==} + engines: {node: '>=22.19.0'} + + unist-util-is@6.0.1: + resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==} + + unist-util-position@5.0.0: + resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} + + unist-util-stringify-position@4.0.0: + resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} + + unist-util-visit-parents@6.0.2: + resolution: {integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==} + + unist-util-visit@5.1.0: + resolution: {integrity: sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==} + + unpipe@1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + + vfile-message@4.0.3: + resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} + + vfile@6.0.3: + resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} + + vite@8.0.16: + resolution: {integrity: sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + '@vitejs/devtools': ^0.1.18 + esbuild: ^0.27.0 || ^0.28.0 + jiti: '>=1.21.0' + less: ^4.0.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + '@vitejs/devtools': + optional: true + esbuild: + optional: true + jiti: + optional: true + less: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + web-streams-polyfill@3.3.3: + resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} + engines: {node: '>= 8'} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + ws@8.21.0: + resolution: {integrity: sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + xml-naming@0.1.0: + resolution: {integrity: sha512-k8KO9hrMyNk6tUWqUfkTEZbezRRpONVOzUTnc97VnCvyj6Tf9lyUR9EDAIeiVLv56jsMcoXEwjW8Kv5yPY52lw==} + engines: {node: '>=16.0.0'} + + yaml@2.9.0: + resolution: {integrity: sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==} + engines: {node: '>= 14.6'} + hasBin: true + + zod-to-json-schema@3.25.2: + resolution: {integrity: sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==} + peerDependencies: + zod: ^3.25.28 || ^4 + + zod@3.25.76: + resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} + + zod@4.4.3: + resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} + + zwitch@2.0.4: + resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} + +snapshots: + + '@agentclientprotocol/sdk@1.1.0(zod@4.4.3)': + dependencies: + zod: 4.4.3 + + '@anthropic-ai/claude-agent-sdk-darwin-arm64@0.3.200': + optional: true + + '@anthropic-ai/claude-agent-sdk-darwin-x64@0.3.200': + optional: true + + '@anthropic-ai/claude-agent-sdk-linux-arm64-musl@0.3.200': + optional: true + + '@anthropic-ai/claude-agent-sdk-linux-arm64@0.3.200': + optional: true + + '@anthropic-ai/claude-agent-sdk-linux-x64-musl@0.3.200': + optional: true + + '@anthropic-ai/claude-agent-sdk-linux-x64@0.3.200': + optional: true + + '@anthropic-ai/claude-agent-sdk-win32-arm64@0.3.200': + optional: true + + '@anthropic-ai/claude-agent-sdk-win32-x64@0.3.200': + optional: true + + '@anthropic-ai/claude-agent-sdk@0.3.200(@anthropic-ai/sdk@0.110.0(zod@4.4.3))(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(zod@4.4.3)': + dependencies: + '@anthropic-ai/sdk': 0.110.0(zod@4.4.3) + '@modelcontextprotocol/sdk': 1.29.0(zod@4.4.3) + zod: 4.4.3 + optionalDependencies: + '@anthropic-ai/claude-agent-sdk-darwin-arm64': 0.3.200 + '@anthropic-ai/claude-agent-sdk-darwin-x64': 0.3.200 + '@anthropic-ai/claude-agent-sdk-linux-arm64': 0.3.200 + '@anthropic-ai/claude-agent-sdk-linux-arm64-musl': 0.3.200 + '@anthropic-ai/claude-agent-sdk-linux-x64': 0.3.200 + '@anthropic-ai/claude-agent-sdk-linux-x64-musl': 0.3.200 + '@anthropic-ai/claude-agent-sdk-win32-arm64': 0.3.200 + '@anthropic-ai/claude-agent-sdk-win32-x64': 0.3.200 + + '@anthropic-ai/sandbox-runtime@0.0.71': + dependencies: + '@pondwader/socks5-server': 1.0.10 + commander: 12.1.0 + node-forge: 1.4.0 + zod: 3.25.76 + + '@anthropic-ai/sdk@0.110.0(zod@4.4.3)': + dependencies: + json-schema-to-ts: 3.1.1 + standardwebhooks: 1.0.0 + optionalDependencies: + zod: 4.4.3 + + '@anthropic-ai/sdk@0.91.1(zod@4.4.3)': + dependencies: + json-schema-to-ts: 3.1.1 + optionalDependencies: + zod: 4.4.3 + + '@aws-crypto/crc32@5.2.0': + dependencies: + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.973.8 + tslib: 2.8.1 + + '@aws-crypto/sha256-browser@5.2.0': + dependencies: + '@aws-crypto/sha256-js': 5.2.0 + '@aws-crypto/supports-web-crypto': 5.2.0 + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.973.8 + '@aws-sdk/util-locate-window': 3.965.5 + '@smithy/util-utf8': 2.3.0 + tslib: 2.8.1 + + '@aws-crypto/sha256-js@5.2.0': + dependencies: + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.973.8 + tslib: 2.8.1 + + '@aws-crypto/supports-web-crypto@5.2.0': + dependencies: + tslib: 2.8.1 + + '@aws-crypto/util@5.2.0': + dependencies: + '@aws-sdk/types': 3.973.8 + '@smithy/util-utf8': 2.3.0 + tslib: 2.8.1 + + '@aws-sdk/client-bedrock-runtime@3.1048.0': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.974.11 + '@aws-sdk/credential-provider-node': 3.972.42 + '@aws-sdk/eventstream-handler-node': 3.972.16 + '@aws-sdk/middleware-eventstream': 3.972.12 + '@aws-sdk/middleware-websocket': 3.972.19 + '@aws-sdk/token-providers': 3.1048.0 + '@aws-sdk/types': 3.973.8 + '@smithy/core': 3.24.3 + '@smithy/fetch-http-handler': 5.4.3 + '@smithy/node-http-handler': 4.7.3 + '@smithy/types': 4.14.2 + tslib: 2.8.1 + + '@aws-sdk/core@3.974.11': + dependencies: + '@aws-sdk/types': 3.973.8 + '@aws-sdk/xml-builder': 3.972.24 + '@aws/lambda-invoke-store': 0.2.4 + '@smithy/core': 3.24.3 + '@smithy/signature-v4': 5.4.3 + '@smithy/types': 4.14.2 + bowser: 2.14.1 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-env@3.972.37': + dependencies: + '@aws-sdk/core': 3.974.11 + '@aws-sdk/types': 3.973.8 + '@smithy/core': 3.24.3 + '@smithy/types': 4.14.2 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-http@3.972.39': + dependencies: + '@aws-sdk/core': 3.974.11 + '@aws-sdk/types': 3.973.8 + '@smithy/core': 3.24.3 + '@smithy/fetch-http-handler': 5.4.3 + '@smithy/node-http-handler': 4.7.3 + '@smithy/types': 4.14.2 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-ini@3.972.41': + dependencies: + '@aws-sdk/core': 3.974.11 + '@aws-sdk/credential-provider-env': 3.972.37 + '@aws-sdk/credential-provider-http': 3.972.39 + '@aws-sdk/credential-provider-login': 3.972.41 + '@aws-sdk/credential-provider-process': 3.972.37 + '@aws-sdk/credential-provider-sso': 3.972.41 + '@aws-sdk/credential-provider-web-identity': 3.972.41 + '@aws-sdk/nested-clients': 3.997.9 + '@aws-sdk/types': 3.973.8 + '@smithy/core': 3.24.3 + '@smithy/credential-provider-imds': 4.3.3 + '@smithy/types': 4.14.2 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-login@3.972.41': + dependencies: + '@aws-sdk/core': 3.974.11 + '@aws-sdk/nested-clients': 3.997.9 + '@aws-sdk/types': 3.973.8 + '@smithy/core': 3.24.3 + '@smithy/types': 4.14.2 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-node@3.972.42': + dependencies: + '@aws-sdk/credential-provider-env': 3.972.37 + '@aws-sdk/credential-provider-http': 3.972.39 + '@aws-sdk/credential-provider-ini': 3.972.41 + '@aws-sdk/credential-provider-process': 3.972.37 + '@aws-sdk/credential-provider-sso': 3.972.41 + '@aws-sdk/credential-provider-web-identity': 3.972.41 + '@aws-sdk/types': 3.973.8 + '@smithy/core': 3.24.3 + '@smithy/credential-provider-imds': 4.3.3 + '@smithy/types': 4.14.2 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-process@3.972.37': + dependencies: + '@aws-sdk/core': 3.974.11 + '@aws-sdk/types': 3.973.8 + '@smithy/core': 3.24.3 + '@smithy/types': 4.14.2 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-sso@3.972.41': + dependencies: + '@aws-sdk/core': 3.974.11 + '@aws-sdk/nested-clients': 3.997.9 + '@aws-sdk/token-providers': 3.1048.0 + '@aws-sdk/types': 3.973.8 + '@smithy/core': 3.24.3 + '@smithy/types': 4.14.2 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-web-identity@3.972.41': + dependencies: + '@aws-sdk/core': 3.974.11 + '@aws-sdk/nested-clients': 3.997.9 + '@aws-sdk/types': 3.973.8 + '@smithy/core': 3.24.3 + '@smithy/types': 4.14.2 + tslib: 2.8.1 + + '@aws-sdk/eventstream-handler-node@3.972.16': + dependencies: + '@aws-sdk/types': 3.973.8 + '@smithy/core': 3.24.3 + '@smithy/types': 4.14.2 + tslib: 2.8.1 + + '@aws-sdk/middleware-eventstream@3.972.12': + dependencies: + '@aws-sdk/types': 3.973.8 + '@smithy/core': 3.24.3 + '@smithy/types': 4.14.2 + tslib: 2.8.1 + + '@aws-sdk/middleware-websocket@3.972.19': + dependencies: + '@aws-sdk/core': 3.974.11 + '@aws-sdk/types': 3.973.8 + '@smithy/core': 3.24.3 + '@smithy/fetch-http-handler': 5.4.3 + '@smithy/signature-v4': 5.4.3 + '@smithy/types': 4.14.2 + tslib: 2.8.1 + + '@aws-sdk/nested-clients@3.997.9': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.974.11 + '@aws-sdk/signature-v4-multi-region': 3.996.27 + '@aws-sdk/types': 3.973.8 + '@smithy/core': 3.24.3 + '@smithy/fetch-http-handler': 5.4.3 + '@smithy/node-http-handler': 4.7.3 + '@smithy/types': 4.14.2 + tslib: 2.8.1 + + '@aws-sdk/signature-v4-multi-region@3.996.27': + dependencies: + '@aws-sdk/types': 3.973.8 + '@smithy/core': 3.24.3 + '@smithy/signature-v4': 5.4.3 + '@smithy/types': 4.14.2 + tslib: 2.8.1 + + '@aws-sdk/token-providers@3.1048.0': + dependencies: + '@aws-sdk/core': 3.974.11 + '@aws-sdk/nested-clients': 3.997.9 + '@aws-sdk/types': 3.973.8 + '@smithy/core': 3.24.3 + '@smithy/types': 4.14.2 + tslib: 2.8.1 + + '@aws-sdk/types@3.973.8': + dependencies: + '@smithy/types': 4.14.2 + tslib: 2.8.1 + + '@aws-sdk/util-locate-window@3.965.5': + dependencies: + tslib: 2.8.1 + + '@aws-sdk/xml-builder@3.972.24': + dependencies: + '@nodable/entities': 2.1.0 + '@smithy/types': 4.14.2 + fast-xml-parser: 5.7.3 + tslib: 2.8.1 + + '@aws/lambda-invoke-store@0.2.4': {} + + '@babel/runtime@7.29.7': {} + + '@clack/core@1.4.1': + dependencies: + fast-wrap-ansi: 0.2.2 + sisteransi: 1.0.5 + + '@clack/prompts@1.5.1': + dependencies: + '@clack/core': 1.4.1 + fast-string-width: 3.0.2 + fast-wrap-ansi: 0.2.2 + sisteransi: 1.0.5 + + '@earendil-works/pi-agent-core@0.80.3(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(ws@8.21.0)(zod@4.4.3)': + dependencies: + '@earendil-works/pi-ai': 0.80.3(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(ws@8.21.0)(zod@4.4.3) + ignore: 7.0.5 + typebox: 1.1.38 + yaml: 2.9.0 + transitivePeerDependencies: + - '@modelcontextprotocol/sdk' + - bufferutil + - supports-color + - utf-8-validate + - ws + - zod + + '@earendil-works/pi-ai@0.80.3(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(ws@8.21.0)(zod@4.4.3)': + dependencies: + '@anthropic-ai/sdk': 0.91.1(zod@4.4.3) + '@aws-sdk/client-bedrock-runtime': 3.1048.0 + '@google/genai': 1.52.0(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3)) + '@mistralai/mistralai': 2.2.6(@opentelemetry/api@1.9.0) + '@opentelemetry/api': 1.9.0 + '@smithy/node-http-handler': 4.7.3 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 + openai: 6.26.0(ws@8.21.0)(zod@4.4.3) + partial-json: 0.1.7 + typebox: 1.1.38 + transitivePeerDependencies: + - '@modelcontextprotocol/sdk' + - bufferutil + - supports-color + - utf-8-validate + - ws + - zod + + '@earendil-works/pi-coding-agent@0.80.3(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(ws@8.21.0)(zod@4.4.3)': + dependencies: + '@earendil-works/pi-agent-core': 0.80.3(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(ws@8.21.0)(zod@4.4.3) + '@earendil-works/pi-ai': 0.80.3(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(ws@8.21.0)(zod@4.4.3) + '@earendil-works/pi-tui': 0.80.3 + '@silvia-odwyer/photon-node': 0.3.4 + chalk: 5.6.2 + cross-spawn: 7.0.6 + diff: 8.0.4 + glob: 13.0.6 + highlight.js: 10.7.3 + hosted-git-info: 9.0.3 + ignore: 7.0.5 + jiti: 2.7.0 + minimatch: 10.2.5 + proper-lockfile: 4.1.2 + semver: 7.8.0 + typebox: 1.1.38 + undici: 8.5.0 + yaml: 2.9.0 + optionalDependencies: + '@mariozechner/clipboard': 0.3.9 + transitivePeerDependencies: + - '@modelcontextprotocol/sdk' + - bufferutil + - supports-color + - utf-8-validate + - ws + - zod + + '@earendil-works/pi-tui@0.80.3': + dependencies: + get-east-asian-width: 1.6.0 + marked: 18.0.5 + + '@emnapi/core@1.10.0': + dependencies: + '@emnapi/wasi-threads': 1.2.1 + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.10.0': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/wasi-threads@1.2.1': + dependencies: + tslib: 2.8.1 + optional: true + + '@esbuild/aix-ppc64@0.28.1': + optional: true + + '@esbuild/android-arm64@0.28.1': + optional: true + + '@esbuild/android-arm@0.28.1': + optional: true + + '@esbuild/android-x64@0.28.1': + optional: true + + '@esbuild/darwin-arm64@0.28.1': + optional: true + + '@esbuild/darwin-x64@0.28.1': + optional: true + + '@esbuild/freebsd-arm64@0.28.1': + optional: true + + '@esbuild/freebsd-x64@0.28.1': + optional: true + + '@esbuild/linux-arm64@0.28.1': + optional: true + + '@esbuild/linux-arm@0.28.1': + optional: true + + '@esbuild/linux-ia32@0.28.1': + optional: true + + '@esbuild/linux-loong64@0.28.1': + optional: true + + '@esbuild/linux-mips64el@0.28.1': + optional: true + + '@esbuild/linux-ppc64@0.28.1': + optional: true + + '@esbuild/linux-riscv64@0.28.1': + optional: true + + '@esbuild/linux-s390x@0.28.1': + optional: true + + '@esbuild/linux-x64@0.28.1': + optional: true + + '@esbuild/netbsd-arm64@0.28.1': + optional: true + + '@esbuild/netbsd-x64@0.28.1': + optional: true + + '@esbuild/openbsd-arm64@0.28.1': + optional: true + + '@esbuild/openbsd-x64@0.28.1': + optional: true + + '@esbuild/openharmony-arm64@0.28.1': + optional: true + + '@esbuild/sunos-x64@0.28.1': + optional: true + + '@esbuild/win32-arm64@0.28.1': + optional: true + + '@esbuild/win32-ia32@0.28.1': + optional: true + + '@esbuild/win32-x64@0.28.1': + optional: true + + '@google/genai@1.52.0(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))': + dependencies: + google-auth-library: 10.6.2 + p-retry: 4.6.2 + protobufjs: 7.6.4 + ws: 8.21.0 + optionalDependencies: + '@modelcontextprotocol/sdk': 1.29.0(zod@4.4.3) + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + '@hono/node-server@1.19.14(hono@4.12.25)': + dependencies: + hono: 4.12.25 + + '@mariozechner/clipboard-darwin-arm64@0.3.9': + optional: true + + '@mariozechner/clipboard-darwin-universal@0.3.9': + optional: true + + '@mariozechner/clipboard-darwin-x64@0.3.9': + optional: true + + '@mariozechner/clipboard-linux-arm64-gnu@0.3.9': + optional: true + + '@mariozechner/clipboard-linux-arm64-musl@0.3.9': + optional: true + + '@mariozechner/clipboard-linux-riscv64-gnu@0.3.9': + optional: true + + '@mariozechner/clipboard-linux-x64-gnu@0.3.9': + optional: true + + '@mariozechner/clipboard-linux-x64-musl@0.3.9': + optional: true + + '@mariozechner/clipboard-win32-arm64-msvc@0.3.9': + optional: true + + '@mariozechner/clipboard-win32-x64-msvc@0.3.9': + optional: true + + '@mariozechner/clipboard@0.3.9': + optionalDependencies: + '@mariozechner/clipboard-darwin-arm64': 0.3.9 + '@mariozechner/clipboard-darwin-universal': 0.3.9 + '@mariozechner/clipboard-darwin-x64': 0.3.9 + '@mariozechner/clipboard-linux-arm64-gnu': 0.3.9 + '@mariozechner/clipboard-linux-arm64-musl': 0.3.9 + '@mariozechner/clipboard-linux-riscv64-gnu': 0.3.9 + '@mariozechner/clipboard-linux-x64-gnu': 0.3.9 + '@mariozechner/clipboard-linux-x64-musl': 0.3.9 + '@mariozechner/clipboard-win32-arm64-msvc': 0.3.9 + '@mariozechner/clipboard-win32-x64-msvc': 0.3.9 + optional: true + + '@mistralai/mistralai@2.2.6(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/semantic-conventions': 1.41.1 + ws: 8.21.0 + zod: 4.4.3 + zod-to-json-schema: 3.25.2(zod@4.4.3) + optionalDependencies: + '@opentelemetry/api': 1.9.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + '@modelcontextprotocol/ext-apps@1.7.2(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(zod@4.4.3)': + dependencies: + '@modelcontextprotocol/sdk': 1.29.0(zod@4.4.3) + '@standard-schema/spec': 1.1.0 + zod: 4.4.3 + optionalDependencies: + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) + + '@modelcontextprotocol/sdk@1.29.0(zod@4.4.3)': + dependencies: + '@hono/node-server': 1.19.14(hono@4.12.25) + ajv: 8.20.0 + ajv-formats: 3.0.1(ajv@8.20.0) + content-type: 1.0.5 + cors: 2.8.6 + cross-spawn: 7.0.6 + eventsource: 3.0.7 + eventsource-parser: 3.1.0 + express: 5.2.1 + express-rate-limit: 8.5.2(express@5.2.1) + hono: 4.12.25 + jose: 6.2.3 + json-schema-typed: 8.0.2 + pkce-challenge: 5.0.1 + raw-body: 3.0.2 + zod: 4.4.3 + zod-to-json-schema: 3.25.2(zod@4.4.3) + transitivePeerDependencies: + - supports-color + + '@napi-rs/wasm-runtime@1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': + dependencies: + '@emnapi/core': 1.10.0 + '@emnapi/runtime': 1.10.0 + '@tybys/wasm-util': 0.10.2 + optional: true + + '@nodable/entities@2.1.0': {} + + '@opencode-ai/sdk@1.17.13': + dependencies: + cross-spawn: 7.0.6 + + '@opentelemetry/api@1.9.0': {} + + '@opentelemetry/semantic-conventions@1.41.1': {} + + '@oxc-project/types@0.133.0': {} + + '@pierre/diffs@1.2.5(react-dom@19.2.6(react@19.2.6))(react@19.2.6)': + dependencies: + '@pierre/theme': 1.0.3 + '@shikijs/transformers': 3.23.0 + diff: 8.0.3 + hast-util-to-html: 9.0.5 + lru_map: 0.4.1 + react: 19.2.6 + react-dom: 19.2.6(react@19.2.6) + shiki: 3.23.0 + + '@pierre/theme@1.0.3': {} + + '@pondwader/socks5-server@1.0.10': {} + + '@protobufjs/aspromise@1.1.2': {} + + '@protobufjs/base64@1.1.2': {} + + '@protobufjs/codegen@2.0.5': {} + + '@protobufjs/eventemitter@1.1.1': {} + + '@protobufjs/fetch@1.1.1': + dependencies: + '@protobufjs/aspromise': 1.1.2 + + '@protobufjs/float@1.0.2': {} + + '@protobufjs/path@1.1.2': {} + + '@protobufjs/pool@1.1.0': {} + + '@protobufjs/utf8@1.1.1': {} + + '@rolldown/binding-android-arm64@1.0.3': + optional: true + + '@rolldown/binding-darwin-arm64@1.0.3': + optional: true + + '@rolldown/binding-darwin-x64@1.0.3': + optional: true + + '@rolldown/binding-freebsd-x64@1.0.3': + optional: true + + '@rolldown/binding-linux-arm-gnueabihf@1.0.3': + optional: true + + '@rolldown/binding-linux-arm64-gnu@1.0.3': + optional: true + + '@rolldown/binding-linux-arm64-musl@1.0.3': + optional: true + + '@rolldown/binding-linux-ppc64-gnu@1.0.3': + optional: true + + '@rolldown/binding-linux-s390x-gnu@1.0.3': + optional: true + + '@rolldown/binding-linux-x64-gnu@1.0.3': + optional: true + + '@rolldown/binding-linux-x64-musl@1.0.3': + optional: true + + '@rolldown/binding-openharmony-arm64@1.0.3': + optional: true + + '@rolldown/binding-wasm32-wasi@1.0.3': + dependencies: + '@emnapi/core': 1.10.0 + '@emnapi/runtime': 1.10.0 + '@napi-rs/wasm-runtime': 1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + optional: true + + '@rolldown/binding-win32-arm64-msvc@1.0.3': + optional: true + + '@rolldown/binding-win32-x64-msvc@1.0.3': + optional: true + + '@rolldown/pluginutils@1.0.1': {} + + '@shikijs/core@3.23.0': + dependencies: + '@shikijs/types': 3.23.0 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + hast-util-to-html: 9.0.5 + + '@shikijs/engine-javascript@3.23.0': + dependencies: + '@shikijs/types': 3.23.0 + '@shikijs/vscode-textmate': 10.0.2 + oniguruma-to-es: 4.3.6 + + '@shikijs/engine-oniguruma@3.23.0': + dependencies: + '@shikijs/types': 3.23.0 + '@shikijs/vscode-textmate': 10.0.2 + + '@shikijs/langs@3.23.0': + dependencies: + '@shikijs/types': 3.23.0 + + '@shikijs/themes@3.23.0': + dependencies: + '@shikijs/types': 3.23.0 + + '@shikijs/transformers@3.23.0': + dependencies: + '@shikijs/core': 3.23.0 + '@shikijs/types': 3.23.0 + + '@shikijs/types@3.23.0': + dependencies: + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + + '@shikijs/vscode-textmate@10.0.2': {} + + '@silvia-odwyer/photon-node@0.3.4': {} + + '@smithy/core@3.24.3': + dependencies: + '@aws-crypto/crc32': 5.2.0 + '@smithy/types': 4.14.2 + tslib: 2.8.1 + + '@smithy/credential-provider-imds@4.3.3': + dependencies: + '@smithy/core': 3.24.3 + '@smithy/types': 4.14.2 + tslib: 2.8.1 + + '@smithy/fetch-http-handler@5.4.3': + dependencies: + '@smithy/core': 3.24.3 + '@smithy/types': 4.14.2 + tslib: 2.8.1 + + '@smithy/is-array-buffer@2.2.0': + dependencies: + tslib: 2.8.1 + + '@smithy/node-http-handler@4.7.3': + dependencies: + '@smithy/core': 3.24.3 + '@smithy/types': 4.14.2 + tslib: 2.8.1 + + '@smithy/signature-v4@5.4.3': + dependencies: + '@smithy/core': 3.24.3 + '@smithy/types': 4.14.2 + tslib: 2.8.1 + + '@smithy/types@4.14.2': + dependencies: + tslib: 2.8.1 + + '@smithy/util-buffer-from@2.2.0': + dependencies: + '@smithy/is-array-buffer': 2.2.0 + tslib: 2.8.1 + + '@smithy/util-utf8@2.3.0': + dependencies: + '@smithy/util-buffer-from': 2.2.0 + tslib: 2.8.1 + + '@stablelib/base64@1.0.1': {} + + '@standard-schema/spec@1.1.0': {} + + '@tybys/wasm-util@0.10.2': + dependencies: + tslib: 2.8.1 + optional: true + + '@types/better-sqlite3@7.6.13': + dependencies: + '@types/node': 25.9.1 + + '@types/body-parser@1.19.6': + dependencies: + '@types/connect': 3.4.38 + '@types/node': 25.9.1 + + '@types/connect@3.4.38': + dependencies: + '@types/node': 25.9.1 + + '@types/express-serve-static-core@5.1.1': + dependencies: + '@types/node': 25.9.1 + '@types/qs': 6.15.1 + '@types/range-parser': 1.2.7 + '@types/send': 1.2.1 + + '@types/express@5.0.6': + dependencies: + '@types/body-parser': 1.19.6 + '@types/express-serve-static-core': 5.1.1 + '@types/serve-static': 2.2.0 + + '@types/hast@3.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/http-errors@2.0.5': {} + + '@types/mdast@4.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/node@25.9.1': + dependencies: + undici-types: 7.24.6 + + '@types/qs@6.15.1': {} + + '@types/range-parser@1.2.7': {} + + '@types/react-dom@19.2.3(@types/react@19.2.15)': + dependencies: + '@types/react': 19.2.15 + + '@types/react@19.2.15': + dependencies: + csstype: 3.2.3 + + '@types/retry@0.12.0': {} + + '@types/semver@7.7.1': {} + + '@types/send@1.2.1': + dependencies: + '@types/node': 25.9.1 + + '@types/serve-static@2.2.0': + dependencies: + '@types/http-errors': 2.0.5 + '@types/node': 25.9.1 + + '@types/unist@3.0.3': {} + + '@ungap/structured-clone@1.3.1': {} + + '@vitejs/plugin-react@6.0.2(vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.3)(yaml@2.9.0))': + dependencies: + '@rolldown/pluginutils': 1.0.1 + vite: 8.0.16(@types/node@25.9.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.3)(yaml@2.9.0) + + accepts@2.0.0: + dependencies: + mime-types: 3.0.2 + negotiator: 1.0.0 + + agent-base@7.1.4: {} + + ajv-formats@3.0.1(ajv@8.20.0): + optionalDependencies: + ajv: 8.20.0 + + ajv@8.20.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.1.2 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + + balanced-match@4.0.4: {} + + base64-js@1.5.1: {} + + better-result@2.10.0: {} + + better-sqlite3@12.10.0: + dependencies: + bindings: 1.5.0 + prebuild-install: 7.1.3 + + bignumber.js@9.3.1: {} + + bindings@1.5.0: + dependencies: + file-uri-to-path: 1.0.0 + + bl@4.1.0: + dependencies: + buffer: 5.7.1 + inherits: 2.0.4 + readable-stream: 3.6.2 + + body-parser@2.2.2: + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 4.4.3 + http-errors: 2.0.1 + iconv-lite: 0.7.2 + on-finished: 2.4.1 + qs: 6.15.2 + raw-body: 3.0.2 + type-is: 2.1.0 + transitivePeerDependencies: + - supports-color + + bowser@2.14.1: {} + + brace-expansion@5.0.6: + dependencies: + balanced-match: 4.0.4 + + buffer-equal-constant-time@1.0.1: {} + + buffer@5.7.1: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + + bytes@3.1.2: {} + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + ccount@2.0.1: {} + + chalk@5.6.2: {} + + character-entities-html4@2.1.0: {} + + character-entities-legacy@3.0.0: {} + + chownr@1.1.4: {} + + comma-separated-tokens@2.0.3: {} + + commander@12.1.0: {} + + content-disposition@1.1.0: {} + + content-type@1.0.5: {} + + content-type@2.0.0: {} + + cookie-signature@1.2.2: {} + + cookie@0.7.2: {} + + cors@2.8.6: + dependencies: + object-assign: 4.1.1 + vary: 1.1.2 + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + csstype@3.2.3: {} + + data-uri-to-buffer@4.0.1: {} + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + decompress-response@6.0.0: + dependencies: + mimic-response: 3.1.0 + + deep-extend@0.6.0: {} + + depd@2.0.0: {} + + dequal@2.0.3: {} + + detect-libc@2.1.2: {} + + devlop@1.1.0: + dependencies: + dequal: 2.0.3 + + diff@8.0.3: {} + + diff@8.0.4: {} + + drizzle-orm@0.45.2(@opentelemetry/api@1.9.0)(@types/better-sqlite3@7.6.13)(better-sqlite3@12.10.0): + optionalDependencies: + '@opentelemetry/api': 1.9.0 + '@types/better-sqlite3': 7.6.13 + better-sqlite3: 12.10.0 + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + ecdsa-sig-formatter@1.0.11: + dependencies: + safe-buffer: 5.2.1 + + ee-first@1.1.1: {} + + encodeurl@2.0.0: {} + + end-of-stream@1.4.5: + dependencies: + once: 1.4.0 + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-object-atoms@1.1.2: + dependencies: + es-errors: 1.3.0 + + esbuild@0.28.1: + optionalDependencies: + '@esbuild/aix-ppc64': 0.28.1 + '@esbuild/android-arm': 0.28.1 + '@esbuild/android-arm64': 0.28.1 + '@esbuild/android-x64': 0.28.1 + '@esbuild/darwin-arm64': 0.28.1 + '@esbuild/darwin-x64': 0.28.1 + '@esbuild/freebsd-arm64': 0.28.1 + '@esbuild/freebsd-x64': 0.28.1 + '@esbuild/linux-arm': 0.28.1 + '@esbuild/linux-arm64': 0.28.1 + '@esbuild/linux-ia32': 0.28.1 + '@esbuild/linux-loong64': 0.28.1 + '@esbuild/linux-mips64el': 0.28.1 + '@esbuild/linux-ppc64': 0.28.1 + '@esbuild/linux-riscv64': 0.28.1 + '@esbuild/linux-s390x': 0.28.1 + '@esbuild/linux-x64': 0.28.1 + '@esbuild/netbsd-arm64': 0.28.1 + '@esbuild/netbsd-x64': 0.28.1 + '@esbuild/openbsd-arm64': 0.28.1 + '@esbuild/openbsd-x64': 0.28.1 + '@esbuild/openharmony-arm64': 0.28.1 + '@esbuild/sunos-x64': 0.28.1 + '@esbuild/win32-arm64': 0.28.1 + '@esbuild/win32-ia32': 0.28.1 + '@esbuild/win32-x64': 0.28.1 + + escape-html@1.0.3: {} + + etag@1.8.1: {} + + eventsource-parser@3.1.0: {} + + eventsource@3.0.7: + dependencies: + eventsource-parser: 3.1.0 + + expand-template@2.0.3: {} + + express-rate-limit@8.5.2(express@5.2.1): + dependencies: + express: 5.2.1 + ip-address: 10.2.0 + + express@5.2.1: + dependencies: + accepts: 2.0.0 + body-parser: 2.2.2 + content-disposition: 1.1.0 + content-type: 1.0.5 + cookie: 0.7.2 + cookie-signature: 1.2.2 + debug: 4.4.3 + depd: 2.0.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 2.1.1 + fresh: 2.0.0 + http-errors: 2.0.1 + merge-descriptors: 2.0.0 + mime-types: 3.0.2 + on-finished: 2.4.1 + once: 1.4.0 + parseurl: 1.3.3 + proxy-addr: 2.0.7 + qs: 6.15.2 + range-parser: 1.2.1 + router: 2.2.0 + send: 1.2.1 + serve-static: 2.2.1 + statuses: 2.0.2 + type-is: 2.1.0 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + + extend@3.0.2: {} + + fast-deep-equal@3.1.3: {} + + fast-sha256@1.3.0: {} + + fast-string-truncated-width@3.0.3: {} + + fast-string-width@3.0.2: + dependencies: + fast-string-truncated-width: 3.0.3 + + fast-uri@3.1.2: {} + + fast-wrap-ansi@0.2.2: + dependencies: + fast-string-width: 3.0.2 + + fast-xml-builder@1.2.0: + dependencies: + path-expression-matcher: 1.5.0 + xml-naming: 0.1.0 + + fast-xml-parser@5.7.3: + dependencies: + '@nodable/entities': 2.1.0 + fast-xml-builder: 1.2.0 + path-expression-matcher: 1.5.0 + strnum: 2.3.0 + + fdir@6.5.0(picomatch@4.0.4): + optionalDependencies: + picomatch: 4.0.4 + + fetch-blob@3.2.0: + dependencies: + node-domexception: 1.0.0 + web-streams-polyfill: 3.3.3 + + file-uri-to-path@1.0.0: {} + + finalhandler@2.1.1: + dependencies: + debug: 4.4.3 + encodeurl: 2.0.0 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + + formdata-polyfill@4.0.10: + dependencies: + fetch-blob: 3.2.0 + + forwarded@0.2.0: {} + + fresh@2.0.0: {} + + fs-constants@1.0.0: {} + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + gaxios@7.1.4: + dependencies: + extend: 3.0.2 + https-proxy-agent: 7.0.6 + node-fetch: 3.3.2 + transitivePeerDependencies: + - supports-color + + gcp-metadata@8.1.2: + dependencies: + gaxios: 7.1.4 + google-logging-utils: 1.1.3 + json-bigint: 1.0.0 + transitivePeerDependencies: + - supports-color + + get-east-asian-width@1.6.0: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.2 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.4 + math-intrinsics: 1.1.0 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.2 + + github-from-package@0.0.0: {} + + glob@13.0.6: + dependencies: + minimatch: 10.2.5 + minipass: 7.1.3 + path-scurry: 2.0.2 + + google-auth-library@10.6.2: + dependencies: + base64-js: 1.5.1 + ecdsa-sig-formatter: 1.0.11 + gaxios: 7.1.4 + gcp-metadata: 8.1.2 + google-logging-utils: 1.1.3 + jws: 4.0.1 + transitivePeerDependencies: + - supports-color + + google-logging-utils@1.1.3: {} + + gopd@1.2.0: {} + + graceful-fs@4.2.11: {} + + has-symbols@1.1.0: {} + + hasown@2.0.4: + dependencies: + function-bind: 1.1.2 + + hast-util-to-html@9.0.5: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + ccount: 2.0.1 + comma-separated-tokens: 2.0.3 + hast-util-whitespace: 3.0.0 + html-void-elements: 3.0.0 + mdast-util-to-hast: 13.2.1 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + stringify-entities: 4.0.4 + zwitch: 2.0.4 + + hast-util-whitespace@3.0.0: + dependencies: + '@types/hast': 3.0.4 + + highlight.js@10.7.3: {} + + hono@4.12.25: {} + + hosted-git-info@9.0.3: + dependencies: + lru-cache: 11.4.0 + + html-void-elements@3.0.0: {} + + http-errors@2.0.1: + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.2 + toidentifier: 1.0.1 + + http-proxy-agent@7.0.2: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + https-proxy-agent@7.0.6: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + iconv-lite@0.7.2: + dependencies: + safer-buffer: 2.1.2 + + ieee754@1.2.1: {} + + ignore@7.0.5: {} + + inherits@2.0.4: {} + + ini@1.3.8: {} + + ip-address@10.2.0: {} + + ipaddr.js@1.9.1: {} + + is-promise@4.0.0: {} + + isexe@2.0.0: {} + + jiti@2.7.0: {} + + jose@6.2.3: {} + + json-bigint@1.0.0: + dependencies: + bignumber.js: 9.3.1 + + json-schema-to-ts@3.1.1: + dependencies: + '@babel/runtime': 7.29.7 + ts-algebra: 2.0.0 + + json-schema-traverse@1.0.0: {} + + json-schema-typed@8.0.2: {} + + jsonc-parser@3.3.1: {} + + jwa@2.0.1: + dependencies: + buffer-equal-constant-time: 1.0.1 + ecdsa-sig-formatter: 1.0.11 + safe-buffer: 5.2.1 + + jws@4.0.1: + dependencies: + jwa: 2.0.1 + safe-buffer: 5.2.1 + + lightningcss-android-arm64@1.32.0: + optional: true + + lightningcss-darwin-arm64@1.32.0: + optional: true + + lightningcss-darwin-x64@1.32.0: + optional: true + + lightningcss-freebsd-x64@1.32.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.32.0: + optional: true + + lightningcss-linux-arm64-gnu@1.32.0: + optional: true + + lightningcss-linux-arm64-musl@1.32.0: + optional: true + + lightningcss-linux-x64-gnu@1.32.0: + optional: true + + lightningcss-linux-x64-musl@1.32.0: + optional: true + + lightningcss-win32-arm64-msvc@1.32.0: + optional: true + + lightningcss-win32-x64-msvc@1.32.0: + optional: true + + lightningcss@1.32.0: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.32.0 + lightningcss-darwin-arm64: 1.32.0 + lightningcss-darwin-x64: 1.32.0 + lightningcss-freebsd-x64: 1.32.0 + lightningcss-linux-arm-gnueabihf: 1.32.0 + lightningcss-linux-arm64-gnu: 1.32.0 + lightningcss-linux-arm64-musl: 1.32.0 + lightningcss-linux-x64-gnu: 1.32.0 + lightningcss-linux-x64-musl: 1.32.0 + lightningcss-win32-arm64-msvc: 1.32.0 + lightningcss-win32-x64-msvc: 1.32.0 + + long@5.3.2: {} + + lru-cache@11.4.0: {} + + lru_map@0.4.1: {} + + lucide@1.24.0: {} + + marked@18.0.5: {} + + math-intrinsics@1.1.0: {} + + mdast-util-to-hast@13.2.1: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@ungap/structured-clone': 1.3.1 + devlop: 1.1.0 + micromark-util-sanitize-uri: 2.0.1 + trim-lines: 3.0.1 + unist-util-position: 5.0.0 + unist-util-visit: 5.1.0 + vfile: 6.0.3 + + media-typer@1.1.0: {} + + merge-descriptors@2.0.0: {} + + micromark-util-character@2.1.1: + dependencies: + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-encode@2.0.1: {} + + micromark-util-sanitize-uri@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-encode: 2.0.1 + micromark-util-symbol: 2.0.1 + + micromark-util-symbol@2.0.1: {} + + micromark-util-types@2.0.2: {} + + mime-db@1.54.0: {} + + mime-types@3.0.2: + dependencies: + mime-db: 1.54.0 + + mimic-response@3.1.0: {} + + minimatch@10.2.5: + dependencies: + brace-expansion: 5.0.6 + + minimist@1.2.8: {} + + minipass@7.1.3: {} + + mkdirp-classic@0.5.3: {} + + ms@2.1.3: {} + + nanoid@3.3.12: {} + + napi-build-utils@2.0.0: {} + + negotiator@1.0.0: {} + + node-abi@3.92.0: + dependencies: + semver: 7.8.4 + + node-addon-api@7.1.1: + optional: true + + node-domexception@1.0.0: {} + + node-fetch@3.3.2: + dependencies: + data-uri-to-buffer: 4.0.1 + fetch-blob: 3.2.0 + formdata-polyfill: 4.0.10 + + node-forge@1.4.0: {} + + node-pty@1.1.0: + dependencies: + node-addon-api: 7.1.1 + optional: true + + object-assign@4.1.1: {} + + object-inspect@1.13.4: {} + + on-finished@2.4.1: + dependencies: + ee-first: 1.1.1 + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + oniguruma-parser@0.12.2: {} + + oniguruma-to-es@4.3.6: + dependencies: + oniguruma-parser: 0.12.2 + regex: 6.1.0 + regex-recursion: 6.0.2 + + openai@6.26.0(ws@8.21.0)(zod@4.4.3): + optionalDependencies: + ws: 8.21.0 + zod: 4.4.3 + + p-retry@4.6.2: + dependencies: + '@types/retry': 0.12.0 + retry: 0.13.1 + + parseurl@1.3.3: {} + + partial-json@0.1.7: {} + + path-expression-matcher@1.5.0: {} + + path-key@3.1.1: {} + + path-scurry@2.0.2: + dependencies: + lru-cache: 11.4.0 + minipass: 7.1.3 + + path-to-regexp@8.4.2: {} + + picocolors@1.1.1: {} + + picomatch@4.0.4: {} + + pkce-challenge@5.0.1: {} + + postcss@8.5.15: + dependencies: + nanoid: 3.3.12 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + prebuild-install@7.1.3: + dependencies: + detect-libc: 2.1.2 + expand-template: 2.0.3 + github-from-package: 0.0.0 + minimist: 1.2.8 + mkdirp-classic: 0.5.3 + napi-build-utils: 2.0.0 + node-abi: 3.92.0 + pump: 3.0.4 + rc: 1.2.8 + simple-get: 4.0.1 + tar-fs: 2.1.4 + tunnel-agent: 0.6.0 + + proper-lockfile@4.1.2: + dependencies: + graceful-fs: 4.2.11 + retry: 0.12.0 + signal-exit: 3.0.7 + + property-information@7.1.0: {} + + protobufjs@7.6.4: + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/base64': 1.1.2 + '@protobufjs/codegen': 2.0.5 + '@protobufjs/eventemitter': 1.1.1 + '@protobufjs/fetch': 1.1.1 + '@protobufjs/float': 1.0.2 + '@protobufjs/path': 1.1.2 + '@protobufjs/pool': 1.1.0 + '@protobufjs/utf8': 1.1.1 + '@types/node': 25.9.1 + long: 5.3.2 + + proxy-addr@2.0.7: + dependencies: + forwarded: 0.2.0 + ipaddr.js: 1.9.1 + + pump@3.0.4: + dependencies: + end-of-stream: 1.4.5 + once: 1.4.0 + + qs@6.15.2: + dependencies: + side-channel: 1.1.0 + + range-parser@1.2.1: {} + + raw-body@3.0.2: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.1 + iconv-lite: 0.7.2 + unpipe: 1.0.0 + + rc@1.2.8: + dependencies: + deep-extend: 0.6.0 + ini: 1.3.8 + minimist: 1.2.8 + strip-json-comments: 2.0.1 + + react-dom@19.2.6(react@19.2.6): + dependencies: + react: 19.2.6 + scheduler: 0.27.0 + + react@19.2.6: {} + + readable-stream@3.6.2: + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + + regex-recursion@6.0.2: + dependencies: + regex-utilities: 2.3.0 + + regex-utilities@2.3.0: {} + + regex@6.1.0: + dependencies: + regex-utilities: 2.3.0 + + require-from-string@2.0.2: {} + + retry@0.12.0: {} + + retry@0.13.1: {} + + rolldown@1.0.3: + dependencies: + '@oxc-project/types': 0.133.0 + '@rolldown/pluginutils': 1.0.1 + optionalDependencies: + '@rolldown/binding-android-arm64': 1.0.3 + '@rolldown/binding-darwin-arm64': 1.0.3 + '@rolldown/binding-darwin-x64': 1.0.3 + '@rolldown/binding-freebsd-x64': 1.0.3 + '@rolldown/binding-linux-arm-gnueabihf': 1.0.3 + '@rolldown/binding-linux-arm64-gnu': 1.0.3 + '@rolldown/binding-linux-arm64-musl': 1.0.3 + '@rolldown/binding-linux-ppc64-gnu': 1.0.3 + '@rolldown/binding-linux-s390x-gnu': 1.0.3 + '@rolldown/binding-linux-x64-gnu': 1.0.3 + '@rolldown/binding-linux-x64-musl': 1.0.3 + '@rolldown/binding-openharmony-arm64': 1.0.3 + '@rolldown/binding-wasm32-wasi': 1.0.3 + '@rolldown/binding-win32-arm64-msvc': 1.0.3 + '@rolldown/binding-win32-x64-msvc': 1.0.3 + + router@2.2.0: + dependencies: + debug: 4.4.3 + depd: 2.0.0 + is-promise: 4.0.0 + parseurl: 1.3.3 + path-to-regexp: 8.4.2 + transitivePeerDependencies: + - supports-color + + safe-buffer@5.2.1: {} + + safer-buffer@2.1.2: {} + + scheduler@0.27.0: {} + + semver@7.8.0: {} + + semver@7.8.4: {} + + send@1.2.1: + dependencies: + debug: 4.4.3 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 2.0.0 + http-errors: 2.0.1 + mime-types: 3.0.2 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + + serve-static@2.2.1: + dependencies: + encodeurl: 2.0.0 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 1.2.1 + transitivePeerDependencies: + - supports-color + + setprototypeof@1.2.0: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + shiki@3.23.0: + dependencies: + '@shikijs/core': 3.23.0 + '@shikijs/engine-javascript': 3.23.0 + '@shikijs/engine-oniguruma': 3.23.0 + '@shikijs/langs': 3.23.0 + '@shikijs/themes': 3.23.0 + '@shikijs/types': 3.23.0 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + + side-channel-list@1.0.1: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + + side-channel@1.1.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.1 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + + signal-exit@3.0.7: {} + + simple-concat@1.0.1: {} + + simple-get@4.0.1: + dependencies: + decompress-response: 6.0.0 + once: 1.4.0 + simple-concat: 1.0.1 + + sisteransi@1.0.5: {} + + source-map-js@1.2.1: {} + + space-separated-tokens@2.0.2: {} + + standardwebhooks@1.0.0: + dependencies: + '@stablelib/base64': 1.0.1 + fast-sha256: 1.3.0 + + statuses@2.0.2: {} + + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + + stringify-entities@4.0.4: + dependencies: + character-entities-html4: 2.1.0 + character-entities-legacy: 3.0.0 + + strip-json-comments@2.0.1: {} + + strnum@2.3.0: {} + + tar-fs@2.1.4: + dependencies: + chownr: 1.1.4 + mkdirp-classic: 0.5.3 + pump: 3.0.4 + tar-stream: 2.2.0 + + tar-stream@2.2.0: + dependencies: + bl: 4.1.0 + end-of-stream: 1.4.5 + fs-constants: 1.0.0 + inherits: 2.0.4 + readable-stream: 3.6.2 + + tinyglobby@0.2.17: + dependencies: + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 + + toidentifier@1.0.1: {} + + trim-lines@3.0.1: {} + + ts-algebra@2.0.0: {} + + tslib@2.8.1: {} + + tsx@4.22.3: + dependencies: + esbuild: 0.28.1 + optionalDependencies: + fsevents: 2.3.3 + + tunnel-agent@0.6.0: + dependencies: + safe-buffer: 5.2.1 + + type-is@2.1.0: + dependencies: + content-type: 2.0.0 + media-typer: 1.1.0 + mime-types: 3.0.2 + + typebox@1.1.38: {} + + typescript@6.0.3: {} + + undici-types@7.24.6: {} + + undici@8.5.0: {} + + unist-util-is@6.0.1: + dependencies: + '@types/unist': 3.0.3 + + unist-util-position@5.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-stringify-position@4.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-visit-parents@6.0.2: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + + unist-util-visit@5.1.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + unist-util-visit-parents: 6.0.2 + + unpipe@1.0.0: {} + + util-deprecate@1.0.2: {} + + vary@1.1.2: {} + + vfile-message@4.0.3: + dependencies: + '@types/unist': 3.0.3 + unist-util-stringify-position: 4.0.0 + + vfile@6.0.3: + dependencies: + '@types/unist': 3.0.3 + vfile-message: 4.0.3 + + vite@8.0.16(@types/node@25.9.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.22.3)(yaml@2.9.0): + dependencies: + lightningcss: 1.32.0 + picomatch: 4.0.4 + postcss: 8.5.15 + rolldown: 1.0.3 + tinyglobby: 0.2.17 + optionalDependencies: + '@types/node': 25.9.1 + esbuild: 0.28.1 + fsevents: 2.3.3 + jiti: 2.7.0 + tsx: 4.22.3 + yaml: 2.9.0 + + web-streams-polyfill@3.3.3: {} + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + wrappy@1.0.2: {} + + ws@8.21.0: {} + + xml-naming@0.1.0: {} + + yaml@2.9.0: {} + + zod-to-json-schema@3.25.2(zod@4.4.3): + dependencies: + zod: 4.4.3 + + zod@3.25.76: {} + + zod@4.4.3: {} + + zwitch@2.0.4: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 000000000..54fb78321 --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,6 @@ +allowBuilds: + '@google/genai': false + better-sqlite3: true + esbuild: true + node-pty: true + protobufjs: false From 14832bbc83dfb055f2978fd35dd8b33c66b05559 Mon Sep 17 00:00:00 2001 From: Waishnav <86405648+Waishnav@users.noreply.github.com> Date: Mon, 31 Aug 2026 08:52:16 +0530 Subject: [PATCH 63/75] chore: run tooling with pnpm --- .github/workflows/ci.yml | 17 +++++++++-------- package.json | 4 ++-- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9db018c62..cfc7150b4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,14 +30,15 @@ jobs: - name: Checkout uses: actions/checkout@v4 - - name: Setup Node - uses: actions/setup-node@v4 + - name: Setup pnpm and Node + uses: pnpm/setup@v2 with: - node-version: 22 - cache: npm + runtime: node@22 + cache: true + install: false - name: Install dependencies - run: npm ci + run: pnpm install --frozen-lockfile - name: Install Pi sandbox dependencies if: matrix.os == 'ubuntu-latest' @@ -49,15 +50,15 @@ jobs: fi - name: Typecheck - run: npm run typecheck + run: pnpm typecheck - name: Test env: DEVSPACE_REQUIRE_PI_SANDBOX: ${{ matrix.os == 'ubuntu-latest' && '1' || '0' }} - run: npm test + run: pnpm test - name: Build - run: npm run build + run: pnpm build - name: Doctor run: node dist/cli.js doctor diff --git a/package.json b/package.json index 45bb446ce..1696333d3 100644 --- a/package.json +++ b/package.json @@ -27,11 +27,11 @@ }, "scripts": { "clean": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"", - "build": "npm run clean && npm run build:app && tsc -p tsconfig.build.json", + "build": "pnpm clean && pnpm build:app && tsc -p tsconfig.build.json", "build:app": "vite build", "dev": "tsx watch --clear-screen=false src/cli.ts serve", "postinstall": "node scripts/fix-node-pty-permissions.mjs", - "prepack": "npm run build", + "prepack": "pnpm build", "schema:config": "tsx scripts/generate-config-schema.ts", "start": "node dist/cli.js serve", "test": "tsx --test --test-concurrency=1 \"src/**/*.test.ts\"", From 75e4ed8b71aa1e4c58513076a1b07338b115c51f Mon Sep 17 00:00:00 2001 From: Waishnav <86405648+Waishnav@users.noreply.github.com> Date: Mon, 31 Aug 2026 08:52:36 +0530 Subject: [PATCH 64/75] docs: use pnpm for local development --- README.md | 12 ++++++------ docs/setup.md | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index b1e4f0a3f..d0be41db1 100644 --- a/README.md +++ b/README.md @@ -247,10 +247,10 @@ This year, I began my journey to build a one-person, multi-agent company capable For working on DevSpace itself: ```bash -npm install --include=dev -npm run dev -npm run typecheck -npm test -npm run build -npm run start +pnpm install --frozen-lockfile +pnpm dev +pnpm typecheck +pnpm test +pnpm build +pnpm start ``` diff --git a/docs/setup.md b/docs/setup.md index e5e76d8f9..fa8e7ae2c 100644 --- a/docs/setup.md +++ b/docs/setup.md @@ -136,8 +136,8 @@ Git, Bash, public URL, allowed hosts, and SQLite native dependency status. If you are developing DevSpace itself instead of using the published package: ```bash -npm install --include=dev -npm run dev +pnpm install --frozen-lockfile +pnpm dev ``` The same setup rules apply. From d6dbe95170792536df77537e10f35b95ccb2eb0c Mon Sep 17 00:00:00 2001 From: Waishnav <86405648+Waishnav@users.noreply.github.com> Date: Mon, 31 Aug 2026 09:24:44 +0530 Subject: [PATCH 65/75] fix(ci): stabilize pnpm workflow --- .github/workflows/ci.yml | 5 ++++- src/config.test.ts | 10 +++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cfc7150b4..be5a8f3d5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,6 +6,9 @@ on: - main pull_request: +permissions: + contents: read + concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} cancel-in-progress: true @@ -31,7 +34,7 @@ jobs: uses: actions/checkout@v4 - name: Setup pnpm and Node - uses: pnpm/setup@v2 + uses: pnpm/setup@84cb39b217b10273981911c288cd62326dc7c6d2 # v2 with: runtime: node@22 cache: true diff --git a/src/config.test.ts b/src/config.test.ts index 47a39652a..5fd24b490 100644 --- a/src/config.test.ts +++ b/src/config.test.ts @@ -1,6 +1,6 @@ import assert from "node:assert/strict"; import { mkdtempSync, rmSync } from "node:fs"; -import { tmpdir } from "node:os"; +import { homedir, tmpdir } from "node:os"; import { join, resolve } from "node:path"; import { loadConfig } from "./config.js"; import { writeDevspaceAuth, writeDevspaceConfig } from "./user-config.js"; @@ -77,7 +77,7 @@ try { assert.equal(configured.host, "0.0.0.0"); assert.equal(configured.port, 8787); assert.equal(configured.publicBaseUrl, "https://devspace.example.com"); - assert.deepEqual(configured.allowedRoots, [resolve("~/work".replace("~", process.env.HOME!))]); + assert.deepEqual(configured.allowedRoots, [resolve(homedir(), "work")]); assert.deepEqual(configured.allowedHosts, [ "localhost", "127.0.0.1", @@ -88,13 +88,13 @@ try { ]); assert.equal(configured.toolMode, "claude"); assert.equal(configured.uiEnabled, false); - assert.equal(configured.stateDir, resolve(process.env.HOME!, "state")); - assert.equal(configured.worktreeRoot, resolve(process.env.HOME!, "trees")); + assert.equal(configured.stateDir, resolve(homedir(), "state")); + assert.equal(configured.worktreeRoot, resolve(homedir(), "trees")); assert.equal(configured.artifactsEnabled, true); assert.equal(configured.artifactMaxFileBytes, 321); assert.equal(configured.skillsEnabled, false); assert.deepEqual(configured.skillPaths, ["~/skills"]); - assert.equal(configured.agentDir, resolve(process.env.HOME!, "agent")); + assert.equal(configured.agentDir, resolve(homedir(), "agent")); assert.equal(configured.subagents.enabled, true); assert.equal(configured.oauth.ownerToken, "persisted-owner-token-long-enough"); assert.equal(configured.oauth.accessTokenTtlSeconds, 120); From 6f90f492cc29ac8eee629a7903656ea95c1f73f0 Mon Sep 17 00:00:00 2001 From: Waishnav <86405648+Waishnav@users.noreply.github.com> Date: Mon, 31 Aug 2026 09:24:44 +0530 Subject: [PATCH 66/75] docs: document local pnpm requirement --- README.md | 3 +++ docs/setup.md | 3 +++ 2 files changed, 6 insertions(+) diff --git a/README.md b/README.md index d0be41db1..381930db8 100644 --- a/README.md +++ b/README.md @@ -246,6 +246,9 @@ This year, I began my journey to build a one-person, multi-agent company capable For working on DevSpace itself: +Install pnpm 11.25.0, the version pinned in `package.json`, with +`npm install --global pnpm@11.25.0`, then: + ```bash pnpm install --frozen-lockfile pnpm dev diff --git a/docs/setup.md b/docs/setup.md index fa8e7ae2c..6f707bff8 100644 --- a/docs/setup.md +++ b/docs/setup.md @@ -135,6 +135,9 @@ Git, Bash, public URL, allowed hosts, and SQLite native dependency status. If you are developing DevSpace itself instead of using the published package: +Local checkout development additionally requires pnpm 11.25.0, the version +pinned in `package.json`. Install it with `npm install --global pnpm@11.25.0`. + ```bash pnpm install --frozen-lockfile pnpm dev From 9dc487349f65970f514952733f953b60b0d23ca3 Mon Sep 17 00:00:00 2001 From: Waishnav <86405648+Waishnav@users.noreply.github.com> Date: Mon, 31 Aug 2026 09:48:49 +0530 Subject: [PATCH 67/75] chore(deps): upgrade Pierre diffs --- package-lock.json | 64 ++++++++++++++++++++++++++++++++++++++--------- package.json | 2 +- 2 files changed, 53 insertions(+), 13 deletions(-) diff --git a/package-lock.json b/package-lock.json index e14a7e90f..8971718ec 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,7 @@ "@modelcontextprotocol/ext-apps": "^1.7.2", "@modelcontextprotocol/sdk": "^1.29.0", "@opencode-ai/sdk": "^1.17.13", - "@pierre/diffs": "^1.2.5", + "@pierre/diffs": "^1.3.6", "better-result": "^2.10.0", "better-sqlite3": "^12.10.0", "cross-spawn": "^7.0.6", @@ -2694,32 +2694,72 @@ } }, "node_modules/@pierre/diffs": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/@pierre/diffs/-/diffs-1.2.5.tgz", - "integrity": "sha512-uYOz3Kfs5ED0qY0VraUXzylsEKvZPTVdexboM3QKPx/qBZmTT9F3lKAFuPpY5aIrV04sdHtoFCKStyzEu99U2A==", + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/@pierre/diffs/-/diffs-1.3.6.tgz", + "integrity": "sha512-a3woaW2QHy78JDxPJK0OJzwZUN4xoQLLIS/pceO8X6+L8gA5D682mP7/w3YxxEVRPXOaoe/p/RJ5Oj/3nrEzew==", "license": "apache-2.0", "dependencies": { - "@pierre/theme": "1.0.3", - "@shikijs/transformers": "^3.0.0", - "diff": "8.0.3", + "@pierre/theme": "2.0.0", + "@pierre/theming": "1.0.1", + "@shikijs/transformers": "^3.0.0 || ^4.0.0", + "diff": "9.0.0", "hast-util-to-html": "9.0.5", "lru_map": "0.4.1", - "shiki": "^3.0.0" + "shiki": "^3.0.0 || ^4.0.0" }, "peerDependencies": { "react": "^18.3.1 || ^19.0.0", "react-dom": "^18.3.1 || ^19.0.0" } }, + "node_modules/@pierre/diffs/node_modules/diff": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-9.0.0.tgz", + "integrity": "sha512-svtcdpS8CgJyqAjEQIXdb3OjhFVVYjzGAPO8WGCmRbrml64SPw/jJD4GoE98aR7r25A0XcgrK3F02yw9R/vhQw==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/@pierre/theme": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@pierre/theme/-/theme-1.0.3.tgz", - "integrity": "sha512-sWHv11TMoqKxKDgTIk5VbhQjdPhs8DCcBxbjh3mRlS3YOM/OcrWoGX6MM8eBGn9cUu3M46Py0JnxsG2nJaFTuA==", - "license": "MIT", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@pierre/theme/-/theme-2.0.0.tgz", + "integrity": "sha512-yNDd9GYLQl1mEUJR8AneJ5e4ohLIHQd/wZLWr4fagt78vS2RwwZNW530vVgHqXFAyFVcFlRmGUD5ramXH46OXw==", + "license": "apache-2.0", "engines": { "vscode": "^1.0.0" } }, + "node_modules/@pierre/theming": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@pierre/theming/-/theming-1.0.1.tgz", + "integrity": "sha512-WCI5Qd7iprDpISL9fBYOLe8RV53+b7mFNA3bPzl60/2CKCSrsKN8zEcep6Y3BAzvARlmca50zGjDodqPGiTUKA==", + "license": "apache-2.0", + "peerDependencies": { + "@pierre/theme": "^1.1.0 || ^2.0.0", + "@shikijs/themes": "^3.0.0 || ^4.0.0", + "react": "^18.3.1 || ^19.0.0", + "react-dom": "^18.3.1 || ^19.0.0", + "shiki": "^3.0.0 || ^4.0.0" + }, + "peerDependenciesMeta": { + "@pierre/theme": { + "optional": true + }, + "@shikijs/themes": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + }, + "shiki": { + "optional": true + } + } + }, "node_modules/@pondwader/socks5-server": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/@pondwader/socks5-server/-/socks5-server-1.0.10.tgz", diff --git a/package.json b/package.json index 3dad18389..d27f5bce4 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "@modelcontextprotocol/ext-apps": "^1.7.2", "@modelcontextprotocol/sdk": "^1.29.0", "@opencode-ai/sdk": "^1.17.13", - "@pierre/diffs": "^1.2.5", + "@pierre/diffs": "^1.3.6", "better-result": "^2.10.0", "better-sqlite3": "^12.10.0", "cross-spawn": "^7.0.6", From cdc20bd1c640e48971bdc4334f40c2fc75a2b43e Mon Sep 17 00:00:00 2001 From: Waishnav <86405648+Waishnav@users.noreply.github.com> Date: Mon, 31 Aug 2026 09:48:49 +0530 Subject: [PATCH 68/75] fix(ui): prevent iOS diff text inflation --- src/ui/workspace-app.css | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/ui/workspace-app.css b/src/ui/workspace-app.css index 92dc4bf2e..9d7735194 100644 --- a/src/ui/workspace-app.css +++ b/src/ui/workspace-app.css @@ -793,8 +793,11 @@ body { --diffs-dark-bg: var(--tool-payload-bg, var(--color-background-primary, #101114)); --diffs-font-family: var(--font-mono, ui-monospace, SFMono-Regular, monospace); --diffs-header-font-family: var(--font-sans, ui-sans-serif, system-ui, sans-serif); - --diffs-font-size: var(--font-text-sm-size, 12px); + /* Keep iOS WebKit from inflating dense diff text independently of the card UI. */ + --diffs-font-size: 12px; --diffs-line-height: 20px; + -webkit-text-size-adjust: 100%; + text-size-adjust: 100%; display: block; overflow: visible; border-bottom-right-radius: 8px; From 5100bbe7b4e0b840026ce25619b989050569e1c9 Mon Sep 17 00:00:00 2001 From: Waishnav <86405648+Waishnav@users.noreply.github.com> Date: Mon, 31 Aug 2026 21:46:22 +0530 Subject: [PATCH 69/75] docs: document Tailscale Funnel root proxy setup --- docs/gotchas.md | 13 +++++++++++++ docs/setup.md | 10 ++++++++++ 2 files changed, 23 insertions(+) diff --git a/docs/gotchas.md b/docs/gotchas.md index 5f6288678..3bf7dd2d8 100644 --- a/docs/gotchas.md +++ b/docs/gotchas.md @@ -65,6 +65,19 @@ If you saved the wrong value: npx @waishnav/devspace config set publicBaseUrl https://your-tunnel-host.example.com ``` +## Tailscale Funnel `/mcp` Returns 404 + +Proxy the whole DevSpace server from the Funnel root: + +```bash +tailscale funnel --bg 7676 +``` + +Do not use `--set-path=/mcp`. Tailscale removes a configured mount path before +proxying to the local service, so a public `/mcp` request can otherwise arrive +at DevSpace as `/`. DevSpace also needs OAuth routes outside `/mcp`, so serving +the whole local origin is the correct setup. + ## Tunnel URL Changed Temporary tunnels often change URLs between runs. diff --git a/docs/setup.md b/docs/setup.md index 6f707bff8..83a66a6eb 100644 --- a/docs/setup.md +++ b/docs/setup.md @@ -77,6 +77,16 @@ reverse proxy first and point it at: http://127.0.0.1:7676 ``` +For Tailscale Funnel, proxy the whole DevSpace server from the root path: + +```bash +tailscale funnel --bg 7676 +``` + +Do not mount Funnel only at `/mcp` with `--set-path=/mcp`. DevSpace also serves +OAuth discovery and authorization routes outside `/mcp`, and a path mount can +strip `/mcp` before the request reaches DevSpace. + Enter the public origin without `/mcp`: ```text From 216e50d30cdd463cf57d94d2936973caa7e8d9e8 Mon Sep 17 00:00:00 2001 From: Rokurolize <1701388+Rokurolize@users.noreply.github.com> Date: Sat, 5 Sep 2026 11:04:27 +0900 Subject: [PATCH 70/75] fix: keep linked runtime aligned with source --- bin/devspace-agentd.js | 4 +- bin/devspace.js | 4 +- bin/run-entrypoint.js | 20 +++++++++ src/bin-launcher.test.ts | 89 ++++++++++++++++++++++++++++++++++++++++ src/config-migration.ts | 4 +- src/user-config.test.ts | 10 +++++ 6 files changed, 128 insertions(+), 3 deletions(-) create mode 100644 bin/run-entrypoint.js create mode 100644 src/bin-launcher.test.ts diff --git a/bin/devspace-agentd.js b/bin/devspace-agentd.js index 268c36097..e6c54ca1b 100755 --- a/bin/devspace-agentd.js +++ b/bin/devspace-agentd.js @@ -1,2 +1,4 @@ #!/usr/bin/env node -import "../dist/local-agent-daemon-main.js"; +import { runEntrypoint } from "./run-entrypoint.js"; + +await runEntrypoint("../src/local-agent-daemon-main.ts", "../dist/local-agent-daemon-main.js"); diff --git a/bin/devspace.js b/bin/devspace.js index 8fb127218..4e4788a1b 100755 --- a/bin/devspace.js +++ b/bin/devspace.js @@ -1,2 +1,4 @@ #!/usr/bin/env node -import "../dist/cli.js"; +import { runEntrypoint } from "./run-entrypoint.js"; + +await runEntrypoint("../src/cli.ts", "../dist/cli.js"); diff --git a/bin/run-entrypoint.js b/bin/run-entrypoint.js new file mode 100644 index 000000000..f32983a1c --- /dev/null +++ b/bin/run-entrypoint.js @@ -0,0 +1,20 @@ +import { existsSync } from "node:fs"; +import { fileURLToPath } from "node:url"; + +export async function runEntrypoint(sourcePath, distPath) { + const sourceUrl = new URL(sourcePath, import.meta.url); + if (existsSync(fileURLToPath(sourceUrl))) { + try { + await import("tsx/esm"); + } catch (error) { + throw new Error( + "DevSpace source checkout detected, but tsx is unavailable. Run `pnpm install` in the checkout; refusing to fall back to potentially stale dist output.", + { cause: error }, + ); + } + await import(sourceUrl.href); + return; + } + + await import(new URL(distPath, import.meta.url).href); +} diff --git a/src/bin-launcher.test.ts b/src/bin-launcher.test.ts new file mode 100644 index 000000000..00b43a99f --- /dev/null +++ b/src/bin-launcher.test.ts @@ -0,0 +1,89 @@ +import assert from "node:assert/strict"; +import { execFileSync } from "node:child_process"; +import { cpSync, mkdirSync, mkdtempSync, rmSync, symlinkSync, writeFileSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; +import { writeTestDevspaceConfig } from "./test-support/config.test.js"; + +const projectRoot = fileURLToPath(new URL("..", import.meta.url)); +const tsxRoot = join(projectRoot, "node_modules", "tsx"); + +for (const entrypoint of [ + { + bin: "devspace.js", + source: "src/cli.ts", + dist: "dist/cli.js", + }, + { + bin: "devspace-agentd.js", + source: "src/local-agent-daemon-main.ts", + dist: "dist/local-agent-daemon-main.js", + }, +]) { + testLauncher(entrypoint); +} + +testLinkedCheckoutReadsCurrentConfig(); +testMissingSourceRuntimeFailsClosed(); + +function testLinkedCheckoutReadsCurrentConfig(): void { + const root = mkdtempSync(join(tmpdir(), "devspace-bin-config-test-")); + try { + const env = writeTestDevspaceConfig(root, { tools: { mode: "codex" } }); + const output = execFileSync(process.execPath, [join(projectRoot, "bin", "devspace.js"), "config", "get"], { + encoding: "utf8", + env: { ...process.env, ...env }, + }); + const config = JSON.parse(output) as { tools?: { mode?: string } }; + assert.equal(config.tools?.mode, "codex"); + } finally { + rmSync(root, { recursive: true, force: true }); + } +} + +function testMissingSourceRuntimeFailsClosed(): void { + const root = mkdtempSync(join(tmpdir(), "devspace-bin-missing-tsx-test-")); + try { + cpSync(join(projectRoot, "bin"), join(root, "bin"), { recursive: true }); + mkdirSync(join(root, "src"), { recursive: true }); + mkdirSync(join(root, "dist"), { recursive: true }); + writeFileSync(join(root, "package.json"), JSON.stringify({ type: "module" })); + writeFileSync(join(root, "src", "cli.ts"), 'console.log("source");\n'); + writeFileSync(join(root, "dist", "cli.js"), 'console.log("stale-dist");\n'); + + assert.throws( + () => execFileSync(process.execPath, [join(root, "bin", "devspace.js")], { encoding: "utf8", stdio: "pipe" }), + /source checkout.*tsx.*pnpm install/is, + ); + } finally { + rmSync(root, { recursive: true, force: true }); + } +} + +function testLauncher(entrypoint: { bin: string; source: string; dist: string }): void { + const root = mkdtempSync(join(tmpdir(), "devspace-bin-launcher-test-")); + try { + cpSync(join(projectRoot, "bin"), join(root, "bin"), { recursive: true }); + mkdirSync(dirname(join(root, entrypoint.source)), { recursive: true }); + mkdirSync(dirname(join(root, entrypoint.dist)), { recursive: true }); + mkdirSync(join(root, "node_modules"), { recursive: true }); + symlinkSync(tsxRoot, join(root, "node_modules", "tsx"), process.platform === "win32" ? "junction" : "dir"); + writeFileSync(join(root, "package.json"), JSON.stringify({ type: "module" })); + writeFileSync(join(root, entrypoint.source), 'console.log("source");\n'); + writeFileSync(join(root, entrypoint.dist), 'console.log("dist");\n'); + + const sourceOutput = execFileSync(process.execPath, [join(root, "bin", entrypoint.bin)], { + encoding: "utf8", + }).trim(); + assert.equal(sourceOutput, "source", `${entrypoint.bin} must prefer source in a linked checkout`); + + rmSync(join(root, entrypoint.source)); + const packagedOutput = execFileSync(process.execPath, [join(root, "bin", entrypoint.bin)], { + encoding: "utf8", + }).trim(); + assert.equal(packagedOutput, "dist", `${entrypoint.bin} must use dist in a published package`); + } finally { + rmSync(root, { recursive: true, force: true }); + } +} diff --git a/src/config-migration.ts b/src/config-migration.ts index f24851adb..d6192a8cc 100644 --- a/src/config-migration.ts +++ b/src/config-migration.ts @@ -22,6 +22,7 @@ const legacyConfigSchema = z.object({ tools: z.object({ mode: z.enum(["claude", "codex"]).optional(), }).strict().optional(), + "tools.mode": z.enum(["claude", "codex"]).optional(), ui: z.object({ enabled: z.boolean().optional(), }).strict().optional(), @@ -40,6 +41,7 @@ const LEGACY_CONFIG_KEYS = new Set([ "agentDir", "subagents", "tools", + "tools.mode", "ui", ]); @@ -65,7 +67,7 @@ export function migrateLegacyConfig(value: unknown): DevspaceConfig { worktreeRoot: legacy.worktreeRoot, }), storage: definedEntries({ stateDir: legacy.stateDir }), - tools: definedEntries({ mode: legacy.tools?.mode }), + tools: definedEntries({ mode: legacy.tools?.mode ?? legacy["tools.mode"] }), ui: definedEntries({ enabled: legacy.ui?.enabled }), artifacts: definedEntries({ enabled: legacy.artifactsEnabled, diff --git a/src/user-config.test.ts b/src/user-config.test.ts index 8e09b9122..0729a7a67 100644 --- a/src/user-config.test.ts +++ b/src/user-config.test.ts @@ -46,6 +46,16 @@ withConfigDir((configDir, env) => { assert.equal(nextLoad.migratedLegacyConfig, false); }); +withConfigDir((configDir, env) => { + writeFileSync(join(configDir, "config.json"), JSON.stringify({ + "tools.mode": "claude", + })); + + const files = loadDevspaceFiles(env); + assert.equal(files.migratedLegacyConfig, true); + assert.equal(files.config.tools.mode, "claude"); +}); + await withConfigDirAsync(async (configDir) => { writeFileSync(join(configDir, "config.json"), JSON.stringify({ port: 8787, From 62812de83b9f407cb4cd09cebe60a32397422d80 Mon Sep 17 00:00:00 2001 From: Rokurolize <1701388+Rokurolize@users.noreply.github.com> Date: Sat, 5 Sep 2026 11:24:22 +0900 Subject: [PATCH 71/75] test: cover migrated codex package paths --- src/bin-launcher.test.ts | 80 +++++++++++++++++++++++++++++++++++++++- src/user-config.test.ts | 15 ++++++++ 2 files changed, 94 insertions(+), 1 deletion(-) diff --git a/src/bin-launcher.test.ts b/src/bin-launcher.test.ts index 00b43a99f..4be0a36f3 100644 --- a/src/bin-launcher.test.ts +++ b/src/bin-launcher.test.ts @@ -1,6 +1,6 @@ import assert from "node:assert/strict"; import { execFileSync } from "node:child_process"; -import { cpSync, mkdirSync, mkdtempSync, rmSync, symlinkSync, writeFileSync } from "node:fs"; +import { cpSync, mkdirSync, mkdtempSync, readdirSync, rmSync, symlinkSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; import { dirname, join } from "node:path"; import { fileURLToPath } from "node:url"; @@ -26,6 +26,7 @@ for (const entrypoint of [ testLinkedCheckoutReadsCurrentConfig(); testMissingSourceRuntimeFailsClosed(); +testPackedPackageLaunchers(); function testLinkedCheckoutReadsCurrentConfig(): void { const root = mkdtempSync(join(tmpdir(), "devspace-bin-config-test-")); @@ -61,6 +62,59 @@ function testMissingSourceRuntimeFailsClosed(): void { } } +function testPackedPackageLaunchers(): void { + const root = mkdtempSync(join(tmpdir(), "devspace-packed-bin-test-")); + const installRoot = join(root, "install"); + try { + mkdirSync(installRoot, { recursive: true }); + execFileSync(npmExecutable(), ["pack", "--silent", "--pack-destination", root], { + cwd: projectRoot, + encoding: "utf8", + stdio: "pipe", + shell: process.platform === "win32", + }); + const archive = readdirSync(root).find((name) => name.endsWith(".tgz")); + assert.ok(archive, "npm pack must produce a package archive"); + + execFileSync(npmExecutable(), [ + "install", + "--no-audit", + "--no-fund", + "--no-package-lock", + "--no-save", + "--omit=optional", + join(root, archive), + ], { + cwd: installRoot, + encoding: "utf8", + stdio: "pipe", + shell: process.platform === "win32", + }); + + const configRoot = join(root, "config"); + const env = writeTestDevspaceConfig(configRoot, { + storage: { stateDir: join(root, "state") }, + workspaces: { allowedRoots: [root], worktreeRoot: join(root, "worktrees") }, + skills: { agentDir: join(root, "agents") }, + }); + const cliOutput = execInstalledBin(installRoot, "devspace", ["config", "get"], { + ...process.env, + ...env, + }); + const config = JSON.parse(cliOutput) as { tools?: { mode?: string } }; + assert.equal(config.tools?.mode, "codex"); + + execInstalledBin(installRoot, "devspace-agentd", [], { + ...process.env, + ...env, + DEVSPACE_AGENTD_IDLE_TIMEOUT_MS: "0", + DEVSPACE_AGENTD_SHUTDOWN_TIMEOUT_MS: "1000", + }); + } finally { + rmSync(root, { recursive: true, force: true }); + } +} + function testLauncher(entrypoint: { bin: string; source: string; dist: string }): void { const root = mkdtempSync(join(tmpdir(), "devspace-bin-launcher-test-")); try { @@ -87,3 +141,27 @@ function testLauncher(entrypoint: { bin: string; source: string; dist: string }) rmSync(root, { recursive: true, force: true }); } } + +function npmExecutable(): string { + return process.platform === "win32" ? "npm.cmd" : "npm"; +} + +function execInstalledBin( + installRoot: string, + name: string, + args: string[], + env: NodeJS.ProcessEnv, +): string { + const executable = join( + installRoot, + "node_modules", + ".bin", + process.platform === "win32" ? `${name}.cmd` : name, + ); + return execFileSync(executable, args, { + encoding: "utf8", + env, + stdio: "pipe", + shell: process.platform === "win32", + }); +} diff --git a/src/user-config.test.ts b/src/user-config.test.ts index 0729a7a67..088926091 100644 --- a/src/user-config.test.ts +++ b/src/user-config.test.ts @@ -9,6 +9,8 @@ import { } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; +import { loadConfig } from "./config.js"; +import { getToolSurface } from "./tool-surfaces/index.js"; import { loadDevspaceFiles, setDevspaceConfigValue, @@ -56,6 +58,19 @@ withConfigDir((configDir, env) => { assert.equal(files.config.tools.mode, "claude"); }); +withConfigDir((configDir, env) => { + writeFileSync(join(configDir, "config.json"), JSON.stringify({ + "tools.mode": "codex", + })); + + const config = loadConfig({ + ...env, + DEVSPACE_OAUTH_OWNER_TOKEN: "test-owner-token-that-is-long-enough", + }); + assert.equal(config.toolMode, "codex"); + assert.strictEqual(getToolSurface(config.toolMode), getToolSurface("codex")); +}); + await withConfigDirAsync(async (configDir) => { writeFileSync(join(configDir, "config.json"), JSON.stringify({ port: 8787, From f582c693b87a62fb3ebb815f0adcabb1c1b04062 Mon Sep 17 00:00:00 2001 From: Rokurolize <1701388+Rokurolize@users.noreply.github.com> Date: Sat, 5 Sep 2026 11:32:06 +0900 Subject: [PATCH 72/75] test: assert codex surface registration --- src/user-config.test.ts | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/user-config.test.ts b/src/user-config.test.ts index 088926091..f8fd169cc 100644 --- a/src/user-config.test.ts +++ b/src/user-config.test.ts @@ -11,6 +11,7 @@ import { tmpdir } from "node:os"; import { join } from "node:path"; import { loadConfig } from "./config.js"; import { getToolSurface } from "./tool-surfaces/index.js"; +import type { ToolRegistrationContext } from "./tool-surfaces/types.js"; import { loadDevspaceFiles, setDevspaceConfigValue, @@ -68,7 +69,19 @@ withConfigDir((configDir, env) => { DEVSPACE_OAUTH_OWNER_TOKEN: "test-owner-token-that-is-long-enough", }); assert.equal(config.toolMode, "codex"); - assert.strictEqual(getToolSurface(config.toolMode), getToolSurface("codex")); + + const registeredTools: string[] = []; + getToolSurface(config.toolMode).register({ + server: { + registerTool(name: string) { + registeredTools.push(name); + }, + }, + config, + workspaces: {}, + processSessions: {}, + } as unknown as ToolRegistrationContext); + assert.deepEqual(registeredTools, ["apply_patch", "exec_command", "write_stdin"]); }); await withConfigDirAsync(async (configDir) => { From a971bef55b7c3fb9ce554bdac4001ce114dcdb03 Mon Sep 17 00:00:00 2001 From: Rokurolize <1701388+Rokurolize@users.noreply.github.com> Date: Sat, 5 Sep 2026 22:14:17 +0900 Subject: [PATCH 73/75] fix: drop unshipped tool mode migration --- src/config-migration.ts | 4 +--- src/user-config.test.ts | 38 -------------------------------------- 2 files changed, 1 insertion(+), 41 deletions(-) diff --git a/src/config-migration.ts b/src/config-migration.ts index d6192a8cc..f24851adb 100644 --- a/src/config-migration.ts +++ b/src/config-migration.ts @@ -22,7 +22,6 @@ const legacyConfigSchema = z.object({ tools: z.object({ mode: z.enum(["claude", "codex"]).optional(), }).strict().optional(), - "tools.mode": z.enum(["claude", "codex"]).optional(), ui: z.object({ enabled: z.boolean().optional(), }).strict().optional(), @@ -41,7 +40,6 @@ const LEGACY_CONFIG_KEYS = new Set([ "agentDir", "subagents", "tools", - "tools.mode", "ui", ]); @@ -67,7 +65,7 @@ export function migrateLegacyConfig(value: unknown): DevspaceConfig { worktreeRoot: legacy.worktreeRoot, }), storage: definedEntries({ stateDir: legacy.stateDir }), - tools: definedEntries({ mode: legacy.tools?.mode ?? legacy["tools.mode"] }), + tools: definedEntries({ mode: legacy.tools?.mode }), ui: definedEntries({ enabled: legacy.ui?.enabled }), artifacts: definedEntries({ enabled: legacy.artifactsEnabled, diff --git a/src/user-config.test.ts b/src/user-config.test.ts index f8fd169cc..8e09b9122 100644 --- a/src/user-config.test.ts +++ b/src/user-config.test.ts @@ -9,9 +9,6 @@ import { } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; -import { loadConfig } from "./config.js"; -import { getToolSurface } from "./tool-surfaces/index.js"; -import type { ToolRegistrationContext } from "./tool-surfaces/types.js"; import { loadDevspaceFiles, setDevspaceConfigValue, @@ -49,41 +46,6 @@ withConfigDir((configDir, env) => { assert.equal(nextLoad.migratedLegacyConfig, false); }); -withConfigDir((configDir, env) => { - writeFileSync(join(configDir, "config.json"), JSON.stringify({ - "tools.mode": "claude", - })); - - const files = loadDevspaceFiles(env); - assert.equal(files.migratedLegacyConfig, true); - assert.equal(files.config.tools.mode, "claude"); -}); - -withConfigDir((configDir, env) => { - writeFileSync(join(configDir, "config.json"), JSON.stringify({ - "tools.mode": "codex", - })); - - const config = loadConfig({ - ...env, - DEVSPACE_OAUTH_OWNER_TOKEN: "test-owner-token-that-is-long-enough", - }); - assert.equal(config.toolMode, "codex"); - - const registeredTools: string[] = []; - getToolSurface(config.toolMode).register({ - server: { - registerTool(name: string) { - registeredTools.push(name); - }, - }, - config, - workspaces: {}, - processSessions: {}, - } as unknown as ToolRegistrationContext); - assert.deepEqual(registeredTools, ["apply_patch", "exec_command", "write_stdin"]); -}); - await withConfigDirAsync(async (configDir) => { writeFileSync(join(configDir, "config.json"), JSON.stringify({ port: 8787, From f7577f171d63910ef54558ad8f15e98dfdc20c15 Mon Sep 17 00:00:00 2001 From: Rokurolize <1701388+Rokurolize@users.noreply.github.com> Date: Sat, 5 Sep 2026 22:14:17 +0900 Subject: [PATCH 74/75] test: separate package install smoke coverage --- .github/workflows/ci.yml | 3 + package.json | 1 + src/bin-launcher.test.ts | 80 +-------------------------- test/package-install-smoke.test.ts | 88 ++++++++++++++++++++++++++++++ 4 files changed, 93 insertions(+), 79 deletions(-) create mode 100644 test/package-install-smoke.test.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index be5a8f3d5..1cb828446 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -60,6 +60,9 @@ jobs: DEVSPACE_REQUIRE_PI_SANDBOX: ${{ matrix.os == 'ubuntu-latest' && '1' || '0' }} run: pnpm test + - name: Package install smoke test + run: pnpm test:package-install + - name: Build run: pnpm build diff --git a/package.json b/package.json index 9a4c2a83a..1835ba80d 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "schema:config": "tsx scripts/generate-config-schema.ts", "start": "node dist/cli.js serve", "test": "tsx --test --test-concurrency=1 \"src/**/*.test.ts\"", + "test:package-install": "tsx --test --test-concurrency=1 \"test/package-install-smoke.test.ts\"", "typecheck": "tsc -p tsconfig.json --noEmit" }, "keywords": [], diff --git a/src/bin-launcher.test.ts b/src/bin-launcher.test.ts index 4be0a36f3..00b43a99f 100644 --- a/src/bin-launcher.test.ts +++ b/src/bin-launcher.test.ts @@ -1,6 +1,6 @@ import assert from "node:assert/strict"; import { execFileSync } from "node:child_process"; -import { cpSync, mkdirSync, mkdtempSync, readdirSync, rmSync, symlinkSync, writeFileSync } from "node:fs"; +import { cpSync, mkdirSync, mkdtempSync, rmSync, symlinkSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; import { dirname, join } from "node:path"; import { fileURLToPath } from "node:url"; @@ -26,7 +26,6 @@ for (const entrypoint of [ testLinkedCheckoutReadsCurrentConfig(); testMissingSourceRuntimeFailsClosed(); -testPackedPackageLaunchers(); function testLinkedCheckoutReadsCurrentConfig(): void { const root = mkdtempSync(join(tmpdir(), "devspace-bin-config-test-")); @@ -62,59 +61,6 @@ function testMissingSourceRuntimeFailsClosed(): void { } } -function testPackedPackageLaunchers(): void { - const root = mkdtempSync(join(tmpdir(), "devspace-packed-bin-test-")); - const installRoot = join(root, "install"); - try { - mkdirSync(installRoot, { recursive: true }); - execFileSync(npmExecutable(), ["pack", "--silent", "--pack-destination", root], { - cwd: projectRoot, - encoding: "utf8", - stdio: "pipe", - shell: process.platform === "win32", - }); - const archive = readdirSync(root).find((name) => name.endsWith(".tgz")); - assert.ok(archive, "npm pack must produce a package archive"); - - execFileSync(npmExecutable(), [ - "install", - "--no-audit", - "--no-fund", - "--no-package-lock", - "--no-save", - "--omit=optional", - join(root, archive), - ], { - cwd: installRoot, - encoding: "utf8", - stdio: "pipe", - shell: process.platform === "win32", - }); - - const configRoot = join(root, "config"); - const env = writeTestDevspaceConfig(configRoot, { - storage: { stateDir: join(root, "state") }, - workspaces: { allowedRoots: [root], worktreeRoot: join(root, "worktrees") }, - skills: { agentDir: join(root, "agents") }, - }); - const cliOutput = execInstalledBin(installRoot, "devspace", ["config", "get"], { - ...process.env, - ...env, - }); - const config = JSON.parse(cliOutput) as { tools?: { mode?: string } }; - assert.equal(config.tools?.mode, "codex"); - - execInstalledBin(installRoot, "devspace-agentd", [], { - ...process.env, - ...env, - DEVSPACE_AGENTD_IDLE_TIMEOUT_MS: "0", - DEVSPACE_AGENTD_SHUTDOWN_TIMEOUT_MS: "1000", - }); - } finally { - rmSync(root, { recursive: true, force: true }); - } -} - function testLauncher(entrypoint: { bin: string; source: string; dist: string }): void { const root = mkdtempSync(join(tmpdir(), "devspace-bin-launcher-test-")); try { @@ -141,27 +87,3 @@ function testLauncher(entrypoint: { bin: string; source: string; dist: string }) rmSync(root, { recursive: true, force: true }); } } - -function npmExecutable(): string { - return process.platform === "win32" ? "npm.cmd" : "npm"; -} - -function execInstalledBin( - installRoot: string, - name: string, - args: string[], - env: NodeJS.ProcessEnv, -): string { - const executable = join( - installRoot, - "node_modules", - ".bin", - process.platform === "win32" ? `${name}.cmd` : name, - ); - return execFileSync(executable, args, { - encoding: "utf8", - env, - stdio: "pipe", - shell: process.platform === "win32", - }); -} diff --git a/test/package-install-smoke.test.ts b/test/package-install-smoke.test.ts new file mode 100644 index 000000000..c53477fd8 --- /dev/null +++ b/test/package-install-smoke.test.ts @@ -0,0 +1,88 @@ +import assert from "node:assert/strict"; +import { execFileSync } from "node:child_process"; +import { mkdirSync, mkdtempSync, readdirSync, rmSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { fileURLToPath } from "node:url"; +import { writeTestDevspaceConfig } from "../src/test-support/config.test.js"; + +const projectRoot = fileURLToPath(new URL("..", import.meta.url)); + +testPackedPackageLaunchers(); + +function testPackedPackageLaunchers(): void { + const root = mkdtempSync(join(tmpdir(), "devspace-packed-bin-test-")); + const installRoot = join(root, "install"); + try { + mkdirSync(installRoot, { recursive: true }); + execFileSync(npmExecutable(), ["pack", "--silent", "--pack-destination", root], { + cwd: projectRoot, + encoding: "utf8", + stdio: "pipe", + shell: process.platform === "win32", + }); + const archive = readdirSync(root).find((name) => name.endsWith(".tgz")); + assert.ok(archive, "npm pack must produce a package archive"); + + execFileSync(npmExecutable(), [ + "install", + "--no-audit", + "--no-fund", + "--no-package-lock", + "--no-save", + "--omit=optional", + join(root, archive), + ], { + cwd: installRoot, + encoding: "utf8", + stdio: "pipe", + shell: process.platform === "win32", + }); + + const configRoot = join(root, "config"); + const env = writeTestDevspaceConfig(configRoot, { + storage: { stateDir: join(root, "state") }, + workspaces: { allowedRoots: [root], worktreeRoot: join(root, "worktrees") }, + skills: { agentDir: join(root, "agents") }, + }); + const cliOutput = execInstalledBin(installRoot, "devspace", ["config", "get"], { + ...process.env, + ...env, + }); + const config = JSON.parse(cliOutput) as { tools?: { mode?: string } }; + assert.equal(config.tools?.mode, "codex"); + + execInstalledBin(installRoot, "devspace-agentd", [], { + ...process.env, + ...env, + DEVSPACE_AGENTD_IDLE_TIMEOUT_MS: "0", + DEVSPACE_AGENTD_SHUTDOWN_TIMEOUT_MS: "1000", + }); + } finally { + rmSync(root, { recursive: true, force: true }); + } +} + +function npmExecutable(): string { + return process.platform === "win32" ? "npm.cmd" : "npm"; +} + +function execInstalledBin( + installRoot: string, + name: string, + args: string[], + env: NodeJS.ProcessEnv, +): string { + const executable = join( + installRoot, + "node_modules", + ".bin", + process.platform === "win32" ? `${name}.cmd` : name, + ); + return execFileSync(executable, args, { + encoding: "utf8", + env, + stdio: "pipe", + shell: process.platform === "win32", + }); +} From 9ed423202f70dcad5c3a94963212c9580d981a9b Mon Sep 17 00:00:00 2001 From: Rokurolize <1701388+Rokurolize@users.noreply.github.com> Date: Sat, 5 Sep 2026 23:14:46 +0900 Subject: [PATCH 75/75] chore: trigger fork CI validation