@earendil-works/pi-coding-agent 1.0.3 → 1.1.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/CHANGELOG.md +75 -0
- package/dist/bundle/chunks/{anthropic-messages-Y22YDKSW.js → anthropic-messages-KT2ID7DZ.js} +1 -1
- package/dist/bundle/chunks/anthropic.js +1 -1
- package/dist/bundle/chunks/{azure-openai-responses-YJDJIIDQ.js → azure-openai-responses-6A4PNZTH.js} +1 -1
- package/dist/bundle/chunks/bedrock-converse-stream.js +2 -2
- package/dist/bundle/chunks/chunk-33CRHCAK.js +2 -0
- package/dist/bundle/chunks/{chunk-UBHCV62G.js → chunk-BK6FEHYX.js} +3 -3
- package/dist/bundle/chunks/chunk-CH4ZBH45.js +11 -0
- package/dist/bundle/chunks/{chunk-6DBOMCBS.js → chunk-GXLAGZ3Z.js} +1 -1
- package/dist/bundle/chunks/{chunk-J52J6TFP.js → chunk-HKYL362W.js} +1 -1
- package/dist/bundle/chunks/chunk-LPXNMA2W.js +2 -0
- package/dist/bundle/chunks/{chunk-TAOL3JFA.js → chunk-LTSARG5Q.js} +1 -1
- package/dist/bundle/chunks/{chunk-ZSQEXT6X.js → chunk-MXTZIDYW.js} +4 -4
- package/dist/bundle/chunks/chunk-O7YIWAPD.js +2 -0
- package/dist/bundle/chunks/{chunk-BFNE7BHG.js → chunk-OIM2DMFI.js} +83 -72
- package/dist/bundle/chunks/{chunk-AXOERKKW.js → chunk-OTL6MGLJ.js} +6 -4
- package/dist/bundle/chunks/{chunk-LD2P6SFZ.js → chunk-PMRZO46Y.js} +1 -1
- package/dist/bundle/chunks/{chunk-7TP2J6F2.js → chunk-SYCGMWFH.js} +1 -1
- package/dist/bundle/chunks/{chunk-2VBQLN7Y.js → chunk-UH5J7I7X.js} +9 -6
- package/dist/bundle/chunks/{chunk-WE4MXUQS.js → chunk-VLRCF7MR.js} +1 -1
- package/dist/bundle/chunks/chunk-W5MPVSHA.js +82 -0
- package/dist/bundle/chunks/chunk-Y4NYF362.js +2 -0
- package/dist/bundle/chunks/{cli-HTJP6F6Z.js → cli-OQYLWLA3.js} +2 -2
- package/dist/bundle/chunks/cloudflare-workers-ai-system-one-U3AH4ODQ.js +2 -0
- package/dist/bundle/chunks/codemode-worker.js +101 -5
- package/dist/bundle/chunks/{easter-egg-3d-6YCNESG3.js → easter-egg-3d-TQFQS342.js} +1 -1
- package/dist/bundle/chunks/{execute-JRUOLYDA.js → execute-7IRB5TUX.js} +110 -8
- package/dist/bundle/chunks/github-copilot.js +1 -1
- package/dist/bundle/chunks/{google-generative-ai-XMFHGQC5.js → google-generative-ai-X2VFFS4I.js} +1 -1
- package/dist/bundle/chunks/{google-vertex-4XRRNP74.js → google-vertex-KMGHFTSP.js} +1 -1
- package/dist/bundle/chunks/image-resize-worker.js +1 -1
- package/dist/bundle/chunks/{llama-cpp-classify-XT44PI3C.js → llama-cpp-classify-FYN623RH.js} +2 -2
- package/dist/bundle/chunks/{mistral-conversations-Q22M3IBG.js → mistral-conversations-PRZLM26U.js} +2 -2
- package/dist/bundle/chunks/openai-chatgpt.js +1 -1
- package/dist/bundle/chunks/{openai-codex-responses-4MYQSO5E.js → openai-codex-responses-ZSLJZYOE.js} +2 -2
- package/dist/bundle/chunks/openai-codex.js +1 -1
- package/dist/bundle/chunks/{openai-completions-OSRCWAAA.js → openai-completions-WWQW7S3V.js} +1 -1
- package/dist/bundle/chunks/openai-decisions-QLZKZLAK.js +5 -0
- package/dist/bundle/chunks/{openai-responses-UHF3DHNG.js → openai-responses-KV7QHJY6.js} +1 -1
- package/dist/bundle/chunks/{openrouter-images-ZVXWCSIM.js → openrouter-images-DUPG5DZO.js} +1 -1
- package/dist/bundle/chunks/{pi-messages-TM5SOOIW.js → pi-messages-YHKPQIHC.js} +1 -1
- package/dist/bundle/chunks/{runtime-NW7GPWKS.js → runtime-ROGZ2EXI.js} +1 -1
- package/dist/bundle/chunks/typesafe-system-one-B4NNEG67.js +2 -0
- package/dist/bundle/chunks/{virtual-modules-CFHJO5SY.js → virtual-modules-4YN2GNVW.js} +1 -1
- package/dist/bundle/cli-runtime.js +1 -1
- package/dist/bundle/index.js +1 -1
- package/dist/bundle/rpc-entry.js +1 -1
- package/dist/cli/args.d.ts +2 -1
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +24 -5
- package/dist/cli/args.js.map +1 -1
- package/dist/core/agent-session.d.ts +33 -4
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +59 -21
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/bash-executor.d.ts.map +1 -1
- package/dist/core/bash-executor.js +40 -45
- package/dist/core/bash-executor.js.map +1 -1
- package/dist/core/export-html/tool-renderer.d.ts.map +1 -1
- package/dist/core/export-html/tool-renderer.js +2 -0
- package/dist/core/export-html/tool-renderer.js.map +1 -1
- package/dist/core/extensions/types.d.ts +13 -0
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/mcp-servers.d.ts +8 -0
- package/dist/core/mcp-servers.d.ts.map +1 -1
- package/dist/core/mcp-servers.js +30 -7
- package/dist/core/mcp-servers.js.map +1 -1
- package/dist/core/model-runtime.d.ts.map +1 -1
- package/dist/core/model-runtime.js +2 -1
- package/dist/core/model-runtime.js.map +1 -1
- package/dist/core/nested-tool-calls.d.ts +1 -0
- package/dist/core/nested-tool-calls.d.ts.map +1 -1
- package/dist/core/nested-tool-calls.js +1 -0
- package/dist/core/nested-tool-calls.js.map +1 -1
- package/dist/core/resource-loader.d.ts +3 -0
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +4 -3
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/sdk.d.ts +13 -4
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +19 -6
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/settings-manager.d.ts +13 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +37 -13
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/skills.d.ts +4 -1
- package/dist/core/skills.d.ts.map +1 -1
- package/dist/core/skills.js +6 -1
- package/dist/core/skills.js.map +1 -1
- package/dist/core/system-prompt.d.ts +6 -0
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +9 -4
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/tools/read.d.ts +13 -0
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js +18 -1
- package/dist/core/tools/read.js.map +1 -1
- package/dist/core/tools/renderers/bash.d.ts.map +1 -1
- package/dist/core/tools/renderers/bash.js +8 -3
- package/dist/core/tools/renderers/bash.js.map +1 -1
- package/dist/core/tools/renderers/edit.d.ts.map +1 -1
- package/dist/core/tools/renderers/edit.js +5 -4
- package/dist/core/tools/renderers/edit.js.map +1 -1
- package/dist/extensions/codemode/execute.d.ts.map +1 -1
- package/dist/extensions/codemode/execute.js +65 -8
- package/dist/extensions/codemode/execute.js.map +1 -1
- package/dist/extensions/codemode/index.d.ts.map +1 -1
- package/dist/extensions/codemode/index.js +1 -0
- package/dist/extensions/codemode/index.js.map +1 -1
- package/dist/extensions/codemode/tool.d.ts +9 -2
- package/dist/extensions/codemode/tool.d.ts.map +1 -1
- package/dist/extensions/codemode/tool.js +14 -6
- package/dist/extensions/codemode/tool.js.map +1 -1
- package/dist/extensions/llama/provider.d.ts.map +1 -1
- package/dist/extensions/llama/provider.js +34 -11
- package/dist/extensions/llama/provider.js.map +1 -1
- package/dist/extensions/mcp/cli.js +17 -23
- package/dist/extensions/mcp/cli.js.map +1 -1
- package/dist/extensions/mcp/index.d.ts.map +1 -1
- package/dist/extensions/mcp/index.js +165 -64
- package/dist/extensions/mcp/index.js.map +1 -1
- package/dist/extensions/mcp/oauth.d.ts +5 -3
- package/dist/extensions/mcp/oauth.d.ts.map +1 -1
- package/dist/extensions/mcp/oauth.js +36 -10
- package/dist/extensions/mcp/oauth.js.map +1 -1
- package/dist/extensions/mcp/resources.d.ts +3 -4
- package/dist/extensions/mcp/resources.d.ts.map +1 -1
- package/dist/extensions/mcp/resources.js +3 -4
- package/dist/extensions/mcp/resources.js.map +1 -1
- package/dist/extensions/mcp/runtime.d.ts +4 -1
- package/dist/extensions/mcp/runtime.d.ts.map +1 -1
- package/dist/extensions/mcp/runtime.js +22 -5
- package/dist/extensions/mcp/runtime.js.map +1 -1
- package/dist/extensions/mcp/tools.d.ts +2 -1
- package/dist/extensions/mcp/tools.d.ts.map +1 -1
- package/dist/extensions/mcp/tools.js +2 -1
- package/dist/extensions/mcp/tools.js.map +1 -1
- package/dist/extensions/mcp/ui.d.ts +3 -3
- package/dist/extensions/mcp/ui.d.ts.map +1 -1
- package/dist/extensions/mcp/ui.js +10 -2
- package/dist/extensions/mcp/ui.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +1 -0
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/bash-execution.d.ts +5 -1
- package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/bash-execution.js +17 -14
- package/dist/modes/interactive/components/bash-execution.js.map +1 -1
- package/dist/modes/interactive/components/branch-summary-message.d.ts +2 -1
- package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/branch-summary-message.js +5 -2
- package/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
- package/dist/modes/interactive/components/compaction-summary-message.d.ts +2 -1
- package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/compaction-summary-message.js +5 -2
- package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
- package/dist/modes/interactive/components/custom-entry.d.ts +3 -1
- package/dist/modes/interactive/components/custom-entry.d.ts.map +1 -1
- package/dist/modes/interactive/components/custom-entry.js +8 -2
- package/dist/modes/interactive/components/custom-entry.js.map +1 -1
- package/dist/modes/interactive/components/custom-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/custom-message.js +1 -0
- package/dist/modes/interactive/components/custom-message.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +1 -1
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/components/skill-invocation-message.d.ts +2 -1
- package/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/skill-invocation-message.js +5 -2
- package/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts +5 -0
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +10 -0
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +2 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +50 -29
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/program-status-reporter.d.ts +34 -0
- package/dist/modes/interactive/program-status-reporter.d.ts.map +1 -0
- package/dist/modes/interactive/program-status-reporter.js +110 -0
- package/dist/modes/interactive/program-status-reporter.js.map +1 -0
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +1 -0
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/dist/package-manager-cli.d.ts.map +1 -1
- package/dist/package-manager-cli.js +24 -0
- package/dist/package-manager-cli.js.map +1 -1
- package/dist/utils/ansi.d.ts +8 -0
- package/dist/utils/ansi.d.ts.map +1 -1
- package/dist/utils/ansi.js +30 -11
- package/dist/utils/ansi.js.map +1 -1
- package/dist/utils/clipboard.d.ts.map +1 -1
- package/dist/utils/clipboard.js +12 -11
- package/dist/utils/clipboard.js.map +1 -1
- package/dist/utils/image-resize-core.d.ts +12 -0
- package/dist/utils/image-resize-core.d.ts.map +1 -1
- package/dist/utils/image-resize-core.js +5 -0
- package/dist/utils/image-resize-core.js.map +1 -1
- package/dist/utils/image-resize-worker.js +3 -2
- package/dist/utils/image-resize-worker.js.map +1 -1
- package/dist/utils/image-resize.d.ts.map +1 -1
- package/dist/utils/image-resize.js +9 -10
- package/dist/utils/image-resize.js.map +1 -1
- package/dist/utils/syntax-highlight.d.ts.map +1 -1
- package/dist/utils/syntax-highlight.js +6 -1
- package/dist/utils/syntax-highlight.js.map +1 -1
- package/docs/cli.md +18 -6
- package/docs/codemode.md +26 -5
- package/docs/environment-variables.md +1 -0
- package/docs/extensions.md +1 -1
- package/docs/json.md +3 -3
- package/docs/llama-cpp.md +8 -1
- package/docs/mcp.md +3 -1
- package/docs/message-types.md +22 -4
- package/docs/models.md +4 -1
- package/docs/providers.md +27 -0
- package/docs/settings.md +3 -3
- package/docs/terminal-setup.md +14 -0
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/gondolin/package-lock.json +2 -2
- package/examples/extensions/gondolin/package.json +1 -1
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/package.json +12 -12
- package/dist/bundle/chunks/chunk-A3JYRWB6.js +0 -2
- package/dist/bundle/chunks/chunk-C47CLQZK.js +0 -2
- package/dist/bundle/chunks/chunk-HQHD6PZI.js +0 -2
- package/dist/bundle/chunks/chunk-KJUDFE6Y.js +0 -11
- package/dist/bundle/chunks/chunk-PSQJVYXD.js +0 -82
- package/dist/bundle/chunks/cloudflare-workers-ai-system-one-NM3JRRWQ.js +0 -2
- package/dist/bundle/chunks/typesafe-system-one-PRR5K6CI.js +0 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"clipboard.js","sourceRoot":"","sources":["../../src/utils/clipboard.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { unlinkSync, writeFileSync } from \"node:fs\";\nimport { platform, tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { getNativeClipboard } from \"@earendil-works/pi-tui\";\nimport { runClipboardCommand } from \"./clipboard-command.ts\";\nimport { isWSL } from \"./wsl.ts\";\n\nconst MAX_OSC52_ENCODED_LENGTH = 100_000;\n\nfunction isRemoteSession(env: NodeJS.ProcessEnv): boolean {\n\treturn Boolean(env.SSH_CONNECTION || env.SSH_CLIENT || env.MOSH_CONNECTION);\n}\n\nfunction emitOsc52(text: string): boolean {\n\tconst encoded = Buffer.from(text).toString(\"base64\");\n\tif (encoded.length > MAX_OSC52_ENCODED_LENGTH) {\n\t\treturn false;\n\t}\n\tprocess.stdout.write(`\\x1b]52;c;${encoded}\\x07`);\n\treturn true;\n}\n\n/**\n * WSL without WSLg has no Linux display, so the Windows clipboard is written through\n * interop. PowerShell reads the text from a file because `clip.exe` and PowerShell stdin\n * decode piped bytes with the console code page, which mangles non-ASCII UTF-8.\n */\nasync function copyViaWindowsClipboard(text: string): Promise<boolean> {\n\tconst tmpFile = join(tmpdir(), `pi-wsl-clip-${randomUUID()}.txt`);\n\ttry {\n\t\twriteFileSync(tmpFile, text, { encoding: \"utf8\", mode: 0o600 });\n\t\tconst winPath = (await runClipboardCommand(\"wslpath\", [\"-w\", tmpFile], { timeoutMs: 1000 }))\n\t\t\t?.toString(\"utf8\")\n\t\t\t.trim();\n\t\tif (!winPath) return false;\n\t\tconst script = `Set-Clipboard -Value ([System.IO.File]::ReadAllText('${winPath.replaceAll(\"'\", \"''\")}', [System.Text.Encoding]::UTF8))`;\n\t\tconst result = await runClipboardCommand(\"powershell.exe\", [\"-NoProfile\", \"-Command\", script], {\n\t\t\ttimeoutMs: 5000,\n\t\t});\n\t\treturn result !== undefined;\n\t} catch {\n\t\treturn false;\n\t} finally {\n\t\ttry {\n\t\t\tunlinkSync(tmpFile);\n\t\t} catch {\n\t\t\t// The file may not have been created.\n\t\t}\n\t}\n}\n\n/** Read plain text from the system clipboard. */\nexport async function readClipboardText(): Promise<string | null> {\n\tif (platform() === \"linux\") {\n\t\tconst commands: [string, string[]][] = [];\n\t\tif (process.env.TERMUX_VERSION) commands.push([\"termux-clipboard-get\", []]);\n\t\tif (process.env.WAYLAND_DISPLAY) commands.push([\"wl-paste\", [\"--no-newline\", \"--type\", \"text\"]]);\n\t\tif (process.env.DISPLAY) {\n\t\t\tcommands.push([\"xclip\", [\"-selection\", \"clipboard\", \"-out\"]], [\"xsel\", [\"--clipboard\", \"--output\"]]);\n\t\t}\n\t\tfor (const [command, args] of commands) {\n\t\t\tconst bytes = await runClipboardCommand(command, args, { timeoutMs: 5000 });\n\t\t\tif (bytes !== undefined) return bytes.toString(\"utf8\") || null;\n\t\t}\n\t}\n\ttry {\n\t\treturn (await getNativeClipboard()?.getText()) || null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/** Read file paths, such as Finder file copies, from the native clipboard. */\nexport async function readClipboardFilePaths(): Promise<string[] | null> {\n\tconst paths = await getNativeClipboard()?.getFilePaths?.();\n\treturn paths?.length ? paths : null;\n}\n\nexport async function copyToClipboard(text: string): Promise<void> {\n\tconst p = platform();\n\tconst env = process.env;\n\tlet copied = false;\n\t// Direct writes precede OSC 52 so the terminal cannot race the native writer.\n\t// Linux tools retain clipboard selection ownership after this call returns.\n\tif (p !== \"linux\") {\n\t\ttry {\n\t\t\tconst clipboard = getNativeClipboard();\n\t\t\tif (clipboard?.setText) {\n\t\t\t\tawait clipboard.setText(text);\n\t\t\t\tcopied = true;\n\t\t\t}\n\t\t} catch {\n\t\t\t// Try platform commands next.\n\t\t}\n\t}\n\tif (!copied) {\n\t\tconst commands: [string, string[]][] = [];\n\t\tif (p === \"darwin\") commands.push([\"pbcopy\", []]);\n\t\telse if (p === \"win32\") commands.push([\"clip\", []]);\n\t\telse {\n\t\t\tif (env.TERMUX_VERSION) commands.push([\"termux-clipboard-set\", []]);\n\t\t\tif (env.WAYLAND_DISPLAY) commands.push([\"wl-copy\", []]);\n\t\t\tif (env.DISPLAY) {\n\t\t\t\tcommands.push([\"xclip\", [\"-selection\", \"clipboard\"]], [\"xsel\", [\"--clipboard\", \"--input\"]]);\n\t\t\t}\n\t\t}\n\t\tfor (const [command, args] of commands) {\n\t\t\tif ((await runClipboardCommand(command, args, { input: text, timeoutMs: 5000 })) !== undefined) {\n\t\t\t\tcopied = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\tlet osc52Emitted = false;\n\tif (!copied && p === \"linux\" && isWSL(env)) {\n\t\t// Windows Terminal supports OSC 52; prefer it over the slower PowerShell round trip.\n\t\tif (env.WT_SESSION) osc52Emitted = emitOsc52(text);\n\t\tcopied = osc52Emitted || (await copyViaWindowsClipboard(text));\n\t}\n\t// OSC 52 cannot be verified, so a desktop session with a display reports the failure\n\t// instead (#9618). Without a display the terminal is the only clipboard route (containers,\n\t// WSL without WSLg), and remote sessions always emit it to reach the client clipboard.\n\tconst headless = p === \"linux\" && !env.DISPLAY && !env.WAYLAND_DISPLAY && !env.TERMUX_VERSION;\n\tlet oversized = false;\n\tif (!osc52Emitted && (isRemoteSession(env) || (!copied && headless))) {\n\t\tif (emitOsc52(text)) copied = true;\n\t\telse oversized = true;\n\t}\n\tif (copied) return;\n\tif (oversized) throw new Error(\"Clipboard unavailable: text exceeds the OSC 52 size limit\");\n\tif (p === \"linux\") {\n\t\tif (env.TERMUX_VERSION) {\n\t\t\tthrow new Error(\"Clipboard unavailable: install the Termux:API app and `termux-api` package\");\n\t\t}\n\t\tif (env.WAYLAND_DISPLAY) {\n\t\t\tthrow new Error(\"Clipboard unavailable: install `wl-clipboard` (`wl-copy`) or check Wayland access\");\n\t\t}\n\t\tif (env.DISPLAY) {\n\t\t\tthrow new Error(\"Clipboard unavailable: install `xclip` or `xsel`, or check X11 access\");\n\t\t}\n\t}\n\tthrow new Error(\"Clipboard unavailable\");\n}\n"]}
|
|
1
|
+
{"version":3,"file":"clipboard.js","sourceRoot":"","sources":["../../src/utils/clipboard.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { unlinkSync, writeFileSync } from \"node:fs\";\nimport { platform, tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { getNativeClipboard } from \"@earendil-works/pi-tui\";\nimport { runClipboardCommand } from \"./clipboard-command.ts\";\nimport { isWSL } from \"./wsl.ts\";\n\nconst MAX_OSC52_ENCODED_LENGTH = 100_000;\n\nfunction isRemoteSession(env: NodeJS.ProcessEnv): boolean {\n\treturn Boolean(env.SSH_CONNECTION || env.SSH_CLIENT || env.MOSH_CONNECTION);\n}\n\nfunction emitOsc52(text: string): boolean {\n\tconst encoded = Buffer.from(text).toString(\"base64\");\n\tif (encoded.length > MAX_OSC52_ENCODED_LENGTH) {\n\t\treturn false;\n\t}\n\tprocess.stdout.write(`\\x1b]52;c;${encoded}\\x07`);\n\treturn true;\n}\n\n/**\n * WSL without WSLg has no Linux display, so the Windows clipboard is written through\n * interop. PowerShell reads the text from a file because `clip.exe` and PowerShell stdin\n * decode piped bytes with the console code page, which mangles non-ASCII UTF-8.\n */\nasync function copyViaWindowsClipboard(text: string): Promise<boolean> {\n\tconst tmpFile = join(tmpdir(), `pi-wsl-clip-${randomUUID()}.txt`);\n\ttry {\n\t\twriteFileSync(tmpFile, text, { encoding: \"utf8\", mode: 0o600 });\n\t\tconst winPath = (await runClipboardCommand(\"wslpath\", [\"-w\", tmpFile], { timeoutMs: 1000 }))\n\t\t\t?.toString(\"utf8\")\n\t\t\t.trim();\n\t\tif (!winPath) return false;\n\t\tconst script = `Set-Clipboard -Value ([System.IO.File]::ReadAllText('${winPath.replaceAll(\"'\", \"''\")}', [System.Text.Encoding]::UTF8))`;\n\t\tconst result = await runClipboardCommand(\"powershell.exe\", [\"-NoProfile\", \"-Command\", script], {\n\t\t\ttimeoutMs: 5000,\n\t\t});\n\t\treturn result !== undefined;\n\t} catch {\n\t\treturn false;\n\t} finally {\n\t\ttry {\n\t\t\tunlinkSync(tmpFile);\n\t\t} catch {\n\t\t\t// The file may not have been created.\n\t\t}\n\t}\n}\n\n/** Read plain text from the system clipboard. */\nexport async function readClipboardText(): Promise<string | null> {\n\tconst commands: [string, string[]][] = [];\n\t// Termux reports platform \"android\", not \"linux\" (#10391).\n\tif (process.env.TERMUX_VERSION) commands.push([\"termux-clipboard-get\", []]);\n\tif (platform() === \"linux\") {\n\t\tif (process.env.WAYLAND_DISPLAY) commands.push([\"wl-paste\", [\"--no-newline\", \"--type\", \"text\"]]);\n\t\tif (process.env.DISPLAY) {\n\t\t\tcommands.push([\"xclip\", [\"-selection\", \"clipboard\", \"-out\"]], [\"xsel\", [\"--clipboard\", \"--output\"]]);\n\t\t}\n\t}\n\tfor (const [command, args] of commands) {\n\t\tconst bytes = await runClipboardCommand(command, args, { timeoutMs: 5000 });\n\t\tif (bytes !== undefined) return bytes.toString(\"utf8\") || null;\n\t}\n\ttry {\n\t\treturn (await getNativeClipboard()?.getText()) || null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/** Read file paths, such as Finder file copies, from the native clipboard. */\nexport async function readClipboardFilePaths(): Promise<string[] | null> {\n\tconst paths = await getNativeClipboard()?.getFilePaths?.();\n\treturn paths?.length ? paths : null;\n}\n\nexport async function copyToClipboard(text: string): Promise<void> {\n\tconst p = platform();\n\tconst env = process.env;\n\tlet copied = false;\n\t// Direct writes precede OSC 52 so the terminal cannot race the native writer.\n\t// Linux tools retain clipboard selection ownership after this call returns.\n\tif (p !== \"linux\") {\n\t\ttry {\n\t\t\tconst clipboard = getNativeClipboard();\n\t\t\tif (clipboard?.setText) {\n\t\t\t\tawait clipboard.setText(text);\n\t\t\t\tcopied = true;\n\t\t\t}\n\t\t} catch {\n\t\t\t// Try platform commands next.\n\t\t}\n\t}\n\tif (!copied) {\n\t\tconst commands: [string, string[]][] = [];\n\t\tif (p === \"darwin\") commands.push([\"pbcopy\", []]);\n\t\telse if (p === \"win32\") commands.push([\"clip\", []]);\n\t\telse {\n\t\t\tif (env.TERMUX_VERSION) commands.push([\"termux-clipboard-set\", []]);\n\t\t\tif (env.WAYLAND_DISPLAY) commands.push([\"wl-copy\", []]);\n\t\t\tif (env.DISPLAY) {\n\t\t\t\tcommands.push([\"xclip\", [\"-selection\", \"clipboard\"]], [\"xsel\", [\"--clipboard\", \"--input\"]]);\n\t\t\t}\n\t\t}\n\t\tfor (const [command, args] of commands) {\n\t\t\tif ((await runClipboardCommand(command, args, { input: text, timeoutMs: 5000 })) !== undefined) {\n\t\t\t\tcopied = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\tlet osc52Emitted = false;\n\tif (!copied && p === \"linux\" && isWSL(env)) {\n\t\t// Windows Terminal supports OSC 52; prefer it over the slower PowerShell round trip.\n\t\tif (env.WT_SESSION) osc52Emitted = emitOsc52(text);\n\t\tcopied = osc52Emitted || (await copyViaWindowsClipboard(text));\n\t}\n\t// OSC 52 cannot be verified, so a desktop session with a display reports the failure\n\t// instead (#9618). Without a display the terminal is the only clipboard route (containers,\n\t// WSL without WSLg), and remote sessions always emit it to reach the client clipboard.\n\tconst headless = p === \"linux\" && !env.DISPLAY && !env.WAYLAND_DISPLAY && !env.TERMUX_VERSION;\n\tlet oversized = false;\n\tif (!osc52Emitted && (isRemoteSession(env) || (!copied && headless))) {\n\t\tif (emitOsc52(text)) copied = true;\n\t\telse oversized = true;\n\t}\n\tif (copied) return;\n\tif (oversized) throw new Error(\"Clipboard unavailable: text exceeds the OSC 52 size limit\");\n\tif (env.TERMUX_VERSION) {\n\t\tthrow new Error(\"Clipboard unavailable: install the Termux:API app and `termux-api` package\");\n\t}\n\tif (p === \"linux\") {\n\t\tif (env.WAYLAND_DISPLAY) {\n\t\t\tthrow new Error(\"Clipboard unavailable: install `wl-clipboard` (`wl-copy`) or check Wayland access\");\n\t\t}\n\t\tif (env.DISPLAY) {\n\t\t\tthrow new Error(\"Clipboard unavailable: install `xclip` or `xsel`, or check X11 access\");\n\t\t}\n\t}\n\tthrow new Error(\"Clipboard unavailable\");\n}\n"]}
|
|
@@ -13,6 +13,18 @@ export interface ResizedImage {
|
|
|
13
13
|
height: number;
|
|
14
14
|
wasResized: boolean;
|
|
15
15
|
}
|
|
16
|
+
/**
|
|
17
|
+
* Tag on image resize worker replies. Node can post its own messages on the worker channel
|
|
18
|
+
* (for example `{ "watch:require": [...] }` under `node --watch`), so replies must be identifiable.
|
|
19
|
+
*/
|
|
20
|
+
export declare const IMAGE_RESIZE_WORKER_RESPONSE_TYPE = "pi:image-resize-response";
|
|
21
|
+
export type ResizeImageWorkerResponse = {
|
|
22
|
+
type: typeof IMAGE_RESIZE_WORKER_RESPONSE_TYPE;
|
|
23
|
+
result: ResizedImage | null;
|
|
24
|
+
} | {
|
|
25
|
+
type: typeof IMAGE_RESIZE_WORKER_RESPONSE_TYPE;
|
|
26
|
+
error: string;
|
|
27
|
+
};
|
|
16
28
|
/**
|
|
17
29
|
* Resize an image to fit within the specified max dimensions and encoded file size.
|
|
18
30
|
* Returns null if the image cannot be resized below maxBytes.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"image-resize-core.d.ts","sourceRoot":"","sources":["../../src/utils/image-resize-core.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,kBAAkB;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,OAAO,CAAC;CACpB;
|
|
1
|
+
{"version":3,"file":"image-resize-core.d.ts","sourceRoot":"","sources":["../../src/utils/image-resize-core.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,kBAAkB;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,OAAO,CAAC;CACpB;AAED;;;GAGG;AACH,eAAO,MAAM,iCAAiC,6BAA6B,CAAC;AAE5E,MAAM,MAAM,yBAAyB,GAClC;IAAE,IAAI,EAAE,OAAO,iCAAiC,CAAC;IAAC,MAAM,EAAE,YAAY,GAAG,IAAI,CAAA;CAAE,GAC/E;IAAE,IAAI,EAAE,OAAO,iCAAiC,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AA2BrE;;;;;;;;;;;;GAYG;AACH,wBAAsB,oBAAoB,CACzC,UAAU,EAAE,UAAU,EACtB,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,kBAAkB,GAC1B,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAqG9B"}
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { applyExifOrientation } from "./exif-orientation.js";
|
|
2
2
|
import { loadPhoton } from "./photon.js";
|
|
3
|
+
/**
|
|
4
|
+
* Tag on image resize worker replies. Node can post its own messages on the worker channel
|
|
5
|
+
* (for example `{ "watch:require": [...] }` under `node --watch`), so replies must be identifiable.
|
|
6
|
+
*/
|
|
7
|
+
export const IMAGE_RESIZE_WORKER_RESPONSE_TYPE = "pi:image-resize-response";
|
|
3
8
|
// 4.5MB of base64 payload. Provides headroom below Anthropic's 5MB limit.
|
|
4
9
|
const DEFAULT_MAX_BYTES = 4.5 * 1024 * 1024;
|
|
5
10
|
const DEFAULT_OPTIONS = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"image-resize-core.js","sourceRoot":"","sources":["../../src/utils/image-resize-core.ts"],"names":[],"mappings":"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","sourcesContent":["import { applyExifOrientation } from \"./exif-orientation.ts\";\nimport { loadPhoton } from \"./photon.ts\";\n\nexport interface ImageResizeOptions {\n\tmaxWidth?: number; // Default: 2000\n\tmaxHeight?: number; // Default: 2000\n\tmaxBytes?: number; // Default: 4.5MB of base64 payload (below Anthropic's 5MB limit)\n\tjpegQuality?: number; // Default: 80\n}\n\nexport interface ResizedImage {\n\tdata: string; // base64\n\tmimeType: string;\n\toriginalWidth: number;\n\toriginalHeight: number;\n\twidth: number;\n\theight: number;\n\twasResized: boolean;\n}\n\n// 4.5MB of base64 payload. Provides headroom below Anthropic's 5MB limit.\nconst DEFAULT_MAX_BYTES = 4.5 * 1024 * 1024;\n\nconst DEFAULT_OPTIONS: Required<ImageResizeOptions> = {\n\tmaxWidth: 2000,\n\tmaxHeight: 2000,\n\tmaxBytes: DEFAULT_MAX_BYTES,\n\tjpegQuality: 80,\n};\n\ninterface EncodedCandidate {\n\tdata: string;\n\tencodedSize: number;\n\tmimeType: string;\n}\n\nfunction encodeCandidate(buffer: Uint8Array, mimeType: string): EncodedCandidate {\n\tconst data = Buffer.from(buffer).toString(\"base64\");\n\treturn {\n\t\tdata,\n\t\tencodedSize: Buffer.byteLength(data, \"utf-8\"),\n\t\tmimeType,\n\t};\n}\n\n/**\n * Resize an image to fit within the specified max dimensions and encoded file size.\n * Returns null if the image cannot be resized below maxBytes.\n *\n * Uses Photon (Rust/WASM) for image processing. If Photon is not available,\n * returns null.\n *\n * Strategy for staying under maxBytes:\n * 1. First resize to maxWidth/maxHeight\n * 2. Try both PNG and JPEG formats, pick the smaller one\n * 3. If still too large, try JPEG with decreasing quality\n * 4. If still too large, progressively reduce dimensions until 1x1\n */\nexport async function resizeImageInProcess(\n\tinputBytes: Uint8Array,\n\tmimeType: string,\n\toptions?: ImageResizeOptions,\n): Promise<ResizedImage | null> {\n\tconst opts = { ...DEFAULT_OPTIONS, ...options };\n\tconst inputBase64Size = Math.ceil(inputBytes.byteLength / 3) * 4;\n\n\tconst photon = await loadPhoton();\n\tif (!photon) {\n\t\treturn null;\n\t}\n\n\tlet image: ReturnType<typeof photon.PhotonImage.new_from_byteslice> | undefined;\n\ttry {\n\t\tconst rawImage = photon.PhotonImage.new_from_byteslice(inputBytes);\n\t\timage = applyExifOrientation(photon, rawImage, inputBytes);\n\t\tif (image !== rawImage) rawImage.free();\n\n\t\tconst originalWidth = image.get_width();\n\t\tconst originalHeight = image.get_height();\n\t\tconst format = mimeType.split(\"/\")[1] ?? \"png\";\n\n\t\t// Check if already within all limits (dimensions AND encoded size)\n\t\tif (originalWidth <= opts.maxWidth && originalHeight <= opts.maxHeight && inputBase64Size < opts.maxBytes) {\n\t\t\treturn {\n\t\t\t\tdata: Buffer.from(inputBytes).toString(\"base64\"),\n\t\t\t\tmimeType: mimeType || `image/${format}`,\n\t\t\t\toriginalWidth,\n\t\t\t\toriginalHeight,\n\t\t\t\twidth: originalWidth,\n\t\t\t\theight: originalHeight,\n\t\t\t\twasResized: false,\n\t\t\t};\n\t\t}\n\n\t\t// Calculate initial dimensions respecting max limits\n\t\tlet targetWidth = originalWidth;\n\t\tlet targetHeight = originalHeight;\n\n\t\tif (targetWidth > opts.maxWidth) {\n\t\t\ttargetHeight = Math.round((targetHeight * opts.maxWidth) / targetWidth);\n\t\t\ttargetWidth = opts.maxWidth;\n\t\t}\n\t\tif (targetHeight > opts.maxHeight) {\n\t\t\ttargetWidth = Math.round((targetWidth * opts.maxHeight) / targetHeight);\n\t\t\ttargetHeight = opts.maxHeight;\n\t\t}\n\n\t\tfunction tryEncodings(width: number, height: number, jpegQualities: number[]): EncodedCandidate[] {\n\t\t\tconst resized = photon!.resize(image!, width, height, photon!.SamplingFilter.Lanczos3);\n\n\t\t\ttry {\n\t\t\t\tconst candidates: EncodedCandidate[] = [encodeCandidate(resized.get_bytes(), \"image/png\")];\n\t\t\t\tfor (const quality of jpegQualities) {\n\t\t\t\t\tcandidates.push(encodeCandidate(resized.get_bytes_jpeg(quality), \"image/jpeg\"));\n\t\t\t\t}\n\t\t\t\treturn candidates;\n\t\t\t} finally {\n\t\t\t\tresized.free();\n\t\t\t}\n\t\t}\n\n\t\tconst qualitySteps = Array.from(new Set([opts.jpegQuality, 85, 70, 55, 40]));\n\t\tlet currentWidth = targetWidth;\n\t\tlet currentHeight = targetHeight;\n\n\t\twhile (true) {\n\t\t\tconst candidates = tryEncodings(currentWidth, currentHeight, qualitySteps);\n\t\t\tfor (const candidate of candidates) {\n\t\t\t\tif (candidate.encodedSize < opts.maxBytes) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tdata: candidate.data,\n\t\t\t\t\t\tmimeType: candidate.mimeType,\n\t\t\t\t\t\toriginalWidth,\n\t\t\t\t\t\toriginalHeight,\n\t\t\t\t\t\twidth: currentWidth,\n\t\t\t\t\t\theight: currentHeight,\n\t\t\t\t\t\twasResized: true,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (currentWidth === 1 && currentHeight === 1) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tconst nextWidth = currentWidth === 1 ? 1 : Math.max(1, Math.floor(currentWidth * 0.75));\n\t\t\tconst nextHeight = currentHeight === 1 ? 1 : Math.max(1, Math.floor(currentHeight * 0.75));\n\t\t\tif (nextWidth === currentWidth && nextHeight === currentHeight) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcurrentWidth = nextWidth;\n\t\t\tcurrentHeight = nextHeight;\n\t\t}\n\n\t\treturn null;\n\t} catch {\n\t\treturn null;\n\t} finally {\n\t\tif (image) {\n\t\t\timage.free();\n\t\t}\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"image-resize-core.js","sourceRoot":"","sources":["../../src/utils/image-resize-core.ts"],"names":[],"mappings":"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","sourcesContent":["import { applyExifOrientation } from \"./exif-orientation.ts\";\nimport { loadPhoton } from \"./photon.ts\";\n\nexport interface ImageResizeOptions {\n\tmaxWidth?: number; // Default: 2000\n\tmaxHeight?: number; // Default: 2000\n\tmaxBytes?: number; // Default: 4.5MB of base64 payload (below Anthropic's 5MB limit)\n\tjpegQuality?: number; // Default: 80\n}\n\nexport interface ResizedImage {\n\tdata: string; // base64\n\tmimeType: string;\n\toriginalWidth: number;\n\toriginalHeight: number;\n\twidth: number;\n\theight: number;\n\twasResized: boolean;\n}\n\n/**\n * Tag on image resize worker replies. Node can post its own messages on the worker channel\n * (for example `{ \"watch:require\": [...] }` under `node --watch`), so replies must be identifiable.\n */\nexport const IMAGE_RESIZE_WORKER_RESPONSE_TYPE = \"pi:image-resize-response\";\n\nexport type ResizeImageWorkerResponse =\n\t| { type: typeof IMAGE_RESIZE_WORKER_RESPONSE_TYPE; result: ResizedImage | null }\n\t| { type: typeof IMAGE_RESIZE_WORKER_RESPONSE_TYPE; error: string };\n\n// 4.5MB of base64 payload. Provides headroom below Anthropic's 5MB limit.\nconst DEFAULT_MAX_BYTES = 4.5 * 1024 * 1024;\n\nconst DEFAULT_OPTIONS: Required<ImageResizeOptions> = {\n\tmaxWidth: 2000,\n\tmaxHeight: 2000,\n\tmaxBytes: DEFAULT_MAX_BYTES,\n\tjpegQuality: 80,\n};\n\ninterface EncodedCandidate {\n\tdata: string;\n\tencodedSize: number;\n\tmimeType: string;\n}\n\nfunction encodeCandidate(buffer: Uint8Array, mimeType: string): EncodedCandidate {\n\tconst data = Buffer.from(buffer).toString(\"base64\");\n\treturn {\n\t\tdata,\n\t\tencodedSize: Buffer.byteLength(data, \"utf-8\"),\n\t\tmimeType,\n\t};\n}\n\n/**\n * Resize an image to fit within the specified max dimensions and encoded file size.\n * Returns null if the image cannot be resized below maxBytes.\n *\n * Uses Photon (Rust/WASM) for image processing. If Photon is not available,\n * returns null.\n *\n * Strategy for staying under maxBytes:\n * 1. First resize to maxWidth/maxHeight\n * 2. Try both PNG and JPEG formats, pick the smaller one\n * 3. If still too large, try JPEG with decreasing quality\n * 4. If still too large, progressively reduce dimensions until 1x1\n */\nexport async function resizeImageInProcess(\n\tinputBytes: Uint8Array,\n\tmimeType: string,\n\toptions?: ImageResizeOptions,\n): Promise<ResizedImage | null> {\n\tconst opts = { ...DEFAULT_OPTIONS, ...options };\n\tconst inputBase64Size = Math.ceil(inputBytes.byteLength / 3) * 4;\n\n\tconst photon = await loadPhoton();\n\tif (!photon) {\n\t\treturn null;\n\t}\n\n\tlet image: ReturnType<typeof photon.PhotonImage.new_from_byteslice> | undefined;\n\ttry {\n\t\tconst rawImage = photon.PhotonImage.new_from_byteslice(inputBytes);\n\t\timage = applyExifOrientation(photon, rawImage, inputBytes);\n\t\tif (image !== rawImage) rawImage.free();\n\n\t\tconst originalWidth = image.get_width();\n\t\tconst originalHeight = image.get_height();\n\t\tconst format = mimeType.split(\"/\")[1] ?? \"png\";\n\n\t\t// Check if already within all limits (dimensions AND encoded size)\n\t\tif (originalWidth <= opts.maxWidth && originalHeight <= opts.maxHeight && inputBase64Size < opts.maxBytes) {\n\t\t\treturn {\n\t\t\t\tdata: Buffer.from(inputBytes).toString(\"base64\"),\n\t\t\t\tmimeType: mimeType || `image/${format}`,\n\t\t\t\toriginalWidth,\n\t\t\t\toriginalHeight,\n\t\t\t\twidth: originalWidth,\n\t\t\t\theight: originalHeight,\n\t\t\t\twasResized: false,\n\t\t\t};\n\t\t}\n\n\t\t// Calculate initial dimensions respecting max limits\n\t\tlet targetWidth = originalWidth;\n\t\tlet targetHeight = originalHeight;\n\n\t\tif (targetWidth > opts.maxWidth) {\n\t\t\ttargetHeight = Math.round((targetHeight * opts.maxWidth) / targetWidth);\n\t\t\ttargetWidth = opts.maxWidth;\n\t\t}\n\t\tif (targetHeight > opts.maxHeight) {\n\t\t\ttargetWidth = Math.round((targetWidth * opts.maxHeight) / targetHeight);\n\t\t\ttargetHeight = opts.maxHeight;\n\t\t}\n\n\t\tfunction tryEncodings(width: number, height: number, jpegQualities: number[]): EncodedCandidate[] {\n\t\t\tconst resized = photon!.resize(image!, width, height, photon!.SamplingFilter.Lanczos3);\n\n\t\t\ttry {\n\t\t\t\tconst candidates: EncodedCandidate[] = [encodeCandidate(resized.get_bytes(), \"image/png\")];\n\t\t\t\tfor (const quality of jpegQualities) {\n\t\t\t\t\tcandidates.push(encodeCandidate(resized.get_bytes_jpeg(quality), \"image/jpeg\"));\n\t\t\t\t}\n\t\t\t\treturn candidates;\n\t\t\t} finally {\n\t\t\t\tresized.free();\n\t\t\t}\n\t\t}\n\n\t\tconst qualitySteps = Array.from(new Set([opts.jpegQuality, 85, 70, 55, 40]));\n\t\tlet currentWidth = targetWidth;\n\t\tlet currentHeight = targetHeight;\n\n\t\twhile (true) {\n\t\t\tconst candidates = tryEncodings(currentWidth, currentHeight, qualitySteps);\n\t\t\tfor (const candidate of candidates) {\n\t\t\t\tif (candidate.encodedSize < opts.maxBytes) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tdata: candidate.data,\n\t\t\t\t\t\tmimeType: candidate.mimeType,\n\t\t\t\t\t\toriginalWidth,\n\t\t\t\t\t\toriginalHeight,\n\t\t\t\t\t\twidth: currentWidth,\n\t\t\t\t\t\theight: currentHeight,\n\t\t\t\t\t\twasResized: true,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (currentWidth === 1 && currentHeight === 1) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tconst nextWidth = currentWidth === 1 ? 1 : Math.max(1, Math.floor(currentWidth * 0.75));\n\t\t\tconst nextHeight = currentHeight === 1 ? 1 : Math.max(1, Math.floor(currentHeight * 0.75));\n\t\t\tif (nextWidth === currentWidth && nextHeight === currentHeight) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcurrentWidth = nextWidth;\n\t\t\tcurrentHeight = nextHeight;\n\t\t}\n\n\t\treturn null;\n\t} catch {\n\t\treturn null;\n\t} finally {\n\t\tif (image) {\n\t\t\timage.free();\n\t\t}\n\t}\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { parentPort } from "node:worker_threads";
|
|
2
|
-
import { resizeImageInProcess } from "./image-resize-core.js";
|
|
2
|
+
import { IMAGE_RESIZE_WORKER_RESPONSE_TYPE, resizeImageInProcess, } from "./image-resize-core.js";
|
|
3
3
|
function isResizeImageWorkerRequest(value) {
|
|
4
4
|
if (!value || typeof value !== "object")
|
|
5
5
|
return false;
|
|
@@ -17,11 +17,12 @@ port.once("message", (message) => {
|
|
|
17
17
|
throw new Error("Invalid image resize worker request");
|
|
18
18
|
}
|
|
19
19
|
const result = await resizeImageInProcess(message.inputBytes, message.mimeType, message.options);
|
|
20
|
-
const response = { result };
|
|
20
|
+
const response = { type: IMAGE_RESIZE_WORKER_RESPONSE_TYPE, result };
|
|
21
21
|
port.postMessage(response);
|
|
22
22
|
}
|
|
23
23
|
catch (error) {
|
|
24
24
|
const response = {
|
|
25
|
+
type: IMAGE_RESIZE_WORKER_RESPONSE_TYPE,
|
|
25
26
|
error: error instanceof Error ? error.message : String(error),
|
|
26
27
|
};
|
|
27
28
|
port.postMessage(response);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"image-resize-worker.js","sourceRoot":"","sources":["../../src/utils/image-resize-worker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,
|
|
1
|
+
{"version":3,"file":"image-resize-worker.js","sourceRoot":"","sources":["../../src/utils/image-resize-worker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,EACN,iCAAiC,EAGjC,oBAAoB,GACpB,MAAM,wBAAwB,CAAC;AAQhC,SAAS,0BAA0B,CAAC,KAAc;IACjD,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IACtD,MAAM,MAAM,GAAG,KAAgC,CAAC;IAChD,OAAO,MAAM,CAAC,UAAU,YAAY,UAAU,IAAI,OAAO,MAAM,CAAC,QAAQ,KAAK,QAAQ,CAAC;AACvF,CAAC;AAED,MAAM,IAAI,GAAG,UAAU,CAAC;AACxB,IAAI,CAAC,IAAI,EAAE,CAAC;IACX,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;AAC5D,CAAC;AAED,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,OAAgB,EAAE,EAAE;IACzC,KAAK,CAAC,KAAK,IAAI,EAAE;QAChB,IAAI,CAAC;YACJ,IAAI,CAAC,0BAA0B,CAAC,OAAO,CAAC,EAAE,CAAC;gBAC1C,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;YACxD,CAAC;YACD,MAAM,MAAM,GAAG,MAAM,oBAAoB,CAAC,OAAO,CAAC,UAAU,EAAE,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;YACjG,MAAM,QAAQ,GAA8B,EAAE,IAAI,EAAE,iCAAiC,EAAE,MAAM,EAAE,CAAC;YAChG,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;QAC5B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,MAAM,QAAQ,GAA8B;gBAC3C,IAAI,EAAE,iCAAiC;gBACvC,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;aAC7D,CAAC;YACF,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;QAC5B,CAAC;IACF,CAAC,CAAC,EAAE,CAAC;AACN,CAAC,CAAC,CAAC","sourcesContent":["import { parentPort } from \"node:worker_threads\";\nimport {\n\tIMAGE_RESIZE_WORKER_RESPONSE_TYPE,\n\ttype ImageResizeOptions,\n\ttype ResizeImageWorkerResponse,\n\tresizeImageInProcess,\n} from \"./image-resize-core.ts\";\n\ninterface ResizeImageWorkerRequest {\n\tinputBytes: Uint8Array;\n\tmimeType: string;\n\toptions?: ImageResizeOptions;\n}\n\nfunction isResizeImageWorkerRequest(value: unknown): value is ResizeImageWorkerRequest {\n\tif (!value || typeof value !== \"object\") return false;\n\tconst record = value as Record<string, unknown>;\n\treturn record.inputBytes instanceof Uint8Array && typeof record.mimeType === \"string\";\n}\n\nconst port = parentPort;\nif (!port) {\n\tthrow new Error(\"image resize worker requires parentPort\");\n}\n\nport.once(\"message\", (message: unknown) => {\n\tvoid (async () => {\n\t\ttry {\n\t\t\tif (!isResizeImageWorkerRequest(message)) {\n\t\t\t\tthrow new Error(\"Invalid image resize worker request\");\n\t\t\t}\n\t\t\tconst result = await resizeImageInProcess(message.inputBytes, message.mimeType, message.options);\n\t\t\tconst response: ResizeImageWorkerResponse = { type: IMAGE_RESIZE_WORKER_RESPONSE_TYPE, result };\n\t\t\tport.postMessage(response);\n\t\t} catch (error) {\n\t\t\tconst response: ResizeImageWorkerResponse = {\n\t\t\t\ttype: IMAGE_RESIZE_WORKER_RESPONSE_TYPE,\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t};\n\t\t\tport.postMessage(response);\n\t\t}\n\t})();\n});\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"image-resize.d.ts","sourceRoot":"","sources":["../../src/utils/image-resize.ts"],"names":[],"mappings":"AACA,OAAO,
|
|
1
|
+
{"version":3,"file":"image-resize.d.ts","sourceRoot":"","sources":["../../src/utils/image-resize.ts"],"names":[],"mappings":"AACA,OAAO,EAEN,KAAK,kBAAkB,EACvB,KAAK,YAAY,EAGjB,MAAM,wBAAwB,CAAC;AAEhC,YAAY,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAmE/E;;;;;;GAMG;AACH,wBAAsB,WAAW,CAChC,UAAU,EAAE,UAAU,EACtB,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,kBAAkB,GAC1B,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAqB9B;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,GAAG,SAAS,CAO5E"}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { Worker } from "node:worker_threads";
|
|
2
|
-
import { resizeImageInProcess } from "./image-resize-core.js";
|
|
2
|
+
import { IMAGE_RESIZE_WORKER_RESPONSE_TYPE, resizeImageInProcess, } from "./image-resize-core.js";
|
|
3
3
|
function toTransferableBytes(input) {
|
|
4
4
|
// Transfer detaches the buffer, so transfer a worker-owned copy and leave the
|
|
5
5
|
// caller's bytes intact.
|
|
6
6
|
return new Uint8Array(input);
|
|
7
7
|
}
|
|
8
8
|
function isResizeImageWorkerResponse(value) {
|
|
9
|
-
return value !== null && typeof value === "object";
|
|
9
|
+
return (value !== null && typeof value === "object" && "type" in value && value.type === IMAGE_RESIZE_WORKER_RESPONSE_TYPE);
|
|
10
10
|
}
|
|
11
11
|
function createResizeWorker(workerSpecifier) {
|
|
12
12
|
return new Worker(workerSpecifier);
|
|
@@ -29,16 +29,15 @@ async function resizeImageInWorker(workerSpecifier, inputBytes, mimeType, option
|
|
|
29
29
|
settled = true;
|
|
30
30
|
reject(error);
|
|
31
31
|
};
|
|
32
|
-
worker.
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
worker.on("message", (message) => {
|
|
33
|
+
// Ignore messages Node posts on the worker channel itself, such as
|
|
34
|
+
// `{ "watch:require": [...] }` under `node --watch` (nodejs/node#65044).
|
|
35
|
+
if (!isResizeImageWorkerResponse(message))
|
|
35
36
|
return;
|
|
36
|
-
|
|
37
|
-
if (message.error) {
|
|
37
|
+
if ("error" in message)
|
|
38
38
|
fail(new Error(message.error));
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
settle(message.result ?? null);
|
|
39
|
+
else
|
|
40
|
+
settle(message.result);
|
|
42
41
|
});
|
|
43
42
|
worker.once("error", fail);
|
|
44
43
|
worker.once("exit", (code) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"image-resize.js","sourceRoot":"","sources":["../../src/utils/image-resize.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,
|
|
1
|
+
{"version":3,"file":"image-resize.js","sourceRoot":"","sources":["../../src/utils/image-resize.ts"],"names":[],"mappings":"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","sourcesContent":["import { Worker } from \"node:worker_threads\";\nimport {\n\tIMAGE_RESIZE_WORKER_RESPONSE_TYPE,\n\ttype ImageResizeOptions,\n\ttype ResizedImage,\n\ttype ResizeImageWorkerResponse,\n\tresizeImageInProcess,\n} from \"./image-resize-core.ts\";\n\nexport type { ImageResizeOptions, ResizedImage } from \"./image-resize-core.ts\";\n\nfunction toTransferableBytes(input: Uint8Array): Uint8Array<ArrayBuffer> {\n\t// Transfer detaches the buffer, so transfer a worker-owned copy and leave the\n\t// caller's bytes intact.\n\treturn new Uint8Array(input);\n}\n\nfunction isResizeImageWorkerResponse(value: unknown): value is ResizeImageWorkerResponse {\n\treturn (\n\t\tvalue !== null && typeof value === \"object\" && \"type\" in value && value.type === IMAGE_RESIZE_WORKER_RESPONSE_TYPE\n\t);\n}\n\nfunction createResizeWorker(workerSpecifier: string | URL): Worker {\n\treturn new Worker(workerSpecifier);\n}\n\nasync function resizeImageInWorker(\n\tworkerSpecifier: string | URL,\n\tinputBytes: Uint8Array,\n\tmimeType: string,\n\toptions?: ImageResizeOptions,\n): Promise<ResizedImage | null> {\n\tconst worker = createResizeWorker(workerSpecifier);\n\ttry {\n\t\tconst inputBytesForWorker = toTransferableBytes(inputBytes);\n\t\treturn await new Promise<ResizedImage | null>((resolve, reject) => {\n\t\t\tlet settled = false;\n\t\t\tconst settle = (result: ResizedImage | null): void => {\n\t\t\t\tif (settled) return;\n\t\t\t\tsettled = true;\n\t\t\t\tresolve(result);\n\t\t\t};\n\t\t\tconst fail = (error: Error): void => {\n\t\t\t\tif (settled) return;\n\t\t\t\tsettled = true;\n\t\t\t\treject(error);\n\t\t\t};\n\n\t\t\tworker.on(\"message\", (message: unknown) => {\n\t\t\t\t// Ignore messages Node posts on the worker channel itself, such as\n\t\t\t\t// `{ \"watch:require\": [...] }` under `node --watch` (nodejs/node#65044).\n\t\t\t\tif (!isResizeImageWorkerResponse(message)) return;\n\t\t\t\tif (\"error\" in message) fail(new Error(message.error));\n\t\t\t\telse settle(message.result);\n\t\t\t});\n\t\t\tworker.once(\"error\", fail);\n\t\t\tworker.once(\"exit\", (code) => {\n\t\t\t\tif (!settled) {\n\t\t\t\t\tfail(new Error(`Image resize worker exited with code ${code}`));\n\t\t\t\t}\n\t\t\t});\n\t\t\tworker.postMessage(\n\t\t\t\t{\n\t\t\t\t\tinputBytes: inputBytesForWorker,\n\t\t\t\t\tmimeType,\n\t\t\t\t\toptions,\n\t\t\t\t},\n\t\t\t\t[inputBytesForWorker.buffer],\n\t\t\t);\n\t\t});\n\t} finally {\n\t\tvoid worker.terminate().catch(() => undefined);\n\t}\n}\n\n/**\n * Resize an image to fit within the specified max dimensions and encoded file size.\n * Runs Photon in a worker thread so WASM decoding, resizing, and encoding do not\n * block the TUI event loop. If the worker cannot be loaded (for example in some\n * Bun compiled executable layouts), fall back to in-process resizing so image\n * reads still work.\n */\nexport async function resizeImage(\n\tinputBytes: Uint8Array,\n\tmimeType: string,\n\toptions?: ImageResizeOptions,\n): Promise<ResizedImage | null> {\n\tconst isTypeScriptRuntime = import.meta.url.endsWith(\".ts\");\n\tconst workerUrl = new URL(\n\t\tisTypeScriptRuntime ? \"./image-resize-worker.ts\" : \"./image-resize-worker.js\",\n\t\timport.meta.url,\n\t);\n\n\t// Bun compiled executables resolve worker entrypoints by string path, not via\n\t// new URL(..., import.meta.url). Try the string path first under Bun so the\n\t// release binary uses the embedded worker instead of falling back in-process.\n\tif (typeof process.versions.bun === \"string\") {\n\t\ttry {\n\t\t\treturn await resizeImageInWorker(\"./src/utils/image-resize-worker.ts\", inputBytes, mimeType, options);\n\t\t} catch {}\n\t}\n\n\ttry {\n\t\treturn await resizeImageInWorker(workerUrl, inputBytes, mimeType, options);\n\t} catch {\n\t\treturn resizeImageInProcess(inputBytes, mimeType, options);\n\t}\n}\n\n/**\n * Format a dimension note for resized images.\n * This helps the model understand the coordinate mapping.\n */\nexport function formatDimensionNote(result: ResizedImage): string | undefined {\n\tif (!result.wasResized) {\n\t\treturn undefined;\n\t}\n\n\tconst scale = result.originalWidth / result.width;\n\treturn `[Image: original ${result.originalWidth}x${result.originalHeight}, displayed at ${result.width}x${result.height}. Multiply coordinates by ${scale.toFixed(2)} to map to original image.]`;\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"syntax-highlight.d.ts","sourceRoot":"","sources":["../../src/utils/syntax-highlight.ts"],"names":[],"mappings":"AAsDA,wBAAgB,yBAAyB,IAAI,OAAO,CAAC,IAAI,CAAC,CAezD;AAED,MAAM,MAAM,kBAAkB,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;AAC1D,MAAM,MAAM,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC,CAAC;AAEzE,MAAM,WAAW,gBAAgB;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,KAAK,CAAC,EAAE,cAAc,CAAC;CACvB;AAoED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,GAAE,cAAmB,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"syntax-highlight.d.ts","sourceRoot":"","sources":["../../src/utils/syntax-highlight.ts"],"names":[],"mappings":"AAsDA,wBAAgB,yBAAyB,IAAI,OAAO,CAAC,IAAI,CAAC,CAezD;AAED,MAAM,MAAM,kBAAkB,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;AAC1D,MAAM,MAAM,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC,CAAC;AAEzE,MAAM,WAAW,gBAAgB;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,KAAK,CAAC,EAAE,cAAc,CAAC;CACvB;AAoED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,GAAE,cAAmB,GAAG,MAAM,CAyDtF;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,GAAG,MAAM,CAQ9E;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEtD"}
|
|
@@ -126,7 +126,12 @@ export function renderHighlightedHtml(html, theme = {}) {
|
|
|
126
126
|
return;
|
|
127
127
|
}
|
|
128
128
|
const formatter = getActiveFormatter(scopes, theme);
|
|
129
|
-
output += formatter
|
|
129
|
+
output += formatter
|
|
130
|
+
? textBuffer
|
|
131
|
+
.split("\n")
|
|
132
|
+
.map((line) => (line ? formatter(line) : line))
|
|
133
|
+
.join("\n")
|
|
134
|
+
: textBuffer;
|
|
130
135
|
textBuffer = "";
|
|
131
136
|
};
|
|
132
137
|
let index = 0;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"syntax-highlight.js","sourceRoot":"","sources":["../../src/utils/syntax-highlight.ts"],"names":[],"mappings":"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","sourcesContent":["import hljs from \"highlight.js/lib/core.js\";\nimport bash from \"highlight.js/lib/languages/bash.js\";\nimport c from \"highlight.js/lib/languages/c.js\";\nimport cpp from \"highlight.js/lib/languages/cpp.js\";\nimport csharp from \"highlight.js/lib/languages/csharp.js\";\nimport dart from \"highlight.js/lib/languages/dart.js\";\nimport go from \"highlight.js/lib/languages/go.js\";\nimport groovy from \"highlight.js/lib/languages/groovy.js\";\nimport java from \"highlight.js/lib/languages/java.js\";\nimport javascript from \"highlight.js/lib/languages/javascript.js\";\nimport json from \"highlight.js/lib/languages/json.js\";\nimport kotlin from \"highlight.js/lib/languages/kotlin.js\";\nimport lua from \"highlight.js/lib/languages/lua.js\";\nimport nix from \"highlight.js/lib/languages/nix.js\";\nimport perl from \"highlight.js/lib/languages/perl.js\";\nimport php from \"highlight.js/lib/languages/php.js\";\nimport python from \"highlight.js/lib/languages/python.js\";\nimport ruby from \"highlight.js/lib/languages/ruby.js\";\nimport rust from \"highlight.js/lib/languages/rust.js\";\nimport scala from \"highlight.js/lib/languages/scala.js\";\nimport swift from \"highlight.js/lib/languages/swift.js\";\nimport typescript from \"highlight.js/lib/languages/typescript.js\";\nimport { decodeHtmlEntityAt } from \"./html.ts\";\n\nconst eagerLanguages = {\n\tpython,\n\tjava,\n\tgo,\n\tjavascript,\n\tjson,\n\tcpp,\n\ttypescript,\n\tphp,\n\truby,\n\tc,\n\tcsharp,\n\tnix,\n\tbash,\n\trust,\n\tscala,\n\tkotlin,\n\tswift,\n\tdart,\n\tgroovy,\n\tperl,\n\tlua,\n};\n\nfor (const [name, language] of Object.entries(eagerLanguages)) {\n\thljs.registerLanguage(name, language);\n}\n\nlet allLanguagesPromise: Promise<void> | undefined;\n\nexport function loadAllHighlightLanguages(): Promise<void> {\n\tif (!allLanguagesPromise) {\n\t\tallLanguagesPromise = new Promise((resolve) => {\n\t\t\tsetImmediate(() => {\n\t\t\t\tvoid import(\"highlight.js/lib/index.js\").then(\n\t\t\t\t\t() => resolve(),\n\t\t\t\t\t() => {\n\t\t\t\t\t\t// Eager languages and plaintext fallback remain available.\n\t\t\t\t\t\tresolve();\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t});\n\t\t});\n\t}\n\treturn allLanguagesPromise;\n}\n\nexport type HighlightFormatter = (text: string) => string;\nexport type HighlightTheme = Partial<Record<string, HighlightFormatter>>;\n\nexport interface HighlightOptions {\n\tlanguage?: string;\n\tignoreIllegals?: boolean;\n\tlanguageSubset?: string[];\n\ttheme?: HighlightTheme;\n}\n\nconst SPAN_CLOSE = \"</span>\";\nconst HIGHLIGHT_CLASS_PREFIX = \"hljs-\";\n\nfunction getScopeFromSpanTag(tag: string): string | undefined {\n\tconst match = /\\sclass\\s*=\\s*(?:\"([^\"]*)\"|'([^']*)')/.exec(tag);\n\tconst classValue = match?.[1] ?? match?.[2];\n\tif (!classValue) {\n\t\treturn undefined;\n\t}\n\n\tfor (const className of classValue.split(/\\s+/)) {\n\t\tif (className.startsWith(HIGHLIGHT_CLASS_PREFIX)) {\n\t\t\treturn className.slice(HIGHLIGHT_CLASS_PREFIX.length);\n\t\t}\n\t}\n\n\treturn undefined;\n}\n\nfunction getScopeFormatter(scope: string, theme: HighlightTheme): HighlightFormatter | undefined {\n\tconst exact = theme[scope];\n\tif (exact) {\n\t\treturn exact;\n\t}\n\n\tconst dotIndex = scope.indexOf(\".\");\n\tif (dotIndex !== -1) {\n\t\tconst prefixFormatter = theme[scope.slice(0, dotIndex)];\n\t\tif (prefixFormatter) {\n\t\t\treturn prefixFormatter;\n\t\t}\n\t}\n\n\tconst dashIndex = scope.indexOf(\"-\");\n\tif (dashIndex !== -1) {\n\t\tconst prefixFormatter = theme[scope.slice(0, dashIndex)];\n\t\tif (prefixFormatter) {\n\t\t\treturn prefixFormatter;\n\t\t}\n\t}\n\n\treturn undefined;\n}\n\nfunction getActiveFormatter(scopes: Array<string | undefined>, theme: HighlightTheme): HighlightFormatter | undefined {\n\tfor (let i = scopes.length - 1; i >= 0; i--) {\n\t\tconst scope = scopes[i];\n\t\tif (!scope) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst formatter = getScopeFormatter(scope, theme);\n\t\tif (formatter) {\n\t\t\treturn formatter;\n\t\t}\n\t}\n\treturn theme.default;\n}\n\nfunction isSpanOpenTagStart(html: string, index: number): boolean {\n\tif (!html.startsWith(\"<span\", index)) {\n\t\treturn false;\n\t}\n\tconst nextChar = html[index + \"<span\".length];\n\treturn nextChar === \">\" || nextChar === \" \" || nextChar === \"\\t\" || nextChar === \"\\n\" || nextChar === \"\\r\";\n}\n\nexport function renderHighlightedHtml(html: string, theme: HighlightTheme = {}): string {\n\tlet output = \"\";\n\tlet textBuffer = \"\";\n\tconst scopes: Array<string | undefined> = [];\n\n\tconst flushText = () => {\n\t\tif (!textBuffer) {\n\t\t\treturn;\n\t\t}\n\t\tconst formatter = getActiveFormatter(scopes, theme);\n\t\toutput += formatter ? formatter(textBuffer) : textBuffer;\n\t\ttextBuffer = \"\";\n\t};\n\n\tlet index = 0;\n\twhile (index < html.length) {\n\t\tif (isSpanOpenTagStart(html, index)) {\n\t\t\tconst tagEndIndex = html.indexOf(\">\", index + 5);\n\t\t\tif (tagEndIndex !== -1) {\n\t\t\t\tflushText();\n\t\t\t\tconst tag = html.slice(index, tagEndIndex + 1);\n\t\t\t\tconst scope = getScopeFromSpanTag(tag);\n\t\t\t\tscopes.push(scope);\n\t\t\t\tindex = tagEndIndex + 1;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\tif (html.startsWith(SPAN_CLOSE, index)) {\n\t\t\tflushText();\n\t\t\tif (scopes.length > 0) {\n\t\t\t\tscopes.pop();\n\t\t\t}\n\t\t\tindex += SPAN_CLOSE.length;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (html[index] === \"&\") {\n\t\t\tconst decoded = decodeHtmlEntityAt(html, index);\n\t\t\tif (decoded) {\n\t\t\t\ttextBuffer += decoded.text;\n\t\t\t\tindex += decoded.length;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\ttextBuffer += html[index];\n\t\tindex++;\n\t}\n\n\tflushText();\n\treturn output;\n}\n\nexport function highlight(code: string, options: HighlightOptions = {}): string {\n\tconst html = options.language\n\t\t? hljs.highlight(code, {\n\t\t\t\tlanguage: options.language,\n\t\t\t\tignoreIllegals: options.ignoreIllegals,\n\t\t\t}).value\n\t\t: hljs.highlightAuto(code, options.languageSubset).value;\n\treturn renderHighlightedHtml(html, options.theme);\n}\n\nexport function supportsLanguage(name: string): boolean {\n\treturn hljs.getLanguage(name) !== undefined;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"syntax-highlight.js","sourceRoot":"","sources":["../../src/utils/syntax-highlight.ts"],"names":[],"mappings":"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","sourcesContent":["import hljs from \"highlight.js/lib/core.js\";\nimport bash from \"highlight.js/lib/languages/bash.js\";\nimport c from \"highlight.js/lib/languages/c.js\";\nimport cpp from \"highlight.js/lib/languages/cpp.js\";\nimport csharp from \"highlight.js/lib/languages/csharp.js\";\nimport dart from \"highlight.js/lib/languages/dart.js\";\nimport go from \"highlight.js/lib/languages/go.js\";\nimport groovy from \"highlight.js/lib/languages/groovy.js\";\nimport java from \"highlight.js/lib/languages/java.js\";\nimport javascript from \"highlight.js/lib/languages/javascript.js\";\nimport json from \"highlight.js/lib/languages/json.js\";\nimport kotlin from \"highlight.js/lib/languages/kotlin.js\";\nimport lua from \"highlight.js/lib/languages/lua.js\";\nimport nix from \"highlight.js/lib/languages/nix.js\";\nimport perl from \"highlight.js/lib/languages/perl.js\";\nimport php from \"highlight.js/lib/languages/php.js\";\nimport python from \"highlight.js/lib/languages/python.js\";\nimport ruby from \"highlight.js/lib/languages/ruby.js\";\nimport rust from \"highlight.js/lib/languages/rust.js\";\nimport scala from \"highlight.js/lib/languages/scala.js\";\nimport swift from \"highlight.js/lib/languages/swift.js\";\nimport typescript from \"highlight.js/lib/languages/typescript.js\";\nimport { decodeHtmlEntityAt } from \"./html.ts\";\n\nconst eagerLanguages = {\n\tpython,\n\tjava,\n\tgo,\n\tjavascript,\n\tjson,\n\tcpp,\n\ttypescript,\n\tphp,\n\truby,\n\tc,\n\tcsharp,\n\tnix,\n\tbash,\n\trust,\n\tscala,\n\tkotlin,\n\tswift,\n\tdart,\n\tgroovy,\n\tperl,\n\tlua,\n};\n\nfor (const [name, language] of Object.entries(eagerLanguages)) {\n\thljs.registerLanguage(name, language);\n}\n\nlet allLanguagesPromise: Promise<void> | undefined;\n\nexport function loadAllHighlightLanguages(): Promise<void> {\n\tif (!allLanguagesPromise) {\n\t\tallLanguagesPromise = new Promise((resolve) => {\n\t\t\tsetImmediate(() => {\n\t\t\t\tvoid import(\"highlight.js/lib/index.js\").then(\n\t\t\t\t\t() => resolve(),\n\t\t\t\t\t() => {\n\t\t\t\t\t\t// Eager languages and plaintext fallback remain available.\n\t\t\t\t\t\tresolve();\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t});\n\t\t});\n\t}\n\treturn allLanguagesPromise;\n}\n\nexport type HighlightFormatter = (text: string) => string;\nexport type HighlightTheme = Partial<Record<string, HighlightFormatter>>;\n\nexport interface HighlightOptions {\n\tlanguage?: string;\n\tignoreIllegals?: boolean;\n\tlanguageSubset?: string[];\n\ttheme?: HighlightTheme;\n}\n\nconst SPAN_CLOSE = \"</span>\";\nconst HIGHLIGHT_CLASS_PREFIX = \"hljs-\";\n\nfunction getScopeFromSpanTag(tag: string): string | undefined {\n\tconst match = /\\sclass\\s*=\\s*(?:\"([^\"]*)\"|'([^']*)')/.exec(tag);\n\tconst classValue = match?.[1] ?? match?.[2];\n\tif (!classValue) {\n\t\treturn undefined;\n\t}\n\n\tfor (const className of classValue.split(/\\s+/)) {\n\t\tif (className.startsWith(HIGHLIGHT_CLASS_PREFIX)) {\n\t\t\treturn className.slice(HIGHLIGHT_CLASS_PREFIX.length);\n\t\t}\n\t}\n\n\treturn undefined;\n}\n\nfunction getScopeFormatter(scope: string, theme: HighlightTheme): HighlightFormatter | undefined {\n\tconst exact = theme[scope];\n\tif (exact) {\n\t\treturn exact;\n\t}\n\n\tconst dotIndex = scope.indexOf(\".\");\n\tif (dotIndex !== -1) {\n\t\tconst prefixFormatter = theme[scope.slice(0, dotIndex)];\n\t\tif (prefixFormatter) {\n\t\t\treturn prefixFormatter;\n\t\t}\n\t}\n\n\tconst dashIndex = scope.indexOf(\"-\");\n\tif (dashIndex !== -1) {\n\t\tconst prefixFormatter = theme[scope.slice(0, dashIndex)];\n\t\tif (prefixFormatter) {\n\t\t\treturn prefixFormatter;\n\t\t}\n\t}\n\n\treturn undefined;\n}\n\nfunction getActiveFormatter(scopes: Array<string | undefined>, theme: HighlightTheme): HighlightFormatter | undefined {\n\tfor (let i = scopes.length - 1; i >= 0; i--) {\n\t\tconst scope = scopes[i];\n\t\tif (!scope) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst formatter = getScopeFormatter(scope, theme);\n\t\tif (formatter) {\n\t\t\treturn formatter;\n\t\t}\n\t}\n\treturn theme.default;\n}\n\nfunction isSpanOpenTagStart(html: string, index: number): boolean {\n\tif (!html.startsWith(\"<span\", index)) {\n\t\treturn false;\n\t}\n\tconst nextChar = html[index + \"<span\".length];\n\treturn nextChar === \">\" || nextChar === \" \" || nextChar === \"\\t\" || nextChar === \"\\n\" || nextChar === \"\\r\";\n}\n\nexport function renderHighlightedHtml(html: string, theme: HighlightTheme = {}): string {\n\tlet output = \"\";\n\tlet textBuffer = \"\";\n\tconst scopes: Array<string | undefined> = [];\n\n\tconst flushText = () => {\n\t\tif (!textBuffer) {\n\t\t\treturn;\n\t\t}\n\t\tconst formatter = getActiveFormatter(scopes, theme);\n\t\toutput += formatter\n\t\t\t? textBuffer\n\t\t\t\t\t.split(\"\\n\")\n\t\t\t\t\t.map((line) => (line ? formatter(line) : line))\n\t\t\t\t\t.join(\"\\n\")\n\t\t\t: textBuffer;\n\t\ttextBuffer = \"\";\n\t};\n\n\tlet index = 0;\n\twhile (index < html.length) {\n\t\tif (isSpanOpenTagStart(html, index)) {\n\t\t\tconst tagEndIndex = html.indexOf(\">\", index + 5);\n\t\t\tif (tagEndIndex !== -1) {\n\t\t\t\tflushText();\n\t\t\t\tconst tag = html.slice(index, tagEndIndex + 1);\n\t\t\t\tconst scope = getScopeFromSpanTag(tag);\n\t\t\t\tscopes.push(scope);\n\t\t\t\tindex = tagEndIndex + 1;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\tif (html.startsWith(SPAN_CLOSE, index)) {\n\t\t\tflushText();\n\t\t\tif (scopes.length > 0) {\n\t\t\t\tscopes.pop();\n\t\t\t}\n\t\t\tindex += SPAN_CLOSE.length;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (html[index] === \"&\") {\n\t\t\tconst decoded = decodeHtmlEntityAt(html, index);\n\t\t\tif (decoded) {\n\t\t\t\ttextBuffer += decoded.text;\n\t\t\t\tindex += decoded.length;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\ttextBuffer += html[index];\n\t\tindex++;\n\t}\n\n\tflushText();\n\treturn output;\n}\n\nexport function highlight(code: string, options: HighlightOptions = {}): string {\n\tconst html = options.language\n\t\t? hljs.highlight(code, {\n\t\t\t\tlanguage: options.language,\n\t\t\t\tignoreIllegals: options.ignoreIllegals,\n\t\t\t}).value\n\t\t: hljs.highlightAuto(code, options.languageSubset).value;\n\treturn renderHighlightedHtml(html, options.theme);\n}\n\nexport function supportsLanguage(name: string): boolean {\n\treturn hljs.getLanguage(name) !== undefined;\n}\n"]}
|
package/docs/cli.md
CHANGED
|
@@ -117,15 +117,25 @@ pi --tools read,grep,find,ls --print "Review this project"
|
|
|
117
117
|
See [Settings](settings.md#tools) for configuring the default tool selection.
|
|
118
118
|
|
|
119
119
|
- `-t`, `--tools <list>`<br>
|
|
120
|
-
Replaces the default selection with a comma-separated allowlist of built-in, extension, or custom tools.
|
|
120
|
+
Replaces the default selection with a comma-separated allowlist of built-in, extension, or custom tools. Entries are tool names or patterns where `*` matches any characters. MCP tools are kept unless an entry starts with `mcp__` (see [MCP tools](#mcp-tools)). A list of only `+name` and `-name` entries is not an allowlist; it changes the default selection instead.
|
|
121
121
|
- `-xt`, `--exclude-tools <list>`<br>
|
|
122
|
-
Disables comma-separated tool names after all other selection options.
|
|
122
|
+
Disables comma-separated tool names or patterns after all other selection options, MCP tools included.
|
|
123
123
|
- `-nbt`, `--no-builtin-tools`<br>
|
|
124
124
|
Disables default built-in tools while retaining extension and custom tools.
|
|
125
125
|
- `-nt`, `--no-tools`<br>
|
|
126
|
-
Starts with all built-in, extension, and
|
|
126
|
+
Starts with all built-in, extension, custom, and MCP tools disabled.
|
|
127
127
|
|
|
128
|
-
Default enabled tools are `read`, `bash`, `edit`, and `write`, unless `defaultTools` changes them. `--tools` replaces the whole selection, so name every tool you want
|
|
128
|
+
Default enabled tools are `read`, `bash`, `edit`, and `write`, unless `defaultTools` changes them. `--tools` with plain names replaces the whole selection, so name every tool you want. Like `defaultTools`, it also accepts a list of only `+name` and `-name` entries, which adds tools to or removes them from the default selection: `pi --tools +codemode,-write` keeps the other default tools, enables `codemode`, and disables `write`. These entries take exact tool names, not `*` patterns; use `--exclude-tools` to disable tools by pattern. Plain names and `+name`/`-name` entries cannot be mixed. `/reload` enables tools newly added to `defaultTools`, but a tool removed with `-name` stays removed.
|
|
129
|
+
|
|
130
|
+
<a id="mcp-tools"></a>
|
|
131
|
+
|
|
132
|
+
`--tools` selects the tools declared to the model. It does not remove MCP tools, whose reach is set by their [exposure](mcp.md#control-tool-exposure): `pi --tools read,codemode` keeps every MCP tool callable from codemode scripts. An MCP tool that no entry names or matches is never declared directly, whatever its exposure; only `tool_search`, if listed, can load it. Once an entry starts with `mcp__`, `--tools` filters MCP tools too, so this keeps only the tools of the `radius` server:
|
|
133
|
+
|
|
134
|
+
```sh
|
|
135
|
+
pi --tools read,bash,codemode,'mcp__radius__*'
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
The MCP resource tools (`list_mcp_resources`, `list_mcp_resource_templates`, `read_mcp_resource`) count as MCP tools. To remove MCP tools, use `--exclude-tools 'mcp__*'` or [`--no-mcp`](#resource-options).
|
|
129
139
|
|
|
130
140
|
| Built-in | Purpose |
|
|
131
141
|
|---|---|
|
|
@@ -155,10 +165,10 @@ To turn on `codemode` for every session, add it to the default tools in `~/.pi/a
|
|
|
155
165
|
}
|
|
156
166
|
```
|
|
157
167
|
|
|
158
|
-
This keeps `read`, `bash`, `edit`, and `write` and adds `codemode`. For one invocation,
|
|
168
|
+
This keeps `read`, `bash`, `edit`, and `write` and adds `codemode`. For one invocation, add it with `--tools`:
|
|
159
169
|
|
|
160
170
|
```sh
|
|
161
|
-
pi --tools
|
|
171
|
+
pi --tools +codemode
|
|
162
172
|
```
|
|
163
173
|
|
|
164
174
|
Codemode is useful without MCP: scripts can run several tool calls in parallel, filter large output before it reaches the model, call classifier models such as TypeSafe's Jev through `models.classify()` (see [Classifier models](models.md#use-classifier-models)), and generate images through `models.generateImages()` (see [Image models](models.md#use-image-models)).
|
|
@@ -185,6 +195,8 @@ See [Configuration](configuration.md) for conventional directories and project t
|
|
|
185
195
|
Loads an extension file or directory, or a built-in extension such as `builtin:mcp`, and is repeatable.
|
|
186
196
|
- `-ne`, `--no-extensions`<br>
|
|
187
197
|
Disables discovered, configured, and built-in extensions. Explicit `-e` paths still load, so `pi -ne -e builtin:mcp` keeps only the built-in MCP support.
|
|
198
|
+
- `--no-mcp`<br>
|
|
199
|
+
Disables the built-in MCP support for this run: no servers connect, and there are no MCP tools or `/mcp`. It does not affect an extension that replaces the built-in MCP support.
|
|
188
200
|
- `--skill <path>`<br>
|
|
189
201
|
Loads a skill file or directory and is repeatable.
|
|
190
202
|
- `-ns`, `--no-skills`<br>
|
package/docs/codemode.md
CHANGED
|
@@ -15,7 +15,7 @@ A script may start with an options line:
|
|
|
15
15
|
- `max_output_tokens` (default 10000) limits the output. Longer output keeps its start and end, and the full text is written to a temp file whose path is included in the result. A script fails when its output passes 16777216 characters of text and base64 image data or 100000 `text()`, `image()`, and `console` calls; write large data to a file with a tool instead.
|
|
16
16
|
- `timeout_ms` is a hard deadline for the whole script. It is unset by default. Image generation can take minutes, so do not set a short deadline for scripts that generate images.
|
|
17
17
|
|
|
18
|
-
The result starts with `Script completed` or `Script failed`, the wall time, and the output. A failed script keeps its partial output, followed by `Script error:` and the error. Tool calls are real: calls made before a failure are not undone. Calls still running when the script ends are cancelled, and unawaited promises are discarded.
|
|
18
|
+
The result starts with `Script completed` or `Script failed`, the wall time, and the output. Text and image items appear in order, each on its own line. When the output has more than one text item (from `text()` or `return`), each starts with a `==> text N/M <==` line. `console` calls follow in one `<console_output>` block with one line per call. A failed script keeps its partial output, followed by `Script error:` and the error. Tool calls are real: calls made before a failure are not undone. Calls still running when the script ends are cancelled, and unawaited promises are discarded.
|
|
19
19
|
|
|
20
20
|
## Globals
|
|
21
21
|
|
|
@@ -24,7 +24,7 @@ The result starts with `Script completed` or `Script failed`, the wall time, and
|
|
|
24
24
|
| `tools.<name>(args)` | Call a tool. See [Call tools](#call-tools). |
|
|
25
25
|
| `text(value)` | Add a text item to the output. Strings are added as is, other values as JSON. |
|
|
26
26
|
| `image(value)` | Add an image to the output: a base64 `data:` URL, an `{ image_url }` object, or an image block `{ type: "image", data, mimeType }` such as those returned by MCP tools and `models.generateImages()`. Remote URLs are not supported. PNG, JPEG, GIF, and WebP are accepted. Each image is also saved to a temp file, and the result names the path before the image. |
|
|
27
|
-
| `console.log(...)` |
|
|
27
|
+
| `console.log(...)` | Add a line to the `<console_output>` block after the other output. Arguments are joined with spaces; `info`, `warn`, `error`, and `debug` do the same. |
|
|
28
28
|
| `return value` | A top-level `return` adds the value like `text()`. |
|
|
29
29
|
| `exit()` | End the script successfully. |
|
|
30
30
|
| `store(key, value)` / `load(key)` | Keep small JSON values across `codemode` calls. See [Store values](#store-values). |
|
|
@@ -42,13 +42,14 @@ What a call resolves to depends on the tool:
|
|
|
42
42
|
|
|
43
43
|
- Tools with an output schema resolve to a structured value. `bash` resolves to `{ output, truncated, full_output_path?, exit_code, wall_time_seconds }`, also for non-zero exit codes. Its `output` is not limited to the 2000 lines or 50KB the model sees: it holds up to 1 MiB, and longer output keeps its first and last 512 KiB around an omission marker, with `truncated` set and the full output in `full_output_path`.
|
|
44
44
|
- MCP tools resolve to their `CallToolResult`, including `isError` and `structuredContent`.
|
|
45
|
-
-
|
|
45
|
+
- `read` resolves to the file's text, or for an image to an image block `{ type: "image", data, mimeType, note }` that `image()` shows. `data` is the base64 image the model would see and `note` the text that goes with it, such as resize hints.
|
|
46
|
+
- Other tools, such as `edit` and `write`, resolve to their text output.
|
|
46
47
|
|
|
47
48
|
A call that fails, is blocked, or gets invalid arguments rejects with an `Error` that carries the tool's error text. Use `Promise.allSettled()` to keep the results of the calls that succeed.
|
|
48
49
|
|
|
49
50
|
The `codemode` description lists tools with their TypeScript declarations, grouped by namespace (for example one MCP server). Tools with `deferred` exposure, which includes MCP tools with the default `codemode` exposure, are not listed, so the description stays the same while MCP servers connect. Listed declarations share a budget of 3000 estimated tokens (`codemode.inlineBudget` in [settings](settings.md#tools)). Scripts find the other tools with `searchTools()`, `describeTool()`, `describeNamespace()`, or by filtering `ALL_TOOLS`.
|
|
50
51
|
|
|
51
|
-
While `codemode` is active, `codemode.mode` in [settings](settings.md#tools) decides how the other tools are presented. With `on` (default) declared tools stay declared, and their descriptions say how to call them from scripts. With `only` they are hidden from the model and listed in the `codemode` description instead, so the model calls them through scripts.
|
|
52
|
+
While `codemode` is active, `codemode.mode` in [settings](settings.md#tools) decides how the other tools are presented. With `on` (default) declared tools stay declared, and their descriptions say how to call them from scripts. With `only` they are hidden from the model and listed in the `codemode` description instead, so the model calls them through scripts. Tool declarations in the `codemode` description, `describeTool()`, and `ALL_TOOLS` carry the tools' prompt guidelines, since the system prompt rules only cover declared tools.
|
|
52
53
|
|
|
53
54
|
## Store values
|
|
54
55
|
|
|
@@ -58,7 +59,7 @@ The store is for small state such as IDs, cursors, or summaries. One value may h
|
|
|
58
59
|
|
|
59
60
|
## Models
|
|
60
61
|
|
|
61
|
-
`models` reaches the model catalog and runs non-LLM models with the session's credentials: classifiers, which answer typed questions about JSON state, and image models, which generate images. Chat models are listed but cannot be run from scripts. Which classifier and image models exist is described in [Use classifier models](models.md#use-classifier-models) and [Use image models](models.md#use-image-models).
|
|
62
|
+
`models` reaches the model catalog and runs non-LLM models with the session's credentials: classifiers, which answer typed questions about JSON state and, for some models, images, and image models, which generate images. Chat models are listed but cannot be run from scripts. Which classifier and image models exist is described in [Use classifier models](models.md#use-classifier-models) and [Use image models](models.md#use-image-models).
|
|
62
63
|
|
|
63
64
|
```ts
|
|
64
65
|
type ModelType = "chat" | "image" | "classifier";
|
|
@@ -99,6 +100,8 @@ Model IDs differ between providers, for example `typesafe/jev-latest` and `openr
|
|
|
99
100
|
interface ClassifierContext {
|
|
100
101
|
/** The data to classify. */
|
|
101
102
|
state: Record<string, unknown>;
|
|
103
|
+
/** Images judged together with `state`. Only models whose `input` includes "image" accept them. */
|
|
104
|
+
images?: { type: "image"; data: string; mimeType: string }[];
|
|
102
105
|
/** Questions by ID. One call answers all of them. */
|
|
103
106
|
questions: Record<string, ClassifierQuestion>;
|
|
104
107
|
}
|
|
@@ -162,6 +165,24 @@ return results.map((result, i) =>
|
|
|
162
165
|
);
|
|
163
166
|
```
|
|
164
167
|
|
|
168
|
+
Classifiers whose `input` includes `"image"` also judge images. `tools.read()` returns an image file as an image block that `images` accepts. Other classifiers return an error result when `images` is not empty.
|
|
169
|
+
|
|
170
|
+
```js
|
|
171
|
+
const luna = await models.getModelOfType("classifier", "openai", "gpt-6-luna");
|
|
172
|
+
const photo = await tools.read({ path: "screenshot.png" });
|
|
173
|
+
const result = await models.classify(luna, {
|
|
174
|
+
state: { task: "Settings page redesign" },
|
|
175
|
+
images: [photo],
|
|
176
|
+
questions: {
|
|
177
|
+
broken: {
|
|
178
|
+
type: "bool",
|
|
179
|
+
instructions: "Does the screenshot show a broken layout?",
|
|
180
|
+
criteria: { true: "Overlapping, cut-off, or misaligned elements", false: "Clean layout" },
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
});
|
|
184
|
+
```
|
|
185
|
+
|
|
165
186
|
### Generate images
|
|
166
187
|
|
|
167
188
|
```ts
|
|
@@ -89,6 +89,7 @@ These variables are read by Pi itself:
|
|
|
89
89
|
| `PI_RADIUS_GATEWAY` | Override the Radius gateway origin used by `/bug` uploads and Radius relay connections |
|
|
90
90
|
| `PI_HARDWARE_CURSOR` | Set to `1` to show the hardware cursor; see [Terminal setup](terminal-setup.md) |
|
|
91
91
|
| `PI_HYPERLINKS` | Override OSC 8 hyperlink detection with `1`, `0`, or `auto` |
|
|
92
|
+
| `PI_PROGRAM_STATUS` | Override OSC 7501 program status detection: `1` always reports, `0` never reports; otherwise Pi reports only after the terminal confirms support. See [Terminal setup](terminal-setup.md#program-status) |
|
|
92
93
|
| `PI_IMAGE_PROTOCOL` | Override inline image detection with `kitty`, `iterm2`, `none`, or `auto` |
|
|
93
94
|
| `PI_TRUE_COLOR` | Override truecolor detection with `1`, `0`, or `auto` |
|
|
94
95
|
| `PI_TUI_ESC_TIMEOUT` | How long to wait after a lone ESC before treating it as Escape, in milliseconds; defaults to `100` over SSH and `10` otherwise. Increase if Alt-key input is misread as Escape |
|
package/docs/extensions.md
CHANGED
|
@@ -177,7 +177,7 @@ pi.on("tool_call", async (event, ctx) => {
|
|
|
177
177
|
});
|
|
178
178
|
```
|
|
179
179
|
|
|
180
|
-
A tool that orchestrates other tools can adjust what the model sees while it is active with `prepareLoadout(loadout)`. It runs whenever the active tools change and receives the declared tools, the callable tools, and every registered tool with its exposure and
|
|
180
|
+
A tool that orchestrates other tools can adjust what the model sees while it is active with `prepareLoadout(loadout)`. It runs whenever the active tools change and receives the declared tools, the callable tools, and every registered tool with its exposure, namespace, and prompt guidelines. It returns replacement `descriptions` for declared tools (including its own) and `hiddenDeclarations`: active tools whose declarations requests leave out while they stay active and callable. The default system prompt leaves hidden tools out of its tool list and rules, and names no tool in the skills hint when the file reader is hidden, so the orchestrating tool should present their guidelines itself. `codemode` uses only this hook, `exposure`, and `ctx.executeTool()`, so another tool can implement the same behavior under a different name.
|
|
181
181
|
|
|
182
182
|
### Activate tools dynamically
|
|
183
183
|
|
package/docs/json.md
CHANGED
|
@@ -42,7 +42,7 @@ A basic run produces records like these:
|
|
|
42
42
|
{"type":"message_end","message":{"role":"assistant","...":"..."}}
|
|
43
43
|
{"type":"turn_end","message":{"role":"assistant","...":"..."},"toolResults":[]}
|
|
44
44
|
{"type":"agent_end","messages":[{"...":"..."}],"willRetry":false}
|
|
45
|
-
{"type":"agent_settled"}
|
|
45
|
+
{"type":"agent_settled","aborted":false}
|
|
46
46
|
```
|
|
47
47
|
|
|
48
48
|
`agent_end` closes one low-level agent run. Automatic retry, overflow recovery, compaction retry, steering, or follow-up work can still continue. `agent_settled` means Pi has no remaining automatic work for that session-level run.
|
|
@@ -53,7 +53,7 @@ A basic run produces records like these:
|
|
|
53
53
|
|---|---|---|
|
|
54
54
|
| `agent_start` | None | A low-level agent run started. |
|
|
55
55
|
| `agent_end` | `messages`, `willRetry` | That low-level run ended. `messages` contains messages generated by the run. |
|
|
56
|
-
| `agent_settled` |
|
|
56
|
+
| `agent_settled` | `aborted` | Pi will not continue automatically through retries, compaction recovery, or queued messages. `aborted` is `true` when the run ended because it was aborted. |
|
|
57
57
|
| `turn_start` | None | One assistant turn started. |
|
|
58
58
|
| `turn_end` | `message`, `toolResults` | One assistant response and its resulting tool calls finished. |
|
|
59
59
|
|
|
@@ -104,7 +104,7 @@ The top-level `usage` is the latest cumulative provider-reported usage for the a
|
|
|
104
104
|
|---|---|---|
|
|
105
105
|
| `tool_execution_start` | `toolCallId`, `toolName`, `args` | Tool execution started. |
|
|
106
106
|
| `tool_execution_update` | `toolCallId`, `toolName`, `args`, `partialResult` | The tool reported a partial result. |
|
|
107
|
-
| `tool_execution_end` | `toolCallId`, `toolName`, `result`, `isError` | Tool execution finished. |
|
|
107
|
+
| `tool_execution_end` | `toolCallId`, `toolName`, `result`, `isError`, `durationMs` | Tool execution finished. `durationMs` is how long the tool's `execute()` took, measured with a monotonic clock; absent when the tool did not run. |
|
|
108
108
|
|
|
109
109
|
Use `toolCallId` to correlate the lifecycle. `partialResult` is the latest partial result supplied by the tool. Whether it replaces or extends an earlier update depends on that tool's result contract.
|
|
110
110
|
|
package/docs/llama-cpp.md
CHANGED
|
@@ -88,7 +88,14 @@ If the router disconnects, `/llama` shows **Retry** and **Close**. Retry reconne
|
|
|
88
88
|
|
|
89
89
|
## Classification
|
|
90
90
|
|
|
91
|
-
|
|
91
|
+
Classifier models answer typed `choice`, `bool`, and `score` questions about JSON state, like TypeSafe's Jev models. The model reaches them from [`codemode`](cli.md#enable-codemode) scripts, and extensions through `ctx.modelRegistry.classify()`; see [Classifier models](models.md#use-classifier-models). Pi lists llama.cpp models as classifiers in two ways:
|
|
92
|
+
|
|
93
|
+
- **Decision models** such as [Julia-1, Laya, Kev, lev, and OpenJev](https://huggingface.co/collections/ggml-org/decision-models-6abf80cca3c83f127060a769) answer natively through llama.cpp's `/v1/systemone` endpoint. They appear only as classifiers, with the `typesafe-system-one` API, and not in `/model`.
|
|
94
|
+
- **Chat models** are also listed as classifiers with the same ID and the `llama-cpp-classify` API, which reads answers from next-token probabilities as described below.
|
|
95
|
+
|
|
96
|
+
llama.cpp 0.6.0 and later report decision models in the router's model list: their `architecture.output_modalities` contains `decisions`. The router reads this from the GGUF metadata without loading the model, so Pi recognizes unloaded and sleeping decision models too. Older llama.cpp builds do not report it, and Pi lists their decision models as chat models.
|
|
97
|
+
|
|
98
|
+
### Chat models as classifiers
|
|
92
99
|
|
|
93
100
|
The model does not generate an answer. Each question becomes one chat prompt: the state, every question of the request, the state again, and then the question with its answers under single-token labels. Labels are letters for a choice (up to 62 options), `Yes`/`No` for a bool, and digits for a score (up to 10 levels). The second copy of the state is read with the questions in view, which improved accuracy on JevBench with small models. Pi reads the probabilities of the labels as the next token and normalizes them. A choice returns every option's probability and a confidence of `(n * peak - 1) / (n - 1)`; a score returns the expected level.
|
|
94
101
|
|
package/docs/mcp.md
CHANGED
|
@@ -225,6 +225,8 @@ Pi activates `codemode` when a server with `codemode` exposure connects. It acti
|
|
|
225
225
|
|
|
226
226
|
Tools with `codemode` or `deferred` exposure can be reached through either indirect mechanism: codemode scripts can call them, and `tool_search` can load them. Codemode calls do not depend on the active tool set, so they remain available after `/tree`, resume, and fork. Tools loaded by `tool_search` are recorded in the transcript and remain declared on that branch.
|
|
227
227
|
|
|
228
|
+
`--tools` does not remove MCP tools unless one of its entries starts with `mcp__`; `pi --tools read,codemode,'mcp__radius__*'` keeps only the tools of `radius`. `--exclude-tools` accepts the same patterns, and `--no-mcp` disables MCP for one run (see [Tools](cli.md#mcp-tools)).
|
|
229
|
+
|
|
228
230
|
To keep `codemode` active without MCP servers, add `"defaultTools": ["+codemode"]` to [settings](settings.md#tools). To prevent automatic codemode activation, set `"autoEnableCodemode": false` beside `mcpServers`. A project value overrides the user-level value. Pi warns once when neither `codemode` nor `tool_search` is active and non-direct tools cannot be called.
|
|
229
231
|
|
|
230
232
|
Text results over 20 KB reach the model with their middle removed around a `…N chars truncated…` marker. The full text is saved to a temporary file named in the result. Codemode scripts receive the complete result and can reduce it before returning output to the model.
|
|
@@ -261,7 +263,7 @@ Changes to enabled state or exposure apply only to the current session. A file-c
|
|
|
261
263
|
|
|
262
264
|
### Replace the built-in MCP support
|
|
263
265
|
|
|
264
|
-
An installed extension that registers `/mcp`, such as `pi-mcp-adapter`, replaces the built-in MCP support for sessions. Pi then does not read `mcp.json` or connect its servers in a session, and `/mcp` belongs to the extension. Remove the extension to restore the built-in behavior. To disable built-in MCP support without a replacement, disable `mcp` under Built-in in `pi config`, or set `"extensions": ["-builtin:mcp"]` in [settings](settings.md#resources).
|
|
266
|
+
An installed extension that registers `/mcp`, such as `pi-mcp-adapter`, replaces the built-in MCP support for sessions. Pi then does not read `mcp.json` or connect its servers in a session, and `/mcp` belongs to the extension. Remove the extension to restore the built-in behavior. To disable built-in MCP support without a replacement, disable `mcp` under Built-in in `pi config`, or set `"extensions": ["-builtin:mcp"]` in [settings](settings.md#resources). `--no-mcp` disables it for one run.
|
|
265
267
|
|
|
266
268
|
An extension that registers `codemode` or `tool_search` similarly replaces the built-in tool with that name. Shell-level `pi mcp` commands always use the built-in implementation.
|
|
267
269
|
|