noumen 0.5.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +237 -93
- package/dist/a2a/index.d.ts +5 -7
- package/dist/a2a/index.js +3 -4
- package/dist/a2a/index.js.map +1 -1
- package/dist/acp/index.d.ts +5 -7
- package/dist/acp/index.js +0 -1
- package/dist/acp/index.js.map +1 -1
- package/dist/{agent-C3eDRsxs.d.ts → agent-D0gl-qYi.d.ts} +259 -31
- package/dist/{chunk-WPCYGZOE.js → chunk-5HY4IYNT.js} +2062 -2545
- package/dist/chunk-5HY4IYNT.js.map +1 -0
- package/dist/chunk-BC5BLWBC.js +21 -0
- package/dist/chunk-BC5BLWBC.js.map +1 -0
- package/dist/{chunk-XZN4QZLK.js → chunk-CX4BL6PC.js} +25 -15
- package/dist/chunk-CX4BL6PC.js.map +1 -0
- package/dist/{chunk-5GEX6ZSB.js → chunk-HQISH4D7.js} +60 -1
- package/dist/chunk-HQISH4D7.js.map +1 -0
- package/dist/{chunk-Y45R3PQL.js → chunk-NUCJXOUV.js} +32 -18
- package/dist/{chunk-Y45R3PQL.js.map → chunk-NUCJXOUV.js.map} +1 -1
- package/dist/chunk-OPFFLQZL.js +40 -0
- package/dist/chunk-OPFFLQZL.js.map +1 -0
- package/dist/chunk-PDEAJ272.js +660 -0
- package/dist/chunk-PDEAJ272.js.map +1 -0
- package/dist/chunk-PKHLGGEC.js +115 -0
- package/dist/chunk-PKHLGGEC.js.map +1 -0
- package/dist/chunk-XQTNXRE7.js +176 -0
- package/dist/chunk-XQTNXRE7.js.map +1 -0
- package/dist/chunk-XZPAA5TO.js +817 -0
- package/dist/chunk-XZPAA5TO.js.map +1 -0
- package/dist/cli/index.js +77 -42
- package/dist/cli/index.js.map +1 -1
- package/dist/client/index.d.ts +1 -2
- package/dist/client/index.js +0 -2
- package/dist/client/index.js.map +1 -1
- package/dist/client-JJFLE6RT.js +9 -0
- package/dist/{computer-BPdxSo6X.d.ts → computer-DzMR92tK.d.ts} +1 -1
- package/dist/docker.d.ts +2 -2
- package/dist/docker.js +0 -1
- package/dist/docker.js.map +1 -1
- package/dist/e2b.d.ts +2 -2
- package/dist/e2b.js +0 -1
- package/dist/e2b.js.map +1 -1
- package/dist/freestyle.d.ts +2 -2
- package/dist/freestyle.js +0 -1
- package/dist/freestyle.js.map +1 -1
- package/dist/{headless-FFU2DESQ.js → headless-25DU4MJQ.js} +1 -3
- package/dist/{headless-FFU2DESQ.js.map → headless-25DU4MJQ.js.map} +1 -1
- package/dist/{history-snip-64GYP4ZL.js → history-snip-HAWNAYKY.js} +1 -2
- package/dist/index.d.ts +358 -72
- package/dist/index.js +68 -55
- package/dist/jsonrpc/index.js +0 -1
- package/dist/local.d.ts +168 -0
- package/dist/local.js +40 -0
- package/dist/local.js.map +1 -0
- package/dist/lsp/index.d.ts +4 -5
- package/dist/lsp/index.js +0 -1
- package/dist/{lsp-PS3BWIHC.js → lsp-3APWNKB2.js} +1 -2
- package/dist/{manager-DLXK63XC.js → manager-Z5EQ7YYV.js} +1 -2
- package/dist/mcp/index.d.ts +16 -8
- package/dist/mcp/index.js +5 -6
- package/dist/mcp/index.js.map +1 -1
- package/dist/{mcp-auth-AEI2R4ZC.js → mcp-auth-NOIQPF7W.js} +1 -2
- package/dist/{provider-factory-KI7OZUY3.js → provider-factory-KNBSHXJ6.js} +3 -3
- package/dist/{render-GRN4ZSSW.js → render-4VEODRK7.js} +1 -2
- package/dist/{resolve-GDSHNMG6.js → resolve-AGQZFMKD.js} +3 -3
- package/dist/sandbox-DAqQo0Tj.d.ts +49 -0
- package/dist/sandbox-index-ODNREIFA.js +32 -0
- package/dist/sandbox-index-ODNREIFA.js.map +1 -0
- package/dist/server/index.d.ts +18 -7
- package/dist/server/index.js +9 -5
- package/dist/server/index.js.map +1 -1
- package/dist/{server-Cu9gv1dk.d.ts → server-DFXdlqyX.d.ts} +1 -1
- package/dist/{spinner-OJNR6NFO.js → spinner-72JEISPK.js} +1 -2
- package/dist/sprites.d.ts +2 -2
- package/dist/sprites.js +0 -1
- package/dist/sprites.js.map +1 -1
- package/dist/ssh.d.ts +2 -2
- package/dist/ssh.js +0 -1
- package/dist/ssh.js.map +1 -1
- package/dist/{types-BA87bHPV.d.ts → types-BX4ALqoN.d.ts} +76 -4
- package/dist/{types-LrU4LRmX.d.ts → types-DLZNyF5t.d.ts} +164 -2
- package/dist/unsandboxed.d.ts +59 -0
- package/dist/unsandboxed.js +32 -0
- package/dist/unsandboxed.js.map +1 -0
- package/dist/{uuid-RVN2T26F.js → uuid-CVTNAPEB.js} +1 -2
- package/dist/{zod-7YXKWYMC.js → zod-VKURGPRT.js} +1 -2
- package/package.json +35 -50
- package/dist/cache-DsRqxx6v.d.ts +0 -38
- package/dist/chunk-5GEX6ZSB.js.map +0 -1
- package/dist/chunk-CS6WNDCF.js +0 -171
- package/dist/chunk-CS6WNDCF.js.map +0 -1
- package/dist/chunk-DGUM43GV.js +0 -11
- package/dist/chunk-EKOGVTBT.js +0 -472
- package/dist/chunk-EKOGVTBT.js.map +0 -1
- package/dist/chunk-HEQQQGK5.js +0 -131
- package/dist/chunk-HEQQQGK5.js.map +0 -1
- package/dist/chunk-L3L3FG5T.js +0 -16
- package/dist/chunk-L3L3FG5T.js.map +0 -1
- package/dist/chunk-WPCYGZOE.js.map +0 -1
- package/dist/chunk-WTLK2ZAR.js +0 -94
- package/dist/chunk-WTLK2ZAR.js.map +0 -1
- package/dist/chunk-XZN4QZLK.js.map +0 -1
- package/dist/client-CRRO2376.js +0 -10
- package/dist/providers/anthropic.d.ts +0 -19
- package/dist/providers/anthropic.js +0 -35
- package/dist/providers/anthropic.js.map +0 -1
- package/dist/providers/bedrock.d.ts +0 -39
- package/dist/providers/bedrock.js +0 -56
- package/dist/providers/bedrock.js.map +0 -1
- package/dist/providers/gemini.d.ts +0 -17
- package/dist/providers/gemini.js +0 -262
- package/dist/providers/gemini.js.map +0 -1
- package/dist/providers/ollama.d.ts +0 -13
- package/dist/providers/ollama.js +0 -20
- package/dist/providers/ollama.js.map +0 -1
- package/dist/providers/openai.d.ts +0 -21
- package/dist/providers/openai.js +0 -9
- package/dist/providers/openrouter.d.ts +0 -16
- package/dist/providers/openrouter.js +0 -24
- package/dist/providers/openrouter.js.map +0 -1
- package/dist/providers/vertex.d.ts +0 -42
- package/dist/providers/vertex.js +0 -67
- package/dist/providers/vertex.js.map +0 -1
- package/dist/sandbox-9qeMTNrD.d.ts +0 -126
- package/dist/types-CD0rUKKT.d.ts +0 -109
- package/dist/uuid-RVN2T26F.js.map +0 -1
- package/dist/zod-7YXKWYMC.js.map +0 -1
- /package/dist/{chunk-DGUM43GV.js.map → client-JJFLE6RT.js.map} +0 -0
- /package/dist/{client-CRRO2376.js.map → history-snip-HAWNAYKY.js.map} +0 -0
- /package/dist/{history-snip-64GYP4ZL.js.map → lsp-3APWNKB2.js.map} +0 -0
- /package/dist/{lsp-PS3BWIHC.js.map → manager-Z5EQ7YYV.js.map} +0 -0
- /package/dist/{manager-DLXK63XC.js.map → mcp-auth-NOIQPF7W.js.map} +0 -0
- /package/dist/{mcp-auth-AEI2R4ZC.js.map → provider-factory-KNBSHXJ6.js.map} +0 -0
- /package/dist/{provider-factory-KI7OZUY3.js.map → render-4VEODRK7.js.map} +0 -0
- /package/dist/{providers/openai.js.map → resolve-AGQZFMKD.js.map} +0 -0
- /package/dist/{render-GRN4ZSSW.js.map → spinner-72JEISPK.js.map} +0 -0
- /package/dist/{resolve-GDSHNMG6.js.map → uuid-CVTNAPEB.js.map} +0 -0
- /package/dist/{spinner-OJNR6NFO.js.map → zod-VKURGPRT.js.map} +0 -0
package/dist/sprites.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/virtual/sprites-fs.ts","../src/virtual/sprites-computer.ts","../src/virtual/sprites-sandbox.ts"],"sourcesContent":["import * as path from \"node:path\";\nimport type { VirtualFs, FileEntry, FileStat, ReadOptions } from \"./fs.js\";\n\nexport interface SpritesFsOptions {\n /** sprites.dev API token */\n token: string;\n /** Name of the sprite container */\n spriteName: string;\n /** Base URL for sprites API (default: https://api.sprites.dev) */\n baseURL?: string;\n /** Working directory inside the sprite (default: /home/sprite) */\n workingDir?: string;\n}\n\n/**\n * Sandboxed VirtualFs backed by a remote sprites.dev container. All file\n * operations are executed over the sprites.dev HTTP API — the agent has no\n * access to the host filesystem. This is the recommended VirtualFs for\n * production deployments and untrusted agents. See `LocalFs` for an\n * unsandboxed local alternative.\n */\nexport class SpritesFs implements VirtualFs {\n private token: string;\n private spriteName: string;\n private baseURL: string;\n private workingDir: string;\n\n constructor(opts: SpritesFsOptions) {\n this.token = opts.token;\n this.spriteName = opts.spriteName;\n this.baseURL = (opts.baseURL ?? \"https://api.sprites.dev\").replace(\n /\\/$/,\n \"\",\n );\n this.workingDir = opts.workingDir ?? \"/home/sprite\";\n }\n\n private fsUrl(endpoint: string, params?: Record<string, string>): string {\n const url = new URL(\n `${this.baseURL}/v1/sprites/${this.spriteName}/fs${endpoint}`,\n );\n if (params) {\n for (const [k, v] of Object.entries(params)) {\n url.searchParams.set(k, v);\n }\n }\n return url.toString();\n }\n\n private resolvePath(p: string): string {\n const normalizedBase = this.workingDir.endsWith(\"/\") ? this.workingDir : this.workingDir + \"/\";\n if (p.startsWith(\"/\")) {\n if (p !== this.workingDir && !p.startsWith(normalizedBase)) {\n throw new Error(`Absolute path \"${p}\" is outside working directory \"${this.workingDir}\"`);\n }\n return p;\n }\n const resolved = path.resolve(this.workingDir, p);\n if (resolved !== this.workingDir && !resolved.startsWith(normalizedBase)) {\n throw new Error(`Path \"${p}\" escapes working directory \"${this.workingDir}\"`);\n }\n return resolved;\n }\n\n private headers(): Record<string, string> {\n return {\n Authorization: `Bearer ${this.token}`,\n };\n }\n\n async readFile(filePath: string, _opts?: ReadOptions): Promise<string> {\n const url = this.fsUrl(\"/read\", { path: this.resolvePath(filePath) });\n const res = await fetch(url, { headers: this.headers() });\n if (!res.ok) {\n throw new Error(\n `SpritesFs readFile failed (${res.status}): ${await res.text()}`,\n );\n }\n return res.text();\n }\n\n async readFileBytes(filePath: string, maxBytes?: number): Promise<Buffer> {\n const url = this.fsUrl(\"/read\", {\n path: this.resolvePath(filePath),\n binary: \"true\",\n });\n const res = await fetch(url, { headers: this.headers() });\n if (!res.ok) {\n throw new Error(\n `SpritesFs readFileBytes failed (${res.status}): ${await res.text()}`,\n );\n }\n const arrayBuf = await res.arrayBuffer();\n const buf = Buffer.from(arrayBuf);\n if (maxBytes !== undefined && buf.length > maxBytes) {\n return buf.subarray(0, maxBytes);\n }\n return buf;\n }\n\n async writeFile(filePath: string, content: string): Promise<void> {\n const url = this.fsUrl(\"/write\");\n const res = await fetch(url, {\n method: \"POST\",\n headers: {\n ...this.headers(),\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n path: this.resolvePath(filePath),\n content,\n }),\n });\n if (!res.ok) {\n throw new Error(\n `SpritesFs writeFile failed (${res.status}): ${await res.text()}`,\n );\n }\n }\n\n /**\n * @warning Not atomic. Concurrent appends may lose data due to\n * read-then-write TOCTOU. The Sprites API does not expose an append primitive.\n */\n async appendFile(filePath: string, content: string): Promise<void> {\n let existing = \"\";\n try {\n existing = await this.readFile(filePath);\n } catch {\n // file may not exist yet\n }\n await this.writeFile(filePath, existing + content);\n }\n\n async deleteFile(\n filePath: string,\n opts?: { recursive?: boolean },\n ): Promise<void> {\n const url = this.fsUrl(\"/remove\");\n const res = await fetch(url, {\n method: \"POST\",\n headers: {\n ...this.headers(),\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n path: this.resolvePath(filePath),\n recursive: opts?.recursive ?? false,\n }),\n });\n if (!res.ok) {\n throw new Error(\n `SpritesFs deleteFile failed (${res.status}): ${await res.text()}`,\n );\n }\n }\n\n async mkdir(\n dirPath: string,\n opts?: { recursive?: boolean },\n ): Promise<void> {\n const url = this.fsUrl(\"/mkdir\");\n const res = await fetch(url, {\n method: \"POST\",\n headers: {\n ...this.headers(),\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n path: this.resolvePath(dirPath),\n recursive: opts?.recursive ?? false,\n }),\n });\n if (!res.ok) {\n throw new Error(\n `SpritesFs mkdir failed (${res.status}): ${await res.text()}`,\n );\n }\n }\n\n async readdir(\n dirPath: string,\n _opts?: { recursive?: boolean },\n ): Promise<FileEntry[]> {\n const url = this.fsUrl(\"/readdir\", { path: this.resolvePath(dirPath) });\n const res = await fetch(url, { headers: this.headers() });\n if (!res.ok) {\n throw new Error(\n `SpritesFs readdir failed (${res.status}): ${await res.text()}`,\n );\n }\n const data = (await res.json()) as Array<{\n name: string;\n path: string;\n is_dir: boolean;\n size?: number;\n }>;\n return data.map((entry) => ({\n name: entry.name,\n path: entry.path,\n isDirectory: entry.is_dir,\n isFile: !entry.is_dir,\n size: entry.size,\n }));\n }\n\n async exists(filePath: string): Promise<boolean> {\n try {\n await this.stat(filePath);\n return true;\n } catch {\n return false;\n }\n }\n\n async stat(filePath: string): Promise<FileStat> {\n const url = this.fsUrl(\"/stat\", { path: this.resolvePath(filePath) });\n const res = await fetch(url, { headers: this.headers() });\n if (!res.ok) {\n throw new Error(\n `SpritesFs stat failed (${res.status}): ${await res.text()}`,\n );\n }\n const data = (await res.json()) as {\n size: number;\n is_dir: boolean;\n created_at?: string;\n modified_at?: string;\n };\n return {\n size: data.size,\n isDirectory: data.is_dir,\n isFile: !data.is_dir,\n createdAt: data.created_at ? new Date(data.created_at) : undefined,\n modifiedAt: data.modified_at ? new Date(data.modified_at) : undefined,\n };\n }\n}\n","import type {\n VirtualComputer,\n ExecOptions,\n CommandResult,\n} from \"./computer.js\";\n\nexport interface SpritesComputerOptions {\n /** sprites.dev API token */\n token: string;\n /** Name of the sprite container */\n spriteName: string;\n /** Base URL for sprites API (default: https://api.sprites.dev) */\n baseURL?: string;\n /** Working directory inside the sprite (default: /home/sprite) */\n workingDir?: string;\n}\n\n/**\n * Sandboxed VirtualComputer that executes commands inside a remote\n * sprites.dev container. All shell execution is fully isolated — the agent\n * has no access to the host machine's processes, filesystem, or network.\n *\n * This is the recommended VirtualComputer for production deployments and\n * untrusted agents. See `LocalComputer` for an unsandboxed local alternative.\n *\n * Uses the non-interactive exec REST endpoint (POST command, receive\n * stdout/stderr/exit_code). The WebSocket exec endpoint can be used for\n * streaming/TTY use cases, but REST is sufficient for tool calls.\n */\nexport class SpritesComputer implements VirtualComputer {\n private token: string;\n private spriteName: string;\n private baseURL: string;\n private workingDir: string;\n\n constructor(opts: SpritesComputerOptions) {\n this.token = opts.token;\n this.spriteName = opts.spriteName;\n this.baseURL = (opts.baseURL ?? \"https://api.sprites.dev\").replace(\n /\\/$/,\n \"\",\n );\n this.workingDir = opts.workingDir ?? \"/home/sprite\";\n }\n\n private headers(): Record<string, string> {\n return {\n Authorization: `Bearer ${this.token}`,\n \"Content-Type\": \"application/json\",\n };\n }\n\n async executeCommand(\n command: string,\n opts?: ExecOptions,\n ): Promise<CommandResult> {\n const cwd = opts?.cwd ?? this.workingDir;\n const wrappedCommand = `cd ${this.shellEscape(cwd)} && ${command}`;\n\n const url = `${this.baseURL}/v1/sprites/${this.spriteName}/exec`;\n\n const res = await fetch(url, {\n method: \"POST\",\n headers: this.headers(),\n body: JSON.stringify({\n command: [\"bash\", \"-c\", wrappedCommand],\n timeout: opts?.timeout ?? 30_000,\n env: opts?.env,\n }),\n });\n\n if (!res.ok) {\n const text = await res.text();\n return {\n exitCode: 1,\n stdout: \"\",\n stderr: `Sprites exec failed (${res.status}): ${text}`,\n };\n }\n\n const data = (await res.json()) as {\n exit_code: number;\n stdout: string;\n stderr: string;\n };\n\n return {\n exitCode: data.exit_code,\n stdout: data.stdout ?? \"\",\n stderr: data.stderr ?? \"\",\n };\n }\n\n private shellEscape(s: string): string {\n return `'${s.replace(/'/g, \"'\\\\''\")}'`;\n }\n}\n","import type { Sandbox } from \"./sandbox.js\";\nimport { SpritesFs } from \"./sprites-fs.js\";\nimport { SpritesComputer } from \"./sprites-computer.js\";\nimport { createFsProxy, createComputerProxy } from \"./proxy.js\";\n\nexport interface SpritesSandboxOptions {\n /** sprites.dev API token. */\n token: string;\n /**\n * Name of an existing sprite container. When provided the sandbox\n * attaches to this sprite directly — no auto-creation occurs and\n * `dispose()` will **not** delete it (lifecycle is yours to manage).\n *\n * When omitted a new sprite is provisioned on the first `init()` call\n * (via `POST /v1/sprites`). The auto-created sprite is deleted when\n * `dispose()` is called, and its name is available via `sandboxId()`\n * for session persistence.\n */\n spriteName?: string;\n /** Base URL for sprites API (default: https://api.sprites.dev). */\n baseURL?: string;\n /** Working directory inside the sprite (default: /home/sprite). */\n workingDir?: string;\n /**\n * Optional prefix for auto-generated sprite names (default: \"noumen-\").\n * Only used when `spriteName` is omitted.\n */\n namePrefix?: string;\n}\n\n/**\n * Create a `Sandbox` backed by a remote sprites.dev container.\n * Full isolation — the agent has no access to the host machine.\n *\n * **Auto-creation:** When `spriteName` is omitted the sandbox is created\n * lazily on the first `init()` call via the Sprites REST API. The sprite\n * name is available through `sandboxId()` so callers can persist it in\n * session metadata for reconnection on resume. Pass the stored name back\n * through `init(storedId)` to reattach instead of creating a new sprite.\n *\n * **Explicit ID:** When `spriteName` is provided the sandbox attaches to\n * that sprite immediately on `init()`. `dispose()` is a no-op in this\n * case — the caller owns the sprite's lifecycle.\n *\n * @example\n * ```ts\n * // Auto-create — sprite provisioned on first init()\n * const sandbox = SpritesSandbox({ token: process.env.SPRITES_TOKEN! });\n *\n * // Explicit — attach to pre-existing sprite, no auto-lifecycle\n * const sandbox = SpritesSandbox({\n * token: process.env.SPRITES_TOKEN!,\n * spriteName: \"my-sprite\",\n * });\n * ```\n */\nexport function SpritesSandbox(opts: SpritesSandboxOptions): Sandbox {\n const baseURL = (opts.baseURL ?? \"https://api.sprites.dev\").replace(/\\/$/, \"\");\n const userProvidedName = opts.spriteName;\n\n if (userProvidedName) {\n const fsOpts = { ...opts, spriteName: userProvidedName };\n return {\n fs: new SpritesFs(fsOpts),\n computer: new SpritesComputer(fsOpts),\n sandboxId: () => userProvidedName,\n };\n }\n\n const fsProxy = createFsProxy();\n const computerProxy = createComputerProxy();\n let resolvedName: string | undefined;\n let autoCreated = false;\n let initPromise: Promise<void> | null = null;\n\n async function doInit(reconnectId?: string): Promise<void> {\n let name = reconnectId ?? `${opts.namePrefix ?? \"noumen-\"}${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;\n let needsCreate = !reconnectId;\n\n if (reconnectId) {\n const check = await fetch(`${baseURL}/v1/sprites/${reconnectId}`, {\n method: \"GET\",\n headers: { Authorization: `Bearer ${opts.token}` },\n });\n if (!check.ok) {\n name = `${opts.namePrefix ?? \"noumen-\"}${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;\n needsCreate = true;\n }\n }\n\n if (needsCreate) {\n const res = await fetch(`${baseURL}/v1/sprites`, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${opts.token}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({ name }),\n });\n if (!res.ok) {\n throw new Error(`Sprites auto-create failed (${res.status}): ${await res.text()}`);\n }\n autoCreated = true;\n }\n\n resolvedName = name;\n const childOpts = { ...opts, spriteName: name };\n fsProxy.setTarget(new SpritesFs(childOpts));\n computerProxy.setTarget(new SpritesComputer(childOpts));\n }\n\n return {\n fs: fsProxy,\n computer: computerProxy,\n sandboxId: () => resolvedName,\n\n init(sandboxId?: string): Promise<void> {\n if (!initPromise) {\n initPromise = doInit(sandboxId).catch((err) => {\n initPromise = null;\n throw err;\n });\n }\n return initPromise;\n },\n\n async dispose(): Promise<void> {\n if (initPromise) {\n await initPromise.catch(() => {});\n }\n if (!autoCreated || !resolvedName) return;\n try {\n const res = await fetch(`${baseURL}/v1/sprites/${resolvedName}`, {\n method: \"DELETE\",\n headers: { Authorization: `Bearer ${opts.token}` },\n });\n if (!res.ok && res.status !== 404) {\n throw new Error(`Sprites dispose failed (${res.status}): ${await res.text()}`);\n }\n } catch {\n // Best-effort cleanup — network errors during dispose are non-fatal\n }\n },\n };\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/virtual/sprites-fs.ts","../src/virtual/sprites-computer.ts","../src/virtual/sprites-sandbox.ts"],"sourcesContent":["import * as path from \"node:path\";\nimport type { VirtualFs, FileEntry, FileStat, ReadOptions } from \"./fs.js\";\n\nexport interface SpritesFsOptions {\n /** sprites.dev API token */\n token: string;\n /** Name of the sprite container */\n spriteName: string;\n /** Base URL for sprites API (default: https://api.sprites.dev) */\n baseURL?: string;\n /** Working directory inside the sprite (default: /home/sprite) */\n workingDir?: string;\n}\n\n/**\n * Sandboxed VirtualFs backed by a remote sprites.dev container. All file\n * operations are executed over the sprites.dev HTTP API — the agent has no\n * access to the host filesystem. This is the recommended VirtualFs for\n * production deployments and untrusted agents. See `LocalFs` for an\n * unsandboxed local alternative.\n */\nexport class SpritesFs implements VirtualFs {\n private token: string;\n private spriteName: string;\n private baseURL: string;\n private workingDir: string;\n\n constructor(opts: SpritesFsOptions) {\n this.token = opts.token;\n this.spriteName = opts.spriteName;\n this.baseURL = (opts.baseURL ?? \"https://api.sprites.dev\").replace(\n /\\/$/,\n \"\",\n );\n this.workingDir = opts.workingDir ?? \"/home/sprite\";\n }\n\n private fsUrl(endpoint: string, params?: Record<string, string>): string {\n const url = new URL(\n `${this.baseURL}/v1/sprites/${this.spriteName}/fs${endpoint}`,\n );\n if (params) {\n for (const [k, v] of Object.entries(params)) {\n url.searchParams.set(k, v);\n }\n }\n return url.toString();\n }\n\n private resolvePath(p: string): string {\n const normalizedBase = this.workingDir.endsWith(\"/\") ? this.workingDir : this.workingDir + \"/\";\n if (p.startsWith(\"/\")) {\n if (p !== this.workingDir && !p.startsWith(normalizedBase)) {\n throw new Error(`Absolute path \"${p}\" is outside working directory \"${this.workingDir}\"`);\n }\n return p;\n }\n const resolved = path.resolve(this.workingDir, p);\n if (resolved !== this.workingDir && !resolved.startsWith(normalizedBase)) {\n throw new Error(`Path \"${p}\" escapes working directory \"${this.workingDir}\"`);\n }\n return resolved;\n }\n\n private headers(): Record<string, string> {\n return {\n Authorization: `Bearer ${this.token}`,\n };\n }\n\n async readFile(filePath: string, _opts?: ReadOptions): Promise<string> {\n const url = this.fsUrl(\"/read\", { path: this.resolvePath(filePath) });\n const res = await fetch(url, { headers: this.headers() });\n if (!res.ok) {\n throw new Error(\n `SpritesFs readFile failed (${res.status}): ${await res.text()}`,\n );\n }\n return res.text();\n }\n\n async readFileBytes(filePath: string, maxBytes?: number): Promise<Buffer> {\n const url = this.fsUrl(\"/read\", {\n path: this.resolvePath(filePath),\n binary: \"true\",\n });\n const res = await fetch(url, { headers: this.headers() });\n if (!res.ok) {\n throw new Error(\n `SpritesFs readFileBytes failed (${res.status}): ${await res.text()}`,\n );\n }\n const arrayBuf = await res.arrayBuffer();\n const buf = Buffer.from(arrayBuf);\n if (maxBytes !== undefined && buf.length > maxBytes) {\n return buf.subarray(0, maxBytes);\n }\n return buf;\n }\n\n async writeFile(filePath: string, content: string): Promise<void> {\n const url = this.fsUrl(\"/write\");\n const res = await fetch(url, {\n method: \"POST\",\n headers: {\n ...this.headers(),\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n path: this.resolvePath(filePath),\n content,\n }),\n });\n if (!res.ok) {\n throw new Error(\n `SpritesFs writeFile failed (${res.status}): ${await res.text()}`,\n );\n }\n }\n\n /**\n * @warning Not atomic. Concurrent appends may lose data due to\n * read-then-write TOCTOU. The Sprites API does not expose an append primitive.\n */\n async appendFile(filePath: string, content: string): Promise<void> {\n let existing = \"\";\n try {\n existing = await this.readFile(filePath);\n } catch {\n // file may not exist yet\n }\n await this.writeFile(filePath, existing + content);\n }\n\n async deleteFile(\n filePath: string,\n opts?: { recursive?: boolean },\n ): Promise<void> {\n const url = this.fsUrl(\"/remove\");\n const res = await fetch(url, {\n method: \"POST\",\n headers: {\n ...this.headers(),\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n path: this.resolvePath(filePath),\n recursive: opts?.recursive ?? false,\n }),\n });\n if (!res.ok) {\n throw new Error(\n `SpritesFs deleteFile failed (${res.status}): ${await res.text()}`,\n );\n }\n }\n\n async mkdir(\n dirPath: string,\n opts?: { recursive?: boolean },\n ): Promise<void> {\n const url = this.fsUrl(\"/mkdir\");\n const res = await fetch(url, {\n method: \"POST\",\n headers: {\n ...this.headers(),\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n path: this.resolvePath(dirPath),\n recursive: opts?.recursive ?? false,\n }),\n });\n if (!res.ok) {\n throw new Error(\n `SpritesFs mkdir failed (${res.status}): ${await res.text()}`,\n );\n }\n }\n\n async readdir(\n dirPath: string,\n _opts?: { recursive?: boolean },\n ): Promise<FileEntry[]> {\n const url = this.fsUrl(\"/readdir\", { path: this.resolvePath(dirPath) });\n const res = await fetch(url, { headers: this.headers() });\n if (!res.ok) {\n throw new Error(\n `SpritesFs readdir failed (${res.status}): ${await res.text()}`,\n );\n }\n const data = (await res.json()) as Array<{\n name: string;\n path: string;\n is_dir: boolean;\n size?: number;\n }>;\n return data.map((entry) => ({\n name: entry.name,\n path: entry.path,\n isDirectory: entry.is_dir,\n isFile: !entry.is_dir,\n size: entry.size,\n }));\n }\n\n async exists(filePath: string): Promise<boolean> {\n try {\n await this.stat(filePath);\n return true;\n } catch {\n return false;\n }\n }\n\n async stat(filePath: string): Promise<FileStat> {\n const url = this.fsUrl(\"/stat\", { path: this.resolvePath(filePath) });\n const res = await fetch(url, { headers: this.headers() });\n if (!res.ok) {\n throw new Error(\n `SpritesFs stat failed (${res.status}): ${await res.text()}`,\n );\n }\n const data = (await res.json()) as {\n size: number;\n is_dir: boolean;\n created_at?: string;\n modified_at?: string;\n };\n return {\n size: data.size,\n isDirectory: data.is_dir,\n isFile: !data.is_dir,\n createdAt: data.created_at ? new Date(data.created_at) : undefined,\n modifiedAt: data.modified_at ? new Date(data.modified_at) : undefined,\n };\n }\n}\n","import type {\n VirtualComputer,\n ExecOptions,\n CommandResult,\n} from \"./computer.js\";\n\nexport interface SpritesComputerOptions {\n /** sprites.dev API token */\n token: string;\n /** Name of the sprite container */\n spriteName: string;\n /** Base URL for sprites API (default: https://api.sprites.dev) */\n baseURL?: string;\n /** Working directory inside the sprite (default: /home/sprite) */\n workingDir?: string;\n}\n\n/**\n * Sandboxed VirtualComputer that executes commands inside a remote\n * sprites.dev container. All shell execution is fully isolated — the agent\n * has no access to the host machine's processes, filesystem, or network.\n *\n * This is the recommended VirtualComputer for production deployments and\n * untrusted agents. See `LocalComputer` for an unsandboxed local alternative.\n *\n * Uses the non-interactive exec REST endpoint (POST command, receive\n * stdout/stderr/exit_code). The WebSocket exec endpoint can be used for\n * streaming/TTY use cases, but REST is sufficient for tool calls.\n */\nexport class SpritesComputer implements VirtualComputer {\n private token: string;\n private spriteName: string;\n private baseURL: string;\n private workingDir: string;\n\n constructor(opts: SpritesComputerOptions) {\n this.token = opts.token;\n this.spriteName = opts.spriteName;\n this.baseURL = (opts.baseURL ?? \"https://api.sprites.dev\").replace(\n /\\/$/,\n \"\",\n );\n this.workingDir = opts.workingDir ?? \"/home/sprite\";\n }\n\n private headers(): Record<string, string> {\n return {\n Authorization: `Bearer ${this.token}`,\n \"Content-Type\": \"application/json\",\n };\n }\n\n async executeCommand(\n command: string,\n opts?: ExecOptions,\n ): Promise<CommandResult> {\n const cwd = opts?.cwd ?? this.workingDir;\n const wrappedCommand = `cd ${this.shellEscape(cwd)} && ${command}`;\n\n const url = `${this.baseURL}/v1/sprites/${this.spriteName}/exec`;\n\n const res = await fetch(url, {\n method: \"POST\",\n headers: this.headers(),\n body: JSON.stringify({\n command: [\"bash\", \"-c\", wrappedCommand],\n timeout: opts?.timeout ?? 30_000,\n env: opts?.env,\n }),\n });\n\n if (!res.ok) {\n const text = await res.text();\n return {\n exitCode: 1,\n stdout: \"\",\n stderr: `Sprites exec failed (${res.status}): ${text}`,\n };\n }\n\n const data = (await res.json()) as {\n exit_code: number;\n stdout: string;\n stderr: string;\n };\n\n return {\n exitCode: data.exit_code,\n stdout: data.stdout ?? \"\",\n stderr: data.stderr ?? \"\",\n };\n }\n\n private shellEscape(s: string): string {\n return `'${s.replace(/'/g, \"'\\\\''\")}'`;\n }\n}\n","import type { Sandbox } from \"./sandbox.js\";\nimport { SpritesFs } from \"./sprites-fs.js\";\nimport { SpritesComputer } from \"./sprites-computer.js\";\nimport { createFsProxy, createComputerProxy } from \"./proxy.js\";\n\nexport interface SpritesSandboxOptions {\n /** sprites.dev API token. */\n token: string;\n /**\n * Name of an existing sprite container. When provided the sandbox\n * attaches to this sprite directly — no auto-creation occurs and\n * `dispose()` will **not** delete it (lifecycle is yours to manage).\n *\n * When omitted a new sprite is provisioned on the first `init()` call\n * (via `POST /v1/sprites`). The auto-created sprite is deleted when\n * `dispose()` is called, and its name is available via `sandboxId()`\n * for session persistence.\n */\n spriteName?: string;\n /** Base URL for sprites API (default: https://api.sprites.dev). */\n baseURL?: string;\n /** Working directory inside the sprite (default: /home/sprite). */\n workingDir?: string;\n /**\n * Optional prefix for auto-generated sprite names (default: \"noumen-\").\n * Only used when `spriteName` is omitted.\n */\n namePrefix?: string;\n}\n\n/**\n * Create a `Sandbox` backed by a remote sprites.dev container.\n * Full isolation — the agent has no access to the host machine.\n *\n * **Auto-creation:** When `spriteName` is omitted the sandbox is created\n * lazily on the first `init()` call via the Sprites REST API. The sprite\n * name is available through `sandboxId()` so callers can persist it in\n * session metadata for reconnection on resume. Pass the stored name back\n * through `init(storedId)` to reattach instead of creating a new sprite.\n *\n * **Explicit ID:** When `spriteName` is provided the sandbox attaches to\n * that sprite immediately on `init()`. `dispose()` is a no-op in this\n * case — the caller owns the sprite's lifecycle.\n *\n * @example\n * ```ts\n * // Auto-create — sprite provisioned on first init()\n * const sandbox = SpritesSandbox({ token: process.env.SPRITES_TOKEN! });\n *\n * // Explicit — attach to pre-existing sprite, no auto-lifecycle\n * const sandbox = SpritesSandbox({\n * token: process.env.SPRITES_TOKEN!,\n * spriteName: \"my-sprite\",\n * });\n * ```\n */\nexport function SpritesSandbox(opts: SpritesSandboxOptions): Sandbox {\n const baseURL = (opts.baseURL ?? \"https://api.sprites.dev\").replace(/\\/$/, \"\");\n const userProvidedName = opts.spriteName;\n\n if (userProvidedName) {\n const fsOpts = { ...opts, spriteName: userProvidedName };\n return {\n fs: new SpritesFs(fsOpts),\n computer: new SpritesComputer(fsOpts),\n sandboxId: () => userProvidedName,\n };\n }\n\n const fsProxy = createFsProxy();\n const computerProxy = createComputerProxy();\n let resolvedName: string | undefined;\n let autoCreated = false;\n let initPromise: Promise<void> | null = null;\n\n async function doInit(reconnectId?: string): Promise<void> {\n let name = reconnectId ?? `${opts.namePrefix ?? \"noumen-\"}${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;\n let needsCreate = !reconnectId;\n\n if (reconnectId) {\n const check = await fetch(`${baseURL}/v1/sprites/${reconnectId}`, {\n method: \"GET\",\n headers: { Authorization: `Bearer ${opts.token}` },\n });\n if (!check.ok) {\n name = `${opts.namePrefix ?? \"noumen-\"}${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;\n needsCreate = true;\n }\n }\n\n if (needsCreate) {\n const res = await fetch(`${baseURL}/v1/sprites`, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${opts.token}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({ name }),\n });\n if (!res.ok) {\n throw new Error(`Sprites auto-create failed (${res.status}): ${await res.text()}`);\n }\n autoCreated = true;\n }\n\n resolvedName = name;\n const childOpts = { ...opts, spriteName: name };\n fsProxy.setTarget(new SpritesFs(childOpts));\n computerProxy.setTarget(new SpritesComputer(childOpts));\n }\n\n return {\n fs: fsProxy,\n computer: computerProxy,\n sandboxId: () => resolvedName,\n\n init(sandboxId?: string): Promise<void> {\n if (!initPromise) {\n initPromise = doInit(sandboxId).catch((err) => {\n initPromise = null;\n throw err;\n });\n }\n return initPromise;\n },\n\n async dispose(): Promise<void> {\n if (initPromise) {\n await initPromise.catch(() => {});\n }\n if (!autoCreated || !resolvedName) return;\n try {\n const res = await fetch(`${baseURL}/v1/sprites/${resolvedName}`, {\n method: \"DELETE\",\n headers: { Authorization: `Bearer ${opts.token}` },\n });\n if (!res.ok && res.status !== 404) {\n throw new Error(`Sprites dispose failed (${res.status}): ${await res.text()}`);\n }\n } catch {\n // Best-effort cleanup — network errors during dispose are non-fatal\n }\n },\n };\n}\n"],"mappings":"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","names":[]}
|
package/dist/ssh.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { S as Sandbox } from './sandbox-
|
|
2
|
-
import {
|
|
1
|
+
import { S as Sandbox } from './sandbox-DAqQo0Tj.js';
|
|
2
|
+
import { a as VirtualComputer, E as ExecOptions, C as CommandResult, V as VirtualFs, R as ReadOptions, F as FileEntry, b as FileStat } from './computer-DzMR92tK.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Minimal subset of the ssh2 Channel interface used by SshComputer / SshFs.
|
package/dist/ssh.js
CHANGED
package/dist/ssh.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/virtual/ssh-fs.ts","../src/virtual/ssh-computer.ts","../src/virtual/ssh-sandbox.ts"],"sourcesContent":["import * as pathMod from \"node:path\";\nimport type { VirtualFs, FileEntry, FileStat, ReadOptions } from \"./fs.js\";\nimport type { SshClient, SshSftpSession } from \"./ssh-computer.js\";\n\nexport interface SshFsOptions {\n /** A connected ssh2 Client instance. */\n client: SshClient;\n /** Working directory for relative path resolution (default: /). */\n workingDir?: string;\n}\n\n/**\n * VirtualFs backed by SFTP file operations over SSH.\n *\n * Uses the ssh2 Client's SFTP subsystem for all file I/O. The SFTP session\n * is opened lazily on the first operation and reused for the lifetime of\n * the SshFs instance.\n *\n * Requires `ssh2` as an optional peer dependency.\n * The caller is responsible for the Client lifecycle.\n */\nexport class SshFs implements VirtualFs {\n private client: SshClient;\n private workingDir: string;\n private sftpSession: SshSftpSession | null = null;\n private sftpPromise: Promise<SshSftpSession> | null = null;\n\n constructor(opts: SshFsOptions) {\n this.client = opts.client;\n this.workingDir = opts.workingDir ?? \"/\";\n }\n\n private getSftp(): Promise<SshSftpSession> {\n if (this.sftpSession) return Promise.resolve(this.sftpSession);\n if (this.sftpPromise) return this.sftpPromise;\n this.sftpPromise = new Promise<SshSftpSession>((resolve, reject) => {\n this.client.sftp((err, sftp) => {\n if (err) {\n this.sftpPromise = null;\n reject(err);\n } else {\n this.sftpSession = sftp;\n resolve(sftp);\n }\n });\n });\n return this.sftpPromise;\n }\n\n private resolvePath(p: string): string {\n if (p.includes(\"\\0\")) {\n throw new Error(\"Path contains null bytes\");\n }\n const normalizedBase = this.workingDir.endsWith(\"/\")\n ? this.workingDir\n : this.workingDir + \"/\";\n if (p.startsWith(\"/\")) {\n const normalized = pathMod.normalize(p);\n if (\n normalized !== this.workingDir &&\n !normalized.startsWith(normalizedBase)\n ) {\n throw new Error(\n `Absolute path \"${p}\" is outside working directory \"${this.workingDir}\"`,\n );\n }\n return normalized;\n }\n const resolved = pathMod.resolve(this.workingDir, p);\n if (\n resolved !== this.workingDir &&\n !resolved.startsWith(normalizedBase)\n ) {\n throw new Error(\n `Path \"${p}\" escapes working directory \"${this.workingDir}\"`,\n );\n }\n return resolved;\n }\n\n async readFile(path: string, _opts?: ReadOptions): Promise<string> {\n const resolved = this.resolvePath(path);\n const sftp = await this.getSftp();\n return new Promise<string>((resolve, reject) => {\n sftp.readFile(resolved, { encoding: \"utf8\" }, (err, data) => {\n if (err) reject(new Error(`SshFs readFile failed: ${err.message}`));\n else resolve(data as string);\n });\n });\n }\n\n async readFileBytes(path: string, maxBytes?: number): Promise<Buffer> {\n const resolved = this.resolvePath(path);\n const sftp = await this.getSftp();\n\n if (maxBytes === undefined) {\n return new Promise<Buffer>((resolve, reject) => {\n sftp.readFile(resolved, (err, data) => {\n if (err) reject(new Error(`SshFs readFileBytes failed: ${err.message}`));\n else resolve(data);\n });\n });\n }\n\n return new Promise<Buffer>((resolve, reject) => {\n sftp.open(resolved, \"r\", (err, handle) => {\n if (err) {\n reject(new Error(`SshFs readFileBytes failed: ${err.message}`));\n return;\n }\n const buf = Buffer.alloc(maxBytes);\n sftp.read(handle, buf, 0, maxBytes, 0, (readErr, bytesRead, readBuf) => {\n sftp.close(handle, () => {});\n if (readErr) {\n reject(new Error(`SshFs readFileBytes failed: ${readErr.message}`));\n } else {\n resolve(readBuf.subarray(0, bytesRead));\n }\n });\n });\n });\n }\n\n async writeFile(path: string, content: string): Promise<void> {\n const resolved = this.resolvePath(path);\n const sftp = await this.getSftp();\n const dir = pathMod.dirname(resolved);\n await this.mkdirRecursive(sftp, dir);\n return new Promise<void>((resolve, reject) => {\n sftp.writeFile(resolved, content, (err) => {\n if (err) reject(new Error(`SshFs writeFile failed: ${err.message}`));\n else resolve();\n });\n });\n }\n\n async appendFile(path: string, content: string): Promise<void> {\n const resolved = this.resolvePath(path);\n const sftp = await this.getSftp();\n const dir = pathMod.dirname(resolved);\n await this.mkdirRecursive(sftp, dir);\n return new Promise<void>((resolve, reject) => {\n sftp.appendFile(resolved, content, (err) => {\n if (err) reject(new Error(`SshFs appendFile failed: ${err.message}`));\n else resolve();\n });\n });\n }\n\n async deleteFile(\n path: string,\n opts?: { recursive?: boolean },\n ): Promise<void> {\n const resolved = this.resolvePath(path);\n if (opts?.recursive) {\n const sftp = await this.getSftp();\n await this.rmRecursive(sftp, resolved);\n return;\n }\n const sftp = await this.getSftp();\n return new Promise<void>((resolve, reject) => {\n sftp.unlink(resolved, (err) => {\n if (err) reject(new Error(`SshFs deleteFile failed: ${err.message}`));\n else resolve();\n });\n });\n }\n\n async mkdir(path: string, opts?: { recursive?: boolean }): Promise<void> {\n const resolved = this.resolvePath(path);\n const sftp = await this.getSftp();\n if (opts?.recursive) {\n await this.mkdirRecursive(sftp, resolved);\n return;\n }\n return new Promise<void>((resolve, reject) => {\n sftp.mkdir(resolved, (err) => {\n if (err) reject(new Error(`SshFs mkdir failed: ${err.message}`));\n else resolve();\n });\n });\n }\n\n async readdir(\n path: string,\n _opts?: { recursive?: boolean },\n ): Promise<FileEntry[]> {\n const resolved = this.resolvePath(path);\n const sftp = await this.getSftp();\n return new Promise<FileEntry[]>((resolve, reject) => {\n sftp.readdir(resolved, (err, list) => {\n if (err) {\n reject(new Error(`SshFs readdir failed: ${err.message}`));\n return;\n }\n const entries: FileEntry[] = list.map((item) => ({\n name: item.filename,\n path: pathMod.join(resolved, item.filename),\n isDirectory: item.attrs.isDirectory(),\n isFile: item.attrs.isFile(),\n }));\n resolve(entries);\n });\n });\n }\n\n async exists(path: string): Promise<boolean> {\n const resolved = this.resolvePath(path);\n const sftp = await this.getSftp();\n return new Promise<boolean>((resolve) => {\n sftp.stat(resolved, (err) => {\n resolve(!err);\n });\n });\n }\n\n async stat(path: string): Promise<FileStat> {\n const resolved = this.resolvePath(path);\n const sftp = await this.getSftp();\n return new Promise<FileStat>((resolve, reject) => {\n sftp.stat(resolved, (err, stats) => {\n if (err) {\n reject(new Error(`SshFs stat failed: ${err.message}`));\n return;\n }\n resolve({\n size: stats.size,\n isDirectory: stats.isDirectory(),\n isFile: stats.isFile(),\n modifiedAt: stats.mtime > 0 ? new Date(stats.mtime * 1000) : undefined,\n });\n });\n });\n }\n\n private async mkdirRecursive(\n sftp: SshSftpSession,\n dir: string,\n ): Promise<void> {\n const parts = dir.split(\"/\").filter(Boolean);\n let current = \"/\";\n for (const part of parts) {\n current = pathMod.join(current, part);\n await new Promise<void>((resolve) => {\n sftp.mkdir(current, () => {\n resolve();\n });\n });\n }\n }\n\n private async rmRecursive(\n sftp: SshSftpSession,\n target: string,\n ): Promise<void> {\n const isDir = await new Promise<boolean>((resolve) => {\n sftp.stat(target, (err, stats) => {\n if (err) resolve(false);\n else resolve(stats.isDirectory());\n });\n });\n\n if (!isDir) {\n return new Promise<void>((resolve, reject) => {\n sftp.unlink(target, (err) => {\n if (err) reject(new Error(`SshFs deleteFile failed: ${err.message}`));\n else resolve();\n });\n });\n }\n\n const entries = await new Promise<Array<{ filename: string; attrs: { isDirectory(): boolean } }>>(\n (resolve, reject) => {\n sftp.readdir(target, (err, list) => {\n if (err) reject(new Error(`SshFs deleteFile failed: ${err.message}`));\n else resolve(list);\n });\n },\n );\n\n for (const entry of entries) {\n await this.rmRecursive(sftp, pathMod.join(target, entry.filename));\n }\n\n return new Promise<void>((resolve, reject) => {\n sftp.rmdir(target, (err) => {\n if (err) reject(new Error(`SshFs deleteFile failed: ${err.message}`));\n else resolve();\n });\n });\n }\n}\n","import type { VirtualComputer, ExecOptions, CommandResult } from \"./computer.js\";\n\n/**\n * Minimal subset of the ssh2 Channel interface used by SshComputer / SshFs.\n * Avoids a hard import of ssh2 at the module level.\n */\nexport interface SshChannel {\n on(event: string, listener: (...args: any[]) => void): SshChannel;\n stderr: { on(event: string, listener: (...args: any[]) => void): unknown };\n destroy?(): void;\n}\n\n/**\n * Minimal subset of the ssh2 SFTPWrapper interface used by SshFs.\n */\nexport interface SshSftpSession {\n readFile(\n path: string,\n callback: (err: Error | undefined, data: Buffer) => void,\n ): void;\n readFile(\n path: string,\n opts: { encoding: string },\n callback: (err: Error | undefined, data: string) => void,\n ): void;\n writeFile(\n path: string,\n data: string | Buffer,\n callback: (err: Error | undefined) => void,\n ): void;\n appendFile(\n path: string,\n data: string | Buffer,\n callback: (err: Error | undefined) => void,\n ): void;\n unlink(path: string, callback: (err: Error | undefined) => void): void;\n rmdir(path: string, callback: (err: Error | undefined) => void): void;\n mkdir(path: string, callback: (err: Error | undefined) => void): void;\n readdir(\n path: string,\n callback: (\n err: Error | undefined,\n list: Array<{\n filename: string;\n longname: string;\n attrs: { size: number; isDirectory(): boolean; isFile(): boolean; mtime: number; atime: number };\n }>,\n ) => void,\n ): void;\n stat(\n path: string,\n callback: (\n err: Error | undefined,\n stats: { size: number; isDirectory(): boolean; isFile(): boolean; mtime: number; atime: number },\n ) => void,\n ): void;\n open(\n path: string,\n flags: string,\n callback: (err: Error | undefined, handle: Buffer) => void,\n ): void;\n read(\n handle: Buffer,\n buffer: Buffer,\n offset: number,\n length: number,\n position: number,\n callback: (err: Error | undefined, bytesRead: number, buf: Buffer) => void,\n ): void;\n close(handle: Buffer, callback: (err: Error | undefined) => void): void;\n end(): void;\n}\n\n/**\n * Minimal subset of the ssh2 Client interface used by SshComputer and SshFs.\n * Avoids a hard import of ssh2 at the module level.\n */\nexport interface SshClient {\n exec(\n command: string,\n callback: (err: Error | undefined, channel: SshChannel) => void,\n ): void;\n exec(\n command: string,\n opts: Record<string, unknown>,\n callback: (err: Error | undefined, channel: SshChannel) => void,\n ): void;\n sftp(\n callback: (err: Error | undefined, sftp: SshSftpSession) => void,\n ): void;\n end(): void;\n on(event: string, listener: (...args: any[]) => void): this;\n}\n\nexport interface SshComputerOptions {\n /** A connected ssh2 Client instance. */\n client: SshClient;\n /** Default working directory for commands (default: /). */\n defaultCwd?: string;\n /** Default timeout in ms for commands (default: 30000). */\n defaultTimeout?: number;\n}\n\n/**\n * VirtualComputer backed by command execution over SSH.\n *\n * Requires `ssh2` as an optional peer dependency.\n * The caller is responsible for the Client lifecycle (connect, end).\n */\nexport class SshComputer implements VirtualComputer {\n private client: SshClient;\n private defaultCwd: string;\n private defaultTimeout: number;\n\n constructor(opts: SshComputerOptions) {\n this.client = opts.client;\n this.defaultCwd = opts.defaultCwd ?? \"/\";\n this.defaultTimeout = opts.defaultTimeout ?? 30_000;\n }\n\n async executeCommand(\n command: string,\n opts?: ExecOptions,\n ): Promise<CommandResult> {\n const cwd = opts?.cwd ?? this.defaultCwd;\n const timeout = opts?.timeout ?? this.defaultTimeout;\n\n let envPrefix = \"\";\n if (opts?.env) {\n envPrefix = Object.entries(opts.env)\n .map(([k, v]) => `${k}=${shellEscape(v)}`)\n .join(\" \") + \" \";\n }\n\n const fullCommand = `cd ${shellEscape(cwd)} && ${envPrefix}${command}`;\n\n return new Promise<CommandResult>((resolve, reject) => {\n this.client.exec(fullCommand, (err, channel) => {\n if (err) {\n reject(err);\n return;\n }\n\n const stdoutBufs: Buffer[] = [];\n const stderrBufs: Buffer[] = [];\n\n const timer = setTimeout(() => {\n channel.destroy?.();\n resolve({\n exitCode: 1,\n stdout: Buffer.concat(stdoutBufs).toString(\"utf-8\"),\n stderr:\n Buffer.concat(stderrBufs).toString(\"utf-8\") +\n \"\\n[timeout after \" + timeout + \"ms]\",\n });\n }, timeout);\n\n channel.on(\"data\", (chunk: Buffer) => {\n stdoutBufs.push(Buffer.from(chunk));\n });\n\n channel.stderr.on(\"data\", (chunk: Buffer) => {\n stderrBufs.push(Buffer.from(chunk));\n });\n\n channel.on(\"close\", (code: number) => {\n clearTimeout(timer);\n resolve({\n exitCode: code ?? 0,\n stdout: Buffer.concat(stdoutBufs).toString(\"utf-8\"),\n stderr: Buffer.concat(stderrBufs).toString(\"utf-8\"),\n });\n });\n\n channel.on(\"error\", (channelErr: Error) => {\n clearTimeout(timer);\n reject(channelErr);\n });\n });\n });\n }\n}\n\nfunction shellEscape(s: string): string {\n return `'${s.replace(/'/g, \"'\\\\''\")}'`;\n}\n","import type { Sandbox } from \"./sandbox.js\";\nimport { SshFs } from \"./ssh-fs.js\";\nimport { SshComputer, type SshClient } from \"./ssh-computer.js\";\nimport { createFsProxy, createComputerProxy } from \"./proxy.js\";\n\nexport interface SshSandboxOptions {\n /**\n * A pre-connected ssh2 Client instance. When provided the sandbox uses\n * this client directly — no auto-connect occurs and `dispose()` will\n * **not** call `client.end()`.\n *\n * When omitted, a new ssh2 Client is created and connected on the first\n * `init()` call using `host`, `port`, `username`, and the provided\n * credentials. The client is ended when `dispose()` is called.\n */\n client?: SshClient;\n /**\n * SSH hostname. Required when `client` is omitted; ignored when `client`\n * is provided.\n */\n host?: string;\n /** SSH port (default: 22). Only used during auto-connect. */\n port?: number;\n /** SSH username (default: \"root\"). Only used during auto-connect. */\n username?: string;\n /** Password for password-based authentication. Only used during auto-connect. */\n password?: string;\n /** Private key for key-based authentication (PEM string or Buffer). Only used during auto-connect. */\n privateKey?: string | Buffer;\n /** Passphrase for an encrypted private key. Only used during auto-connect. */\n passphrase?: string;\n /** Working directory on the remote host. */\n cwd?: string;\n /** Default timeout (ms) for shell commands. */\n defaultTimeout?: number;\n}\n\n/**\n * Create a `Sandbox` backed by a remote host over SSH.\n * Requires `ssh2` as an optional peer dependency.\n *\n * **Auto-connect:** When `client` is omitted and `host` is provided,\n * an ssh2 Client is created and connected lazily on the first `init()`\n * call. The connection identifier (`host:port`) is available through\n * `sandboxId()` for session persistence.\n *\n * **Explicit client:** When `client` is provided, `init()` binds it\n * immediately. `dispose()` is a no-op — the caller owns the client's\n * lifecycle.\n *\n * @example\n * ```ts\n * // Auto-connect with private key\n * const sandbox = SshSandbox({\n * host: \"dev.example.com\",\n * username: \"deploy\",\n * privateKey: fs.readFileSync(\"~/.ssh/id_ed25519\"),\n * cwd: \"/home/deploy/project\",\n * });\n *\n * // Explicit client (lifecycle managed externally)\n * const sandbox = SshSandbox({ client: myConnectedClient, cwd: \"/workspace\" });\n * ```\n */\nexport function SshSandbox(opts: SshSandboxOptions): Sandbox {\n if (opts.client) {\n const c = opts.client;\n return {\n fs: new SshFs({ client: c, workingDir: opts.cwd }),\n computer: new SshComputer({\n client: c,\n defaultCwd: opts.cwd,\n defaultTimeout: opts.defaultTimeout,\n }),\n sandboxId: () =>\n opts.host ? `${opts.host}:${opts.port ?? 22}` : undefined,\n };\n }\n\n if (!opts.host) {\n throw new Error(\"SshSandbox requires either `client` or `host`\");\n }\n\n const fsProxy = createFsProxy();\n const computerProxy = createComputerProxy();\n const identifier = `${opts.host}:${opts.port ?? 22}`;\n let clientRef: SshClient | null = null;\n let autoCreated = false;\n let initPromise: Promise<void> | null = null;\n\n async function doInit(): Promise<void> {\n const modName = \"ssh2\";\n const ssh2 = await import(/* webpackIgnore: true */ modName);\n const ClientClass =\n (ssh2 as any).Client ?? (ssh2 as any).default?.Client;\n if (!ClientClass) {\n throw new Error(\n \"Could not resolve Client class from 'ssh2' package\",\n );\n }\n\n const client: SshClient = new ClientClass();\n await new Promise<void>((resolve, reject) => {\n client.on(\"ready\", () => resolve());\n client.on(\"error\", (err: Error) => reject(err));\n (client as any).connect({\n host: opts.host,\n port: opts.port ?? 22,\n username: opts.username ?? \"root\",\n password: opts.password,\n privateKey: opts.privateKey,\n passphrase: opts.passphrase,\n });\n });\n\n clientRef = client;\n autoCreated = true;\n fsProxy.setTarget(new SshFs({ client, workingDir: opts.cwd }));\n computerProxy.setTarget(\n new SshComputer({\n client,\n defaultCwd: opts.cwd,\n defaultTimeout: opts.defaultTimeout,\n }),\n );\n }\n\n return {\n fs: fsProxy,\n computer: computerProxy,\n sandboxId: () => identifier,\n\n init(): Promise<void> {\n if (!initPromise) {\n initPromise = doInit().catch((err) => {\n initPromise = null;\n throw err;\n });\n }\n return initPromise;\n },\n\n async dispose(): Promise<void> {\n if (initPromise) {\n await initPromise.catch(() => {});\n }\n if (!autoCreated || !clientRef) return;\n try {\n clientRef.end();\n } catch {\n /* best-effort */\n }\n },\n };\n}\n"],"mappings":"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","names":["resolve","sftp","resolve","resolve"]}
|
|
1
|
+
{"version":3,"sources":["../src/virtual/ssh-fs.ts","../src/virtual/ssh-computer.ts","../src/virtual/ssh-sandbox.ts"],"sourcesContent":["import * as pathMod from \"node:path\";\nimport type { VirtualFs, FileEntry, FileStat, ReadOptions } from \"./fs.js\";\nimport type { SshClient, SshSftpSession } from \"./ssh-computer.js\";\n\nexport interface SshFsOptions {\n /** A connected ssh2 Client instance. */\n client: SshClient;\n /** Working directory for relative path resolution (default: /). */\n workingDir?: string;\n}\n\n/**\n * VirtualFs backed by SFTP file operations over SSH.\n *\n * Uses the ssh2 Client's SFTP subsystem for all file I/O. The SFTP session\n * is opened lazily on the first operation and reused for the lifetime of\n * the SshFs instance.\n *\n * Requires `ssh2` as an optional peer dependency.\n * The caller is responsible for the Client lifecycle.\n */\nexport class SshFs implements VirtualFs {\n private client: SshClient;\n private workingDir: string;\n private sftpSession: SshSftpSession | null = null;\n private sftpPromise: Promise<SshSftpSession> | null = null;\n\n constructor(opts: SshFsOptions) {\n this.client = opts.client;\n this.workingDir = opts.workingDir ?? \"/\";\n }\n\n private getSftp(): Promise<SshSftpSession> {\n if (this.sftpSession) return Promise.resolve(this.sftpSession);\n if (this.sftpPromise) return this.sftpPromise;\n this.sftpPromise = new Promise<SshSftpSession>((resolve, reject) => {\n this.client.sftp((err, sftp) => {\n if (err) {\n this.sftpPromise = null;\n reject(err);\n } else {\n this.sftpSession = sftp;\n resolve(sftp);\n }\n });\n });\n return this.sftpPromise;\n }\n\n private resolvePath(p: string): string {\n if (p.includes(\"\\0\")) {\n throw new Error(\"Path contains null bytes\");\n }\n const normalizedBase = this.workingDir.endsWith(\"/\")\n ? this.workingDir\n : this.workingDir + \"/\";\n if (p.startsWith(\"/\")) {\n const normalized = pathMod.normalize(p);\n if (\n normalized !== this.workingDir &&\n !normalized.startsWith(normalizedBase)\n ) {\n throw new Error(\n `Absolute path \"${p}\" is outside working directory \"${this.workingDir}\"`,\n );\n }\n return normalized;\n }\n const resolved = pathMod.resolve(this.workingDir, p);\n if (\n resolved !== this.workingDir &&\n !resolved.startsWith(normalizedBase)\n ) {\n throw new Error(\n `Path \"${p}\" escapes working directory \"${this.workingDir}\"`,\n );\n }\n return resolved;\n }\n\n async readFile(path: string, _opts?: ReadOptions): Promise<string> {\n const resolved = this.resolvePath(path);\n const sftp = await this.getSftp();\n return new Promise<string>((resolve, reject) => {\n sftp.readFile(resolved, { encoding: \"utf8\" }, (err, data) => {\n if (err) reject(new Error(`SshFs readFile failed: ${err.message}`));\n else resolve(data as string);\n });\n });\n }\n\n async readFileBytes(path: string, maxBytes?: number): Promise<Buffer> {\n const resolved = this.resolvePath(path);\n const sftp = await this.getSftp();\n\n if (maxBytes === undefined) {\n return new Promise<Buffer>((resolve, reject) => {\n sftp.readFile(resolved, (err, data) => {\n if (err) reject(new Error(`SshFs readFileBytes failed: ${err.message}`));\n else resolve(data);\n });\n });\n }\n\n return new Promise<Buffer>((resolve, reject) => {\n sftp.open(resolved, \"r\", (err, handle) => {\n if (err) {\n reject(new Error(`SshFs readFileBytes failed: ${err.message}`));\n return;\n }\n const buf = Buffer.alloc(maxBytes);\n sftp.read(handle, buf, 0, maxBytes, 0, (readErr, bytesRead, readBuf) => {\n sftp.close(handle, () => {});\n if (readErr) {\n reject(new Error(`SshFs readFileBytes failed: ${readErr.message}`));\n } else {\n resolve(readBuf.subarray(0, bytesRead));\n }\n });\n });\n });\n }\n\n async writeFile(path: string, content: string): Promise<void> {\n const resolved = this.resolvePath(path);\n const sftp = await this.getSftp();\n const dir = pathMod.dirname(resolved);\n await this.mkdirRecursive(sftp, dir);\n return new Promise<void>((resolve, reject) => {\n sftp.writeFile(resolved, content, (err) => {\n if (err) reject(new Error(`SshFs writeFile failed: ${err.message}`));\n else resolve();\n });\n });\n }\n\n async appendFile(path: string, content: string): Promise<void> {\n const resolved = this.resolvePath(path);\n const sftp = await this.getSftp();\n const dir = pathMod.dirname(resolved);\n await this.mkdirRecursive(sftp, dir);\n return new Promise<void>((resolve, reject) => {\n sftp.appendFile(resolved, content, (err) => {\n if (err) reject(new Error(`SshFs appendFile failed: ${err.message}`));\n else resolve();\n });\n });\n }\n\n async deleteFile(\n path: string,\n opts?: { recursive?: boolean },\n ): Promise<void> {\n const resolved = this.resolvePath(path);\n if (opts?.recursive) {\n const sftp = await this.getSftp();\n await this.rmRecursive(sftp, resolved);\n return;\n }\n const sftp = await this.getSftp();\n return new Promise<void>((resolve, reject) => {\n sftp.unlink(resolved, (err) => {\n if (err) reject(new Error(`SshFs deleteFile failed: ${err.message}`));\n else resolve();\n });\n });\n }\n\n async mkdir(path: string, opts?: { recursive?: boolean }): Promise<void> {\n const resolved = this.resolvePath(path);\n const sftp = await this.getSftp();\n if (opts?.recursive) {\n await this.mkdirRecursive(sftp, resolved);\n return;\n }\n return new Promise<void>((resolve, reject) => {\n sftp.mkdir(resolved, (err) => {\n if (err) reject(new Error(`SshFs mkdir failed: ${err.message}`));\n else resolve();\n });\n });\n }\n\n async readdir(\n path: string,\n _opts?: { recursive?: boolean },\n ): Promise<FileEntry[]> {\n const resolved = this.resolvePath(path);\n const sftp = await this.getSftp();\n return new Promise<FileEntry[]>((resolve, reject) => {\n sftp.readdir(resolved, (err, list) => {\n if (err) {\n reject(new Error(`SshFs readdir failed: ${err.message}`));\n return;\n }\n const entries: FileEntry[] = list.map((item) => ({\n name: item.filename,\n path: pathMod.join(resolved, item.filename),\n isDirectory: item.attrs.isDirectory(),\n isFile: item.attrs.isFile(),\n }));\n resolve(entries);\n });\n });\n }\n\n async exists(path: string): Promise<boolean> {\n const resolved = this.resolvePath(path);\n const sftp = await this.getSftp();\n return new Promise<boolean>((resolve) => {\n sftp.stat(resolved, (err) => {\n resolve(!err);\n });\n });\n }\n\n async stat(path: string): Promise<FileStat> {\n const resolved = this.resolvePath(path);\n const sftp = await this.getSftp();\n return new Promise<FileStat>((resolve, reject) => {\n sftp.stat(resolved, (err, stats) => {\n if (err) {\n reject(new Error(`SshFs stat failed: ${err.message}`));\n return;\n }\n resolve({\n size: stats.size,\n isDirectory: stats.isDirectory(),\n isFile: stats.isFile(),\n modifiedAt: stats.mtime > 0 ? new Date(stats.mtime * 1000) : undefined,\n });\n });\n });\n }\n\n private async mkdirRecursive(\n sftp: SshSftpSession,\n dir: string,\n ): Promise<void> {\n const parts = dir.split(\"/\").filter(Boolean);\n let current = \"/\";\n for (const part of parts) {\n current = pathMod.join(current, part);\n await new Promise<void>((resolve) => {\n sftp.mkdir(current, () => {\n resolve();\n });\n });\n }\n }\n\n private async rmRecursive(\n sftp: SshSftpSession,\n target: string,\n ): Promise<void> {\n const isDir = await new Promise<boolean>((resolve) => {\n sftp.stat(target, (err, stats) => {\n if (err) resolve(false);\n else resolve(stats.isDirectory());\n });\n });\n\n if (!isDir) {\n return new Promise<void>((resolve, reject) => {\n sftp.unlink(target, (err) => {\n if (err) reject(new Error(`SshFs deleteFile failed: ${err.message}`));\n else resolve();\n });\n });\n }\n\n const entries = await new Promise<Array<{ filename: string; attrs: { isDirectory(): boolean } }>>(\n (resolve, reject) => {\n sftp.readdir(target, (err, list) => {\n if (err) reject(new Error(`SshFs deleteFile failed: ${err.message}`));\n else resolve(list);\n });\n },\n );\n\n for (const entry of entries) {\n await this.rmRecursive(sftp, pathMod.join(target, entry.filename));\n }\n\n return new Promise<void>((resolve, reject) => {\n sftp.rmdir(target, (err) => {\n if (err) reject(new Error(`SshFs deleteFile failed: ${err.message}`));\n else resolve();\n });\n });\n }\n}\n","import type { VirtualComputer, ExecOptions, CommandResult } from \"./computer.js\";\n\n/**\n * Minimal subset of the ssh2 Channel interface used by SshComputer / SshFs.\n * Avoids a hard import of ssh2 at the module level.\n */\nexport interface SshChannel {\n on(event: string, listener: (...args: any[]) => void): SshChannel;\n stderr: { on(event: string, listener: (...args: any[]) => void): unknown };\n destroy?(): void;\n}\n\n/**\n * Minimal subset of the ssh2 SFTPWrapper interface used by SshFs.\n */\nexport interface SshSftpSession {\n readFile(\n path: string,\n callback: (err: Error | undefined, data: Buffer) => void,\n ): void;\n readFile(\n path: string,\n opts: { encoding: string },\n callback: (err: Error | undefined, data: string) => void,\n ): void;\n writeFile(\n path: string,\n data: string | Buffer,\n callback: (err: Error | undefined) => void,\n ): void;\n appendFile(\n path: string,\n data: string | Buffer,\n callback: (err: Error | undefined) => void,\n ): void;\n unlink(path: string, callback: (err: Error | undefined) => void): void;\n rmdir(path: string, callback: (err: Error | undefined) => void): void;\n mkdir(path: string, callback: (err: Error | undefined) => void): void;\n readdir(\n path: string,\n callback: (\n err: Error | undefined,\n list: Array<{\n filename: string;\n longname: string;\n attrs: { size: number; isDirectory(): boolean; isFile(): boolean; mtime: number; atime: number };\n }>,\n ) => void,\n ): void;\n stat(\n path: string,\n callback: (\n err: Error | undefined,\n stats: { size: number; isDirectory(): boolean; isFile(): boolean; mtime: number; atime: number },\n ) => void,\n ): void;\n open(\n path: string,\n flags: string,\n callback: (err: Error | undefined, handle: Buffer) => void,\n ): void;\n read(\n handle: Buffer,\n buffer: Buffer,\n offset: number,\n length: number,\n position: number,\n callback: (err: Error | undefined, bytesRead: number, buf: Buffer) => void,\n ): void;\n close(handle: Buffer, callback: (err: Error | undefined) => void): void;\n end(): void;\n}\n\n/**\n * Minimal subset of the ssh2 Client interface used by SshComputer and SshFs.\n * Avoids a hard import of ssh2 at the module level.\n */\nexport interface SshClient {\n exec(\n command: string,\n callback: (err: Error | undefined, channel: SshChannel) => void,\n ): void;\n exec(\n command: string,\n opts: Record<string, unknown>,\n callback: (err: Error | undefined, channel: SshChannel) => void,\n ): void;\n sftp(\n callback: (err: Error | undefined, sftp: SshSftpSession) => void,\n ): void;\n end(): void;\n on(event: string, listener: (...args: any[]) => void): this;\n}\n\nexport interface SshComputerOptions {\n /** A connected ssh2 Client instance. */\n client: SshClient;\n /** Default working directory for commands (default: /). */\n defaultCwd?: string;\n /** Default timeout in ms for commands (default: 30000). */\n defaultTimeout?: number;\n}\n\n/**\n * VirtualComputer backed by command execution over SSH.\n *\n * Requires `ssh2` as an optional peer dependency.\n * The caller is responsible for the Client lifecycle (connect, end).\n */\nexport class SshComputer implements VirtualComputer {\n private client: SshClient;\n private defaultCwd: string;\n private defaultTimeout: number;\n\n constructor(opts: SshComputerOptions) {\n this.client = opts.client;\n this.defaultCwd = opts.defaultCwd ?? \"/\";\n this.defaultTimeout = opts.defaultTimeout ?? 30_000;\n }\n\n async executeCommand(\n command: string,\n opts?: ExecOptions,\n ): Promise<CommandResult> {\n const cwd = opts?.cwd ?? this.defaultCwd;\n const timeout = opts?.timeout ?? this.defaultTimeout;\n\n let envPrefix = \"\";\n if (opts?.env) {\n envPrefix = Object.entries(opts.env)\n .map(([k, v]) => `${k}=${shellEscape(v)}`)\n .join(\" \") + \" \";\n }\n\n const fullCommand = `cd ${shellEscape(cwd)} && ${envPrefix}${command}`;\n\n return new Promise<CommandResult>((resolve, reject) => {\n this.client.exec(fullCommand, (err, channel) => {\n if (err) {\n reject(err);\n return;\n }\n\n const stdoutBufs: Buffer[] = [];\n const stderrBufs: Buffer[] = [];\n\n const timer = setTimeout(() => {\n channel.destroy?.();\n resolve({\n exitCode: 1,\n stdout: Buffer.concat(stdoutBufs).toString(\"utf-8\"),\n stderr:\n Buffer.concat(stderrBufs).toString(\"utf-8\") +\n \"\\n[timeout after \" + timeout + \"ms]\",\n });\n }, timeout);\n\n channel.on(\"data\", (chunk: Buffer) => {\n stdoutBufs.push(Buffer.from(chunk));\n });\n\n channel.stderr.on(\"data\", (chunk: Buffer) => {\n stderrBufs.push(Buffer.from(chunk));\n });\n\n channel.on(\"close\", (code: number) => {\n clearTimeout(timer);\n resolve({\n exitCode: code ?? 0,\n stdout: Buffer.concat(stdoutBufs).toString(\"utf-8\"),\n stderr: Buffer.concat(stderrBufs).toString(\"utf-8\"),\n });\n });\n\n channel.on(\"error\", (channelErr: Error) => {\n clearTimeout(timer);\n reject(channelErr);\n });\n });\n });\n }\n}\n\nfunction shellEscape(s: string): string {\n return `'${s.replace(/'/g, \"'\\\\''\")}'`;\n}\n","import type { Sandbox } from \"./sandbox.js\";\nimport { SshFs } from \"./ssh-fs.js\";\nimport { SshComputer, type SshClient } from \"./ssh-computer.js\";\nimport { createFsProxy, createComputerProxy } from \"./proxy.js\";\n\nexport interface SshSandboxOptions {\n /**\n * A pre-connected ssh2 Client instance. When provided the sandbox uses\n * this client directly — no auto-connect occurs and `dispose()` will\n * **not** call `client.end()`.\n *\n * When omitted, a new ssh2 Client is created and connected on the first\n * `init()` call using `host`, `port`, `username`, and the provided\n * credentials. The client is ended when `dispose()` is called.\n */\n client?: SshClient;\n /**\n * SSH hostname. Required when `client` is omitted; ignored when `client`\n * is provided.\n */\n host?: string;\n /** SSH port (default: 22). Only used during auto-connect. */\n port?: number;\n /** SSH username (default: \"root\"). Only used during auto-connect. */\n username?: string;\n /** Password for password-based authentication. Only used during auto-connect. */\n password?: string;\n /** Private key for key-based authentication (PEM string or Buffer). Only used during auto-connect. */\n privateKey?: string | Buffer;\n /** Passphrase for an encrypted private key. Only used during auto-connect. */\n passphrase?: string;\n /** Working directory on the remote host. */\n cwd?: string;\n /** Default timeout (ms) for shell commands. */\n defaultTimeout?: number;\n}\n\n/**\n * Create a `Sandbox` backed by a remote host over SSH.\n * Requires `ssh2` as an optional peer dependency.\n *\n * **Auto-connect:** When `client` is omitted and `host` is provided,\n * an ssh2 Client is created and connected lazily on the first `init()`\n * call. The connection identifier (`host:port`) is available through\n * `sandboxId()` for session persistence.\n *\n * **Explicit client:** When `client` is provided, `init()` binds it\n * immediately. `dispose()` is a no-op — the caller owns the client's\n * lifecycle.\n *\n * @example\n * ```ts\n * // Auto-connect with private key\n * const sandbox = SshSandbox({\n * host: \"dev.example.com\",\n * username: \"deploy\",\n * privateKey: fs.readFileSync(\"~/.ssh/id_ed25519\"),\n * cwd: \"/home/deploy/project\",\n * });\n *\n * // Explicit client (lifecycle managed externally)\n * const sandbox = SshSandbox({ client: myConnectedClient, cwd: \"/workspace\" });\n * ```\n */\nexport function SshSandbox(opts: SshSandboxOptions): Sandbox {\n if (opts.client) {\n const c = opts.client;\n return {\n fs: new SshFs({ client: c, workingDir: opts.cwd }),\n computer: new SshComputer({\n client: c,\n defaultCwd: opts.cwd,\n defaultTimeout: opts.defaultTimeout,\n }),\n sandboxId: () =>\n opts.host ? `${opts.host}:${opts.port ?? 22}` : undefined,\n };\n }\n\n if (!opts.host) {\n throw new Error(\"SshSandbox requires either `client` or `host`\");\n }\n\n const fsProxy = createFsProxy();\n const computerProxy = createComputerProxy();\n const identifier = `${opts.host}:${opts.port ?? 22}`;\n let clientRef: SshClient | null = null;\n let autoCreated = false;\n let initPromise: Promise<void> | null = null;\n\n async function doInit(): Promise<void> {\n const modName = \"ssh2\";\n const ssh2 = await import(/* webpackIgnore: true */ modName);\n const ClientClass =\n (ssh2 as any).Client ?? (ssh2 as any).default?.Client;\n if (!ClientClass) {\n throw new Error(\n \"Could not resolve Client class from 'ssh2' package\",\n );\n }\n\n const client: SshClient = new ClientClass();\n await new Promise<void>((resolve, reject) => {\n client.on(\"ready\", () => resolve());\n client.on(\"error\", (err: Error) => reject(err));\n (client as any).connect({\n host: opts.host,\n port: opts.port ?? 22,\n username: opts.username ?? \"root\",\n password: opts.password,\n privateKey: opts.privateKey,\n passphrase: opts.passphrase,\n });\n });\n\n clientRef = client;\n autoCreated = true;\n fsProxy.setTarget(new SshFs({ client, workingDir: opts.cwd }));\n computerProxy.setTarget(\n new SshComputer({\n client,\n defaultCwd: opts.cwd,\n defaultTimeout: opts.defaultTimeout,\n }),\n );\n }\n\n return {\n fs: fsProxy,\n computer: computerProxy,\n sandboxId: () => identifier,\n\n init(): Promise<void> {\n if (!initPromise) {\n initPromise = doInit().catch((err) => {\n initPromise = null;\n throw err;\n });\n }\n return initPromise;\n },\n\n async dispose(): Promise<void> {\n if (initPromise) {\n await initPromise.catch(() => {});\n }\n if (!autoCreated || !clientRef) return;\n try {\n clientRef.end();\n } catch {\n /* best-effort */\n }\n },\n };\n}\n"],"mappings":"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","names":["resolve","sftp","resolve","resolve"]}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as
|
|
3
|
-
import { a as PermissionMode, b as PermissionResult } from './types-CD0rUKKT.js';
|
|
1
|
+
import { V as VirtualFs, a as VirtualComputer } from './computer-DzMR92tK.js';
|
|
2
|
+
import { C as CheckpointConfig, F as FileCheckpointState, D as DiffStats, a as FileCheckpointSnapshot, b as PermissionMode, S as StreamEvent, c as PermissionResult, d as ContentPart } from './types-DLZNyF5t.js';
|
|
4
3
|
|
|
5
4
|
/**
|
|
6
5
|
* Zod integration utilities. Users bring their own `zod` dependency — these
|
|
@@ -46,6 +45,74 @@ declare function registerZodToJsonSchema(fn: (schema: unknown) => JsonSchemaType
|
|
|
46
45
|
*/
|
|
47
46
|
declare function formatZodValidationError(toolName: string, issues: SafeParseResult["error"]): string;
|
|
48
47
|
|
|
48
|
+
/**
|
|
49
|
+
* Configuration for which dot-directory names the agent recognizes when
|
|
50
|
+
* reading and writing auxiliary state (context files, skills, sessions,
|
|
51
|
+
* tasks, checkpoints, CLI config, MCP auth tokens, etc).
|
|
52
|
+
*
|
|
53
|
+
* The list is ordered by preference:
|
|
54
|
+
* - `names[0]` is the canonical write target.
|
|
55
|
+
* - For single-file reads (e.g. `config.json`), callers use first-hit-wins
|
|
56
|
+
* across candidates in order.
|
|
57
|
+
* - For additive reads (e.g. rules, skills), every candidate is loaded and
|
|
58
|
+
* later entries in the list have higher precedence when resolving
|
|
59
|
+
* collisions (mirrors the context loader's layer ordering).
|
|
60
|
+
*
|
|
61
|
+
* The same list applies to both project scope (cwd ancestors) and user
|
|
62
|
+
* scope (home directory). There is intentionally no per-scope split — if
|
|
63
|
+
* a caller needs that, they can route through different resolvers.
|
|
64
|
+
*/
|
|
65
|
+
interface DotDirConfig {
|
|
66
|
+
names: string[];
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Default dot-directory configuration. Recognizes `.noumen` (canonical)
|
|
70
|
+
* and `.claude` (compatibility), preferring `.noumen` for writes.
|
|
71
|
+
*/
|
|
72
|
+
declare const DEFAULT_DOT_DIRS: DotDirConfig;
|
|
73
|
+
/**
|
|
74
|
+
* Pure path resolver over a `DotDirConfig`. Does no I/O — callers pair it
|
|
75
|
+
* with `readFirstDotDir` / `readAllDotDirs` (VirtualFs helpers below) or
|
|
76
|
+
* their own filesystem code.
|
|
77
|
+
*/
|
|
78
|
+
interface DotDirResolver {
|
|
79
|
+
/** The underlying configuration (exposed for permissions and logging). */
|
|
80
|
+
config: DotDirConfig;
|
|
81
|
+
/**
|
|
82
|
+
* Absolute paths for each candidate dot-dir under `base`, in preference
|
|
83
|
+
* order (most preferred first). `base` is appended with a `/` separator
|
|
84
|
+
* unless already present.
|
|
85
|
+
*/
|
|
86
|
+
candidates(base: string): string[];
|
|
87
|
+
/** The canonical write target (`candidates(base)[0]`). */
|
|
88
|
+
writePath(base: string): string;
|
|
89
|
+
/**
|
|
90
|
+
* Each candidate joined with `rel`, in preference order. Use this for
|
|
91
|
+
* first-hit-wins reads (e.g. `config.json`).
|
|
92
|
+
*/
|
|
93
|
+
joinRead(base: string, rel: string): string[];
|
|
94
|
+
/** Write path joined with `rel`. */
|
|
95
|
+
joinWrite(base: string, rel: string): string;
|
|
96
|
+
}
|
|
97
|
+
declare function createDotDirResolver(config?: DotDirConfig): DotDirResolver;
|
|
98
|
+
/**
|
|
99
|
+
* Read the first file that exists across the resolver's candidate paths
|
|
100
|
+
* under `base/<name>/<rel>`. Returns `null` if none match.
|
|
101
|
+
*/
|
|
102
|
+
declare function readFirstDotDir(fs: VirtualFs, resolver: DotDirResolver, base: string, rel: string): Promise<{
|
|
103
|
+
path: string;
|
|
104
|
+
content: string;
|
|
105
|
+
} | null>;
|
|
106
|
+
/**
|
|
107
|
+
* Read every candidate file that exists under `base/<name>/<rel>`. Results
|
|
108
|
+
* are ordered from lowest to highest precedence (matching the context
|
|
109
|
+
* loader's layer-stacking convention where later entries win).
|
|
110
|
+
*/
|
|
111
|
+
declare function readAllDotDirs(fs: VirtualFs, resolver: DotDirResolver, base: string, rel: string): Promise<Array<{
|
|
112
|
+
path: string;
|
|
113
|
+
content: string;
|
|
114
|
+
}>>;
|
|
115
|
+
|
|
49
116
|
type HookEvent = "PreToolUse" | "PostToolUse" | "PostToolUseFailure" | "PreCompact" | "PostCompact" | "TurnStart" | "TurnEnd" | "SubagentStart" | "SubagentStop" | "SessionStart" | "SessionEnd" | "PermissionRequest" | "PermissionDenied" | "FileWrite" | "ModelSwitch" | "RetryAttempt" | "MemoryUpdate" | "Error";
|
|
50
117
|
interface PreToolUseHookInput {
|
|
51
118
|
event: "PreToolUse";
|
|
@@ -474,6 +541,11 @@ interface ToolContext {
|
|
|
474
541
|
notifyHook?: (event: string, input: Record<string, unknown>) => Promise<void>;
|
|
475
542
|
/** Abort signal — tools should check this to terminate early on user abort. */
|
|
476
543
|
signal?: AbortSignal;
|
|
544
|
+
/**
|
|
545
|
+
* Dot-directory resolver — lets tools honor custom dot-dir configurations
|
|
546
|
+
* (e.g. Worktree writes under `<dotdir>/worktrees/<slug>`).
|
|
547
|
+
*/
|
|
548
|
+
dotDirResolver?: DotDirResolver;
|
|
477
549
|
}
|
|
478
550
|
interface ToolParameterProperty {
|
|
479
551
|
type: string;
|
|
@@ -556,4 +628,4 @@ interface Tool {
|
|
|
556
628
|
call(args: Record<string, unknown>, ctx: ToolContext): Promise<ToolResult>;
|
|
557
629
|
}
|
|
558
630
|
|
|
559
|
-
export { type
|
|
631
|
+
export { readFirstDotDir as $, type PermissionDeniedHookInput as A, type PermissionRequestHookInput as B, type SafeParseResult as C, DiagnosticRegistry as D, type SessionEndHookInput as E, FileCheckpointManager as F, type SessionStartHookInput as G, type HookDefinition as H, type SubagentStartHookInput as I, type JsonSchemaType as J, type SubagentStopHookInput as K, type LspServerConfig as L, type MemoryUpdateHookInput as M, type NotificationHookInput as N, type Task as O, type PostToolUseFailureHookInput as P, type TaskCreateInput as Q, type RetryAttemptHookInput as R, type SubagentConfig as S, type Tool as T, type TaskStatus as U, type TaskUpdateInput as V, type ToolParameters as W, createDotDirResolver as X, formatZodValidationError as Y, type ZodLikeSchema as Z, readAllDotDirs as _, type LspServerState as a, registerZodToJsonSchema as a0, zodToJsonSchema as a1, type LspDiagnostic as b, type LspLocation as c, type LspOperation as d, LspServerManager as e, type LspSymbol as f, type ToolResult as g, type ToolContext as h, type DotDirResolver as i, type DotDirConfig as j, type SubagentRun as k, TaskStore as l, type FileStateCacheConfig as m, type HookEvent as n, type HookInput as o, type PostToolUseFailureHookOutput as p, type PostToolUseHookInput as q, type PostToolUseHookOutput as r, type PreToolUseHookInput as s, type PreToolUseHookOutput as t, DEFAULT_DOT_DIRS as u, type FileState as v, FileStateCache as w, type FileWriteHookInput as x, type HookOutput as y, type ModelSwitchHookInput as z };
|
|
@@ -225,6 +225,17 @@ interface CustomTitleEntry {
|
|
|
225
225
|
title: string;
|
|
226
226
|
timestamp: string;
|
|
227
227
|
}
|
|
228
|
+
/**
|
|
229
|
+
* AI-generated session title. Persisted separately from `custom-title`
|
|
230
|
+
* so user-set titles always win regardless of write order. Reader logic
|
|
231
|
+
* prefers `custom-title` over `ai-title`.
|
|
232
|
+
*/
|
|
233
|
+
interface AiTitleEntry {
|
|
234
|
+
type: "ai-title";
|
|
235
|
+
sessionId: string;
|
|
236
|
+
title: string;
|
|
237
|
+
timestamp: string;
|
|
238
|
+
}
|
|
228
239
|
interface MetadataEntry {
|
|
229
240
|
type: "metadata";
|
|
230
241
|
sessionId: string;
|
|
@@ -265,12 +276,20 @@ interface ContentReplacementEntry {
|
|
|
265
276
|
timestamp: string;
|
|
266
277
|
replacements: ContentReplacementRecord[];
|
|
267
278
|
}
|
|
268
|
-
type Entry = MessageEntry | CompactBoundaryEntry | SummaryEntry | CustomTitleEntry | MetadataEntry | ToolResultOverflowEntry | FileCheckpointEntry | ContentReplacementEntry | SnipBoundaryEntry;
|
|
279
|
+
type Entry = MessageEntry | CompactBoundaryEntry | SummaryEntry | CustomTitleEntry | AiTitleEntry | MetadataEntry | ToolResultOverflowEntry | FileCheckpointEntry | ContentReplacementEntry | SnipBoundaryEntry;
|
|
269
280
|
interface SessionInfo {
|
|
270
281
|
sessionId: string;
|
|
271
282
|
createdAt: string;
|
|
272
283
|
lastMessageAt: string;
|
|
284
|
+
/**
|
|
285
|
+
* Effective title for display. When both a user-set (`custom-title`) and
|
|
286
|
+
* an AI-generated (`ai-title`) entry exist, the user-set one wins.
|
|
287
|
+
*/
|
|
273
288
|
title?: string;
|
|
289
|
+
/** User-set title, if any. */
|
|
290
|
+
customTitle?: string;
|
|
291
|
+
/** AI-generated title, if any. */
|
|
292
|
+
aiTitle?: string;
|
|
274
293
|
messageCount: number;
|
|
275
294
|
}
|
|
276
295
|
interface ToolResult {
|
|
@@ -388,6 +407,18 @@ type StreamEvent = {
|
|
|
388
407
|
type: "session_resumed";
|
|
389
408
|
sessionId: string;
|
|
390
409
|
messageCount: number;
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* Title change event. `noumen` defines the shape so consumers can wire
|
|
413
|
+
* renames + auto-titles into the same stream as other StreamEvents, but
|
|
414
|
+
* does not emit it internally: callers synthesize it after
|
|
415
|
+
* `Agent.setCustomTitle` / `Agent.autoTitleIfMissing` resolves.
|
|
416
|
+
*/
|
|
417
|
+
| {
|
|
418
|
+
type: "title_updated";
|
|
419
|
+
sessionId: string;
|
|
420
|
+
title: string;
|
|
421
|
+
source: "custom" | "ai";
|
|
391
422
|
} | {
|
|
392
423
|
type: "checkpoint_snapshot";
|
|
393
424
|
messageId: string;
|
|
@@ -543,6 +574,15 @@ interface ChatParams {
|
|
|
543
574
|
system?: string;
|
|
544
575
|
temperature?: number;
|
|
545
576
|
thinking?: ThinkingConfig;
|
|
577
|
+
/**
|
|
578
|
+
* Fine-grained reasoning effort hint. Honored by providers whose
|
|
579
|
+
* reasoning-capable models expose an explicit effort knob (OpenAI
|
|
580
|
+
* GPT-5 / o-series today). Providers that don't recognise the knob
|
|
581
|
+
* ignore it. Use `"minimal"` for cheap structural calls (e.g.
|
|
582
|
+
* auto-title, classification) so the model doesn't burn the whole
|
|
583
|
+
* `max_tokens` budget on internal reasoning before emitting output.
|
|
584
|
+
*/
|
|
585
|
+
reasoningEffort?: "minimal" | "low" | "medium" | "high";
|
|
546
586
|
/** Constrain the model to produce structured output matching this schema. */
|
|
547
587
|
outputFormat?: OutputFormat;
|
|
548
588
|
/**
|
|
@@ -556,6 +596,13 @@ interface ChatParams {
|
|
|
556
596
|
}
|
|
557
597
|
interface AIProvider {
|
|
558
598
|
chat(params: ChatParams): AsyncIterable<ChatStreamChunk>;
|
|
599
|
+
/**
|
|
600
|
+
* Optional fallback model name, used when no `model` is provided at the
|
|
601
|
+
* Thread / Agent level. Consumers should pass explicit models when they
|
|
602
|
+
* can, but this lets each provider ship a sensible default without
|
|
603
|
+
* forcing Thread to hardcode provider-specific strings.
|
|
604
|
+
*/
|
|
605
|
+
readonly defaultModel?: string;
|
|
559
606
|
}
|
|
560
607
|
/**
|
|
561
608
|
* Extended error type that providers can throw to convey retry-relevant metadata.
|
|
@@ -572,4 +619,119 @@ declare class ChatStreamError extends Error {
|
|
|
572
619
|
});
|
|
573
620
|
}
|
|
574
621
|
|
|
575
|
-
|
|
622
|
+
type PermissionMode = "default" | "plan" | "acceptEdits" | "auto" | "bypassPermissions" | "dontAsk";
|
|
623
|
+
type PermissionBehavior = "allow" | "deny" | "ask";
|
|
624
|
+
type PermissionRuleSource = "user" | "project" | "session" | "policy";
|
|
625
|
+
/** Precedence order: policy > project > user > session. */
|
|
626
|
+
declare const RULE_SOURCE_PRECEDENCE: PermissionRuleSource[];
|
|
627
|
+
interface PermissionRule {
|
|
628
|
+
toolName: string;
|
|
629
|
+
ruleContent?: string;
|
|
630
|
+
behavior: PermissionBehavior;
|
|
631
|
+
/** Where this rule came from. Higher-precedence sources override lower ones. */
|
|
632
|
+
source?: PermissionRuleSource;
|
|
633
|
+
}
|
|
634
|
+
type PermissionUpdate = {
|
|
635
|
+
type: "addRules";
|
|
636
|
+
rules: PermissionRule[];
|
|
637
|
+
} | {
|
|
638
|
+
type: "removeRules";
|
|
639
|
+
toolName: string;
|
|
640
|
+
behavior?: PermissionBehavior;
|
|
641
|
+
} | {
|
|
642
|
+
type: "setMode";
|
|
643
|
+
mode: PermissionMode;
|
|
644
|
+
} | {
|
|
645
|
+
type: "addDirectories";
|
|
646
|
+
directories: string[];
|
|
647
|
+
} | {
|
|
648
|
+
type: "removeDirectories";
|
|
649
|
+
directories: string[];
|
|
650
|
+
};
|
|
651
|
+
interface PermissionAllowResult<Input extends Record<string, unknown> = Record<string, unknown>> {
|
|
652
|
+
behavior: "allow";
|
|
653
|
+
updatedInput?: Input;
|
|
654
|
+
reason?: string;
|
|
655
|
+
}
|
|
656
|
+
interface PermissionDenyResult {
|
|
657
|
+
behavior: "deny";
|
|
658
|
+
message: string;
|
|
659
|
+
reason?: string;
|
|
660
|
+
}
|
|
661
|
+
interface PermissionAskResult {
|
|
662
|
+
behavior: "ask";
|
|
663
|
+
message: string;
|
|
664
|
+
reason?: string;
|
|
665
|
+
suggestions?: PermissionRule[];
|
|
666
|
+
}
|
|
667
|
+
interface PermissionPassthroughResult {
|
|
668
|
+
behavior: "passthrough";
|
|
669
|
+
message: string;
|
|
670
|
+
reason?: string;
|
|
671
|
+
suggestions?: PermissionRule[];
|
|
672
|
+
}
|
|
673
|
+
/**
|
|
674
|
+
* What `Tool.checkPermissions` returns. Includes `passthrough` for tools that
|
|
675
|
+
* have no opinion and want the global pipeline to decide.
|
|
676
|
+
*/
|
|
677
|
+
type PermissionResult<Input extends Record<string, unknown> = Record<string, unknown>> = PermissionAllowResult<Input> | PermissionDenyResult | PermissionAskResult | PermissionPassthroughResult;
|
|
678
|
+
/**
|
|
679
|
+
* Final decision after the pipeline resolves. No `passthrough` — always
|
|
680
|
+
* one of allow / deny / ask.
|
|
681
|
+
*/
|
|
682
|
+
type PermissionDecision<Input extends Record<string, unknown> = Record<string, unknown>> = PermissionAllowResult<Input> | PermissionDenyResult | PermissionAskResult;
|
|
683
|
+
interface PermissionRequest {
|
|
684
|
+
toolName: string;
|
|
685
|
+
input: Record<string, unknown>;
|
|
686
|
+
message: string;
|
|
687
|
+
suggestions?: PermissionRule[];
|
|
688
|
+
isReadOnly: boolean;
|
|
689
|
+
isDestructive: boolean;
|
|
690
|
+
/** Abort signal from the session — handlers should stop promptly when fired. */
|
|
691
|
+
signal?: AbortSignal;
|
|
692
|
+
}
|
|
693
|
+
interface PermissionResponse {
|
|
694
|
+
allow: boolean;
|
|
695
|
+
updatedInput?: Record<string, unknown>;
|
|
696
|
+
feedback?: string;
|
|
697
|
+
addRules?: PermissionRule[];
|
|
698
|
+
}
|
|
699
|
+
type PermissionHandler = (request: PermissionRequest) => Promise<PermissionResponse>;
|
|
700
|
+
interface AutoModeConfig {
|
|
701
|
+
/** Custom system prompt for the classifier. When omitted, uses a default. */
|
|
702
|
+
classifierPrompt?: string;
|
|
703
|
+
/** Model to use for classification. When omitted, uses the thread's model. */
|
|
704
|
+
classifierModel?: string;
|
|
705
|
+
}
|
|
706
|
+
interface DenialTrackingConfig {
|
|
707
|
+
/** Max consecutive denials before fallback (default: 3). */
|
|
708
|
+
maxConsecutive?: number;
|
|
709
|
+
/** Max total denials before fallback (default: 20). */
|
|
710
|
+
maxTotal?: number;
|
|
711
|
+
}
|
|
712
|
+
interface PermissionConfig {
|
|
713
|
+
mode?: PermissionMode;
|
|
714
|
+
rules?: PermissionRule[];
|
|
715
|
+
handler?: PermissionHandler;
|
|
716
|
+
workingDirectories?: string[];
|
|
717
|
+
/** Called when a permission update is applied (for host-side persistence). */
|
|
718
|
+
onPermissionUpdate?: (update: PermissionUpdate) => void;
|
|
719
|
+
/** Configuration for auto mode classifier. */
|
|
720
|
+
autoMode?: AutoModeConfig;
|
|
721
|
+
/** Configuration for denial tracking limits. */
|
|
722
|
+
denialTracking?: DenialTrackingConfig;
|
|
723
|
+
}
|
|
724
|
+
interface PermissionContext {
|
|
725
|
+
mode: PermissionMode;
|
|
726
|
+
rules: PermissionRule[];
|
|
727
|
+
workingDirectories: string[];
|
|
728
|
+
/**
|
|
729
|
+
* Ordered list of dot-directory names (e.g. `['.noumen', '.claude']`)
|
|
730
|
+
* whose contents are treated as dangerous paths for write/edit tools.
|
|
731
|
+
* When unset, defaults to the conservative built-in list
|
|
732
|
+
* (`['.noumen', '.claude']`) so non-agent callers stay protected.
|
|
733
|
+
*/
|
|
734
|
+
dotDirNames?: string[];
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
export { type ImageContent as $, type AIProvider as A, type MemoryEntry as B, type CheckpointConfig as C, type DiffStats as D, type Entry as E, type FileCheckpointState as F, type AssistantMessage as G, type ToolResultMessage as H, type ToolResultOverflowEntry as I, type JsonSchemaOutputFormat as J, type AiTitleEntry as K, type ChatStreamChoice as L, type ModelPricing as M, type ChatStreamDelta as N, type OutputFormat as O, type PermissionResponse as P, ChatStreamError as Q, type RunOptions as R, type StreamEvent as S, type ToolDefinition as T, type UUID as U, type CompactBoundaryEntry as V, type ContentReplacementEntry as W, type CustomTitleEntry as X, type DenialTrackingConfig as Y, type FileCheckpointBackup as Z, type FileCheckpointEntry as _, type FileCheckpointSnapshot as a, type ImageUrlContent as a0, type JsonObjectOutputFormat as a1, type MemoryType as a2, type MessageEntry as a3, type MetadataEntry as a4, type PermissionAllowResult as a5, type PermissionAskResult as a6, type PermissionDenyResult as a7, type PermissionPassthroughResult as a8, type PermissionRequest as a9, type PermissionRuleSource as aa, RULE_SOURCE_PRECEDENCE as ab, type SerializedMessage as ac, type SnipBoundaryEntry as ad, type SummaryEntry as ae, type SystemMessage as af, type TextContent as ag, type ToolParameterProperty as ah, type UserMessage as ai, createCheckpointState as aj, type PermissionMode as b, type PermissionResult as c, type ContentPart as d, type ChatMessage as e, type ContentReplacementRecord as f, type SessionInfo as g, type UsageRecord as h, type CostSummary as i, type ModelUsageSummary as j, type ChatCompletionUsage as k, type PermissionHandler as l, type PermissionConfig as m, type ThinkingConfig as n, type MemoryConfig as o, type ToolResult as p, type ChatParams as q, type ChatStreamChunk as r, type ToolCallContent as s, type PermissionContext as t, type PermissionBehavior as u, type PermissionRule as v, type AutoModeConfig as w, type PermissionDecision as x, type PermissionUpdate as y, type MemoryProvider as z };
|