Use Pi terminal UI in xterm.js with browser-local model and tools
Browse files- README.md +1 -1
- SPACE_README.md +1 -1
- app/README.md +11 -3
- app/index.html +3 -10
- app/package-lock.json +95 -5
- app/package.json +7 -1
- app/scripts/licenses.mjs +1 -0
- app/scripts/pi-browser.mjs +31 -0
- app/src/main.mjs +20 -42
- app/src/pi/browser-host.mjs +8 -0
- app/src/pi/capabilities.mjs +25 -0
- app/src/pi/entry.mjs +17 -0
- app/src/pi/terminal-ui.mjs +216 -0
- app/src/pi/theme.mjs +31 -0
- app/src/pi/xterm-terminal.mjs +31 -0
- app/src/style.css +11 -36
- app/tests/browser.mjs +15 -5
- app/tests/space-embedded.mjs +2 -1
- app/tests/terminal.mjs +104 -0
- app/vendor/pi-coding-agent/LICENSE +21 -0
- app/vendor/pi-coding-agent/README.md +21 -0
- app/vendor/pi-coding-agent/components/assistant-message.js +158 -0
- app/vendor/pi-coding-agent/components/custom-editor.js +115 -0
- app/vendor/pi-coding-agent/components/markdown-transform.js +19 -0
- app/vendor/pi-coding-agent/components/user-message.js +49 -0
- app/vendor/pi-coding-agent/provenance.json +12 -0
- app/vendor/pi-coding-agent/theme/light.json +90 -0
- app/vite.config.mjs +3 -1
- assets/index-BRKUEVCq.js +0 -0
- assets/index-Cfkr7NLu.css +0 -1
- assets/index-DL-Ot0hN.css +1 -0
- assets/index-LA8xezhP.js +0 -0
- deployment-manifest.json +118 -38
- index.html +5 -12
- licenses/dependencies.txt +342 -1
- licenses/pi-components.json +12 -0
- research/browser-agent/REPORT.md +20 -1
- scripts/prepare_space.py +2 -1
- validation/agent-terminal-dev.json +157 -0
- validation/agent-terminal-model.json +1885 -0
- validation/agent-terminal.json +39 -0
README.md
CHANGED
|
@@ -33,7 +33,7 @@ Requires WebGPU with FP16 (`shader-f16`) in a browser worker, OPFS, and sufficie
|
|
| 33 |
|
| 34 |
Try: **Read sales.csv, calculate total revenue using bash, and save a short report to report.md. Verify the file.**
|
| 35 |
|
| 36 |
-
Pi has `bash`, `read`, `write`, and `edit` tools. just-bash supports shell scripts, pipes, redirects, `awk`, `jq`, `sed`, `grep`, and other text commands. Node.js, Python, npm, native binaries, SQLite, and network commands are unavailable.
|
| 37 |
|
| 38 |
Source and run instructions: [app/README.md](app/README.md). Architecture and primary-source research: [investigation](research/browser-agent/REPORT.md).
|
| 39 |
|
|
|
|
| 33 |
|
| 34 |
Try: **Read sales.csv, calculate total revenue using bash, and save a short report to report.md. Verify the file.**
|
| 35 |
|
| 36 |
+
Pi has `bash`, `read`, `write`, and `edit` tools. just-bash supports shell scripts, pipes, redirects, `awk`, `jq`, `sed`, `grep`, and other text commands. Node.js, Python, npm, native binaries, SQLite, and network commands are unavailable. The left column uses xterm.js with Pi's actual terminal renderer, editor, and message components. MiniCPM and the tools run in the browser worker. This reuses Pi's TUI; the full Node CLI and its extensions are not included. Files are virtual and remain on your device. Export work you want to keep.
|
| 37 |
|
| 38 |
Source and run instructions: [app/README.md](app/README.md). Architecture and primary-source research: [investigation](research/browser-agent/REPORT.md).
|
| 39 |
|
SPACE_README.md
CHANGED
|
@@ -33,7 +33,7 @@ Requires WebGPU with FP16 (`shader-f16`) in a browser worker, OPFS, and sufficie
|
|
| 33 |
|
| 34 |
Try: **Read sales.csv, calculate total revenue using bash, and save a short report to report.md. Verify the file.**
|
| 35 |
|
| 36 |
-
Pi has `bash`, `read`, `write`, and `edit` tools. just-bash supports shell scripts, pipes, redirects, `awk`, `jq`, `sed`, `grep`, and other text commands. Node.js, Python, npm, native binaries, SQLite, and network commands are unavailable.
|
| 37 |
|
| 38 |
Source and run instructions: [app/README.md](app/README.md). Architecture and primary-source research: [investigation](research/browser-agent/REPORT.md).
|
| 39 |
|
|
|
|
| 33 |
|
| 34 |
Try: **Read sales.csv, calculate total revenue using bash, and save a short report to report.md. Verify the file.**
|
| 35 |
|
| 36 |
+
Pi has `bash`, `read`, `write`, and `edit` tools. just-bash supports shell scripts, pipes, redirects, `awk`, `jq`, `sed`, `grep`, and other text commands. Node.js, Python, npm, native binaries, SQLite, and network commands are unavailable. The left column uses xterm.js with Pi's actual terminal renderer, editor, and message components. MiniCPM and the tools run in the browser worker. This reuses Pi's TUI; the full Node CLI and its extensions are not included. Files are virtual and remain on your device. Export work you want to keep.
|
| 37 |
|
| 38 |
Source and run instructions: [app/README.md](app/README.md). Architecture and primary-source research: [investigation](research/browser-agent/REPORT.md).
|
| 39 |
|
app/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
| 1 |
# Pi in the browser
|
| 2 |
|
| 3 |
-
A static web app running **the real Pi Agent**, **just-bash**, and **our verified MiniCPM5-2B q4f16 conversion** in the browser. A dedicated worker owns the agent loop, WebGPU inference, and virtual shell. No inference API, API key, shell server, or Pi RPC server is involved.
|
| 4 |
|
| 5 |
```sh
|
| 6 |
cd app
|
|
@@ -15,6 +15,8 @@ For development, use `npm run dev`. To use the already converted weights on this
|
|
| 15 |
|
| 16 |
## Features
|
| 17 |
|
|
|
|
|
|
|
| 18 |
- Streaming conversation with genuine `@earendil-works/pi-agent-core` tool execution and automatic follow-up turns.
|
| 19 |
- `bash`, `read`, `write`, and exact-match `edit` tools connected to the same just-bash filesystem.
|
| 20 |
- File editor, text-file import, workspace export, and an interactive command entry.
|
|
@@ -27,7 +29,9 @@ For development, use `npm run dev`. To use the already converted weights on this
|
|
| 27 |
|
| 28 |
## Runtime boundaries
|
| 29 |
|
| 30 |
-
This uses Pi's actual agent core with
|
|
|
|
|
|
|
| 31 |
|
| 32 |
just-bash is a simulated shell, not a Linux VM. Shell scripts, pipes, redirects, `awk`, `jq`, `sed`, `grep`, `find`, and other supported text commands execute in JavaScript. Node.js, Python, npm, native executables, SQLite, and network commands are unavailable. The installed browser bundle also excludes working gzip/zlib commands. Shell cwd and variables reset between commands; filesystem changes persist. Commands have a 10-second deadline, 3,000-command cap, 16 MiB filesystem budget, and 256 KiB output budget. Tool output passed to the model is capped at 6,000 characters.
|
| 33 |
|
|
@@ -40,11 +44,13 @@ The small 4-bit model can make mistakes. These tests establish working integrati
|
|
| 40 |
```sh
|
| 41 |
npm test
|
| 42 |
npm run build
|
|
|
|
|
|
|
| 43 |
# Start npm run preview -- --port 4173 and the root static model server first:
|
| 44 |
npm run test:browser
|
| 45 |
```
|
| 46 |
|
| 47 |
-
The browser suite uses real Chromium, the actual model, and the production bundle. It disables networking while Pi reads a random value from an unseen file, calculates CSV revenue, writes and verifies a report, creates and executes a shell script, edits it, and recovers from a missing-file tool error. It also checks stopping generation, file import/edit/save, manual shell execution, workspace persistence, cached reloads, and a 390 px layout.
|
| 48 |
|
| 49 |
To exercise the public Hugging Face download in a separate browser profile:
|
| 50 |
|
|
@@ -78,6 +84,8 @@ Browsers that deny storage in third-party embeds get an **Open workspace in a ne
|
|
| 78 |
| Component | Version / revision |
|
| 79 |
|---|---|
|
| 80 |
| Pi agent core and Pi AI types/event stream | `@earendil-works/*` 0.85.1 |
|
|
|
|
|
|
|
| 81 |
| just-bash | 3.4.2, explicit `just-bash/browser` import |
|
| 82 |
| Transformers.js | 4.2.0 |
|
| 83 |
| Model | `Mike0021/MiniCPM5-2B-ONNX` at `04a6c49fcba3a65a0351c92644c3a7e9d4343059` |
|
|
|
|
| 1 |
# Pi in the browser
|
| 2 |
|
| 3 |
+
A static web app running **Piβs actual terminal UI in xterm.js**, **the real Pi Agent**, **just-bash**, and **our verified MiniCPM5-2B q4f16 conversion** in the browser. A dedicated worker owns the agent loop, WebGPU inference, and virtual shell. No inference API, API key, shell server, or Pi RPC server is involved.
|
| 4 |
|
| 5 |
```sh
|
| 6 |
cd app
|
|
|
|
| 15 |
|
| 16 |
## Features
|
| 17 |
|
| 18 |
+
- Piβs upstream fullscreen renderer, multiline editor, user/assistant message components, Markdown, prompt history, and autocomplete. xterm.js renders their ANSI output.
|
| 19 |
+
- `/load`, `/help`, `/new`, and `!command` browser commands. Enter sends, Shift+Enter inserts a newline, Escape/Ctrl+C stops, Ctrl+O expands tools. Ctrl+P/N recalls prompts; Ctrl+Shift+F searches the transcript. The Send and Newline buttons also support touch input.
|
| 20 |
- Streaming conversation with genuine `@earendil-works/pi-agent-core` tool execution and automatic follow-up turns.
|
| 21 |
- `bash`, `read`, `write`, and exact-match `edit` tools connected to the same just-bash filesystem.
|
| 22 |
- File editor, text-file import, workspace export, and an interactive command entry.
|
|
|
|
| 29 |
|
| 30 |
## Runtime boundaries
|
| 31 |
|
| 32 |
+
This uses Pi's actual TUI and agent core with browser host adapters. The npm `pi-tui` package supplies the renderer, editor, input parser, layout, Markdown, search, and keybindings. Unmodified CLI presentation components are in [vendor/pi-coding-agent](vendor/pi-coding-agent/README.md), with provenance checksums and the upstream light palette. [The build adapter](scripts/pi-browser.mjs) replaces terminal host services; [the terminal adapter](src/pi/xterm-terminal.mjs) connects xterm input/output/resize to Pi. Browser command dispatch and just-bash tool cards are app code.
|
| 33 |
+
|
| 34 |
+
The full Node CLI, host filesystem, OAuth providers, extensions, package installation, and RPC server are not included. xterm.js is the terminal emulator; it does not add Node.js or a native shell. Model inference and tool execution remain in the browser worker. Terminal control sequences from model/file output are stripped before rendering, and Markdown images do not trigger downloads.
|
| 35 |
|
| 36 |
just-bash is a simulated shell, not a Linux VM. Shell scripts, pipes, redirects, `awk`, `jq`, `sed`, `grep`, `find`, and other supported text commands execute in JavaScript. Node.js, Python, npm, native executables, SQLite, and network commands are unavailable. The installed browser bundle also excludes working gzip/zlib commands. Shell cwd and variables reset between commands; filesystem changes persist. Commands have a 10-second deadline, 3,000-command cap, 16 MiB filesystem budget, and 256 KiB output budget. Tool output passed to the model is capped at 6,000 characters.
|
| 37 |
|
|
|
|
| 44 |
```sh
|
| 45 |
npm test
|
| 46 |
npm run build
|
| 47 |
+
# Start npm run preview, then run the terminal checks (no model needed):
|
| 48 |
+
node tests/terminal.mjs
|
| 49 |
# Start npm run preview -- --port 4173 and the root static model server first:
|
| 50 |
npm run test:browser
|
| 51 |
```
|
| 52 |
|
| 53 |
+
The terminal suite checks real keyboard events, multiline editing, autocomplete, history, Unicode and large clipboard pastes, shell execution, and live resize. The browser suite uses real Chromium, the actual model, and the production bundle; prompts are submitted through xterm and Piβs editor. It disables networking while Pi reads a random value from an unseen file, calculates CSV revenue, writes and verifies a report, creates and executes a shell script, edits it, and recovers from a missing-file tool error. It also checks stopping generation, file import/edit/save, manual shell execution, workspace persistence, cached reloads, and a 390 px layout.
|
| 54 |
|
| 55 |
To exercise the public Hugging Face download in a separate browser profile:
|
| 56 |
|
|
|
|
| 84 |
| Component | Version / revision |
|
| 85 |
|---|---|
|
| 86 |
| Pi agent core and Pi AI types/event stream | `@earendil-works/*` 0.85.1 |
|
| 87 |
+
| Pi TUI / CLI presentation components | 0.85.1 |
|
| 88 |
+
| xterm.js / fit addon | 6.0.0 / 0.11.0 |
|
| 89 |
| just-bash | 3.4.2, explicit `just-bash/browser` import |
|
| 90 |
| Transformers.js | 4.2.0 |
|
| 91 |
| Model | `Mike0021/MiniCPM5-2B-ONNX` at `04a6c49fcba3a65a0351c92644c3a7e9d4343059` |
|
app/index.html
CHANGED
|
@@ -20,17 +20,10 @@
|
|
| 20 |
<div id="load-detail" class="muted"></div>
|
| 21 |
<a id="open-tab" class="button" target="_blank" rel="noopener noreferrer" hidden>Open workspace in a new tab β</a>
|
| 22 |
</div>
|
| 23 |
-
<div id="
|
| 24 |
-
<div id="welcome"><div class="eyebrow">A small agent. A real workspace.</div><h1>What shall we work on?</h1>
|
| 25 |
-
<p>Chat with Pi, work with files, and run shell commands.<br>The model and every tool run here, in your browser.</p>
|
| 26 |
-
<div class="suggestions"><button data-prompt="Read sales.csv, calculate total revenue using bash, and save a short report to report.md. Verify the file.">Analyze the sample data <span>β</span></button><button data-prompt="Create a bash script called hello.sh that prints Hello from the browser, run it, and report the output.">Write and run a script <span>β</span></button></div>
|
| 27 |
-
</div>
|
| 28 |
-
</div>
|
| 29 |
<div id="notice" role="status" hidden></div>
|
| 30 |
-
<
|
| 31 |
-
|
| 32 |
-
</form>
|
| 33 |
-
<footer>Pi agent core Β· just-bash Β· Transformers.js <span>4,096 token context</span></footer>
|
| 34 |
</section>
|
| 35 |
<aside class="workspace" aria-label="Workspace">
|
| 36 |
<div class="workspace-heading"><h2>Workspace</h2><div><button id="add-file" class="quiet">+ File</button><label class="button quiet" for="upload">Import</label><input id="upload" type="file" multiple hidden><button id="export" class="quiet">Export</button></div></div>
|
|
|
|
| 20 |
<div id="load-detail" class="muted"></div>
|
| 21 |
<a id="open-tab" class="button" target="_blank" rel="noopener noreferrer" hidden>Open workspace in a new tab β</a>
|
| 22 |
</div>
|
| 23 |
+
<div id="pi-terminal" aria-label="Pi terminal"></div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 24 |
<div id="notice" role="status" hidden></div>
|
| 25 |
+
<div class="terminal-controls"><span id="activity" role="status">Files and chat are saved on this device.</span><button id="newline" type="button" title="Insert a newline (Shift+Enter)">β΅ Newline</button><button id="stop" type="button" hidden>Stop</button><button id="send" class="primary">Send β</button></div>
|
| 26 |
+
<footer>Pi TUI Β· xterm.js <span>Model and tools run in your browser</span></footer>
|
|
|
|
|
|
|
| 27 |
</section>
|
| 28 |
<aside class="workspace" aria-label="Workspace">
|
| 29 |
<div class="workspace-heading"><h2>Workspace</h2><div><button id="add-file" class="quiet">+ File</button><label class="button quiet" for="upload">Import</label><input id="upload" type="file" multiple hidden><button id="export" class="quiet">Export</button></div></div>
|
app/package-lock.json
CHANGED
|
@@ -10,8 +10,13 @@
|
|
| 10 |
"dependencies": {
|
| 11 |
"@earendil-works/pi-agent-core": "0.85.1",
|
| 12 |
"@earendil-works/pi-ai": "0.85.1",
|
|
|
|
| 13 |
"@huggingface/transformers": "4.2.0",
|
| 14 |
"@xmldom/xmldom": "0.9.12",
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15 |
"hash-wasm": "4.12.0",
|
| 16 |
"just-bash": "3.4.2",
|
| 17 |
"markdown-it": "15.0.2"
|
|
@@ -556,6 +561,19 @@
|
|
| 556 |
"node": ">=22.19.0"
|
| 557 |
}
|
| 558 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 559 |
"node_modules/@emnapi/runtime": {
|
| 560 |
"version": "1.11.3",
|
| 561 |
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.3.tgz",
|
|
@@ -2141,6 +2159,21 @@
|
|
| 2141 |
"node": ">=14.6"
|
| 2142 |
}
|
| 2143 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2144 |
"node_modules/adm-zip": {
|
| 2145 |
"version": "0.5.18",
|
| 2146 |
"resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.18.tgz",
|
|
@@ -2237,6 +2270,31 @@
|
|
| 2237 |
"readable-stream": "^3.4.0"
|
| 2238 |
}
|
| 2239 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2240 |
"node_modules/boolean": {
|
| 2241 |
"version": "3.2.0",
|
| 2242 |
"resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz",
|
|
@@ -2263,9 +2321,9 @@
|
|
| 2263 |
}
|
| 2264 |
},
|
| 2265 |
"node_modules/buffer": {
|
| 2266 |
-
"version": "
|
| 2267 |
-
"resolved": "https://registry.npmjs.org/buffer/-/buffer-
|
| 2268 |
-
"integrity": "sha512-
|
| 2269 |
"funding": [
|
| 2270 |
{
|
| 2271 |
"type": "github",
|
|
@@ -2281,10 +2339,9 @@
|
|
| 2281 |
}
|
| 2282 |
],
|
| 2283 |
"license": "MIT",
|
| 2284 |
-
"optional": true,
|
| 2285 |
"dependencies": {
|
| 2286 |
"base64-js": "^1.3.1",
|
| 2287 |
-
"ieee754": "^1.
|
| 2288 |
}
|
| 2289 |
},
|
| 2290 |
"node_modules/buffer-equal-constant-time": {
|
|
@@ -2527,6 +2584,15 @@
|
|
| 2527 |
"url": "https://github.com/sponsors/sindresorhus"
|
| 2528 |
}
|
| 2529 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2530 |
"node_modules/expand-template": {
|
| 2531 |
"version": "2.0.3",
|
| 2532 |
"resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",
|
|
@@ -2715,6 +2781,18 @@
|
|
| 2715 |
"node": ">=18"
|
| 2716 |
}
|
| 2717 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2718 |
"node_modules/github-from-package": {
|
| 2719 |
"version": "0.0.0",
|
| 2720 |
"resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz",
|
|
@@ -3309,6 +3387,18 @@
|
|
| 3309 |
"markdown-it": "bin/markdown-it.mjs"
|
| 3310 |
}
|
| 3311 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3312 |
"node_modules/matcher": {
|
| 3313 |
"version": "3.0.0",
|
| 3314 |
"resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz",
|
|
|
|
| 10 |
"dependencies": {
|
| 11 |
"@earendil-works/pi-agent-core": "0.85.1",
|
| 12 |
"@earendil-works/pi-ai": "0.85.1",
|
| 13 |
+
"@earendil-works/pi-tui": "0.85.1",
|
| 14 |
"@huggingface/transformers": "4.2.0",
|
| 15 |
"@xmldom/xmldom": "0.9.12",
|
| 16 |
+
"@xterm/addon-fit": "0.11.0",
|
| 17 |
+
"@xterm/xterm": "6.0.0",
|
| 18 |
+
"buffer": "6.0.3",
|
| 19 |
+
"events": "3.3.0",
|
| 20 |
"hash-wasm": "4.12.0",
|
| 21 |
"just-bash": "3.4.2",
|
| 22 |
"markdown-it": "15.0.2"
|
|
|
|
| 561 |
"node": ">=22.19.0"
|
| 562 |
}
|
| 563 |
},
|
| 564 |
+
"node_modules/@earendil-works/pi-tui": {
|
| 565 |
+
"version": "0.85.1",
|
| 566 |
+
"resolved": "https://registry.npmjs.org/@earendil-works/pi-tui/-/pi-tui-0.85.1.tgz",
|
| 567 |
+
"integrity": "sha512-OIzw9efInmO4WOBnD4TxcTdBjmzvYJpzslkgoUro946nEGoYWg5rwv1p4fDt3/JvMx9QybryUCUwlm7j8Dreig==",
|
| 568 |
+
"license": "MIT",
|
| 569 |
+
"dependencies": {
|
| 570 |
+
"get-east-asian-width": "1.6.0",
|
| 571 |
+
"marked": "18.0.5"
|
| 572 |
+
},
|
| 573 |
+
"engines": {
|
| 574 |
+
"node": ">=22.19.0"
|
| 575 |
+
}
|
| 576 |
+
},
|
| 577 |
"node_modules/@emnapi/runtime": {
|
| 578 |
"version": "1.11.3",
|
| 579 |
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.3.tgz",
|
|
|
|
| 2159 |
"node": ">=14.6"
|
| 2160 |
}
|
| 2161 |
},
|
| 2162 |
+
"node_modules/@xterm/addon-fit": {
|
| 2163 |
+
"version": "0.11.0",
|
| 2164 |
+
"resolved": "https://registry.npmjs.org/@xterm/addon-fit/-/addon-fit-0.11.0.tgz",
|
| 2165 |
+
"integrity": "sha512-jYcgT6xtVYhnhgxh3QgYDnnNMYTcf8ElbxxFzX0IZo+vabQqSPAjC3c1wJrKB5E19VwQei89QCiZZP86DCPF7g==",
|
| 2166 |
+
"license": "MIT"
|
| 2167 |
+
},
|
| 2168 |
+
"node_modules/@xterm/xterm": {
|
| 2169 |
+
"version": "6.0.0",
|
| 2170 |
+
"resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-6.0.0.tgz",
|
| 2171 |
+
"integrity": "sha512-TQwDdQGtwwDt+2cgKDLn0IRaSxYu1tSUjgKarSDkUM0ZNiSRXFpjxEsvc/Zgc5kq5omJ+V0a8/kIM2WD3sMOYg==",
|
| 2172 |
+
"license": "MIT",
|
| 2173 |
+
"workspaces": [
|
| 2174 |
+
"addons/*"
|
| 2175 |
+
]
|
| 2176 |
+
},
|
| 2177 |
"node_modules/adm-zip": {
|
| 2178 |
"version": "0.5.18",
|
| 2179 |
"resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.18.tgz",
|
|
|
|
| 2270 |
"readable-stream": "^3.4.0"
|
| 2271 |
}
|
| 2272 |
},
|
| 2273 |
+
"node_modules/bl/node_modules/buffer": {
|
| 2274 |
+
"version": "5.7.1",
|
| 2275 |
+
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
|
| 2276 |
+
"integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
|
| 2277 |
+
"funding": [
|
| 2278 |
+
{
|
| 2279 |
+
"type": "github",
|
| 2280 |
+
"url": "https://github.com/sponsors/feross"
|
| 2281 |
+
},
|
| 2282 |
+
{
|
| 2283 |
+
"type": "patreon",
|
| 2284 |
+
"url": "https://www.patreon.com/feross"
|
| 2285 |
+
},
|
| 2286 |
+
{
|
| 2287 |
+
"type": "consulting",
|
| 2288 |
+
"url": "https://feross.org/support"
|
| 2289 |
+
}
|
| 2290 |
+
],
|
| 2291 |
+
"license": "MIT",
|
| 2292 |
+
"optional": true,
|
| 2293 |
+
"dependencies": {
|
| 2294 |
+
"base64-js": "^1.3.1",
|
| 2295 |
+
"ieee754": "^1.1.13"
|
| 2296 |
+
}
|
| 2297 |
+
},
|
| 2298 |
"node_modules/boolean": {
|
| 2299 |
"version": "3.2.0",
|
| 2300 |
"resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz",
|
|
|
|
| 2321 |
}
|
| 2322 |
},
|
| 2323 |
"node_modules/buffer": {
|
| 2324 |
+
"version": "6.0.3",
|
| 2325 |
+
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
|
| 2326 |
+
"integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
|
| 2327 |
"funding": [
|
| 2328 |
{
|
| 2329 |
"type": "github",
|
|
|
|
| 2339 |
}
|
| 2340 |
],
|
| 2341 |
"license": "MIT",
|
|
|
|
| 2342 |
"dependencies": {
|
| 2343 |
"base64-js": "^1.3.1",
|
| 2344 |
+
"ieee754": "^1.2.1"
|
| 2345 |
}
|
| 2346 |
},
|
| 2347 |
"node_modules/buffer-equal-constant-time": {
|
|
|
|
| 2584 |
"url": "https://github.com/sponsors/sindresorhus"
|
| 2585 |
}
|
| 2586 |
},
|
| 2587 |
+
"node_modules/events": {
|
| 2588 |
+
"version": "3.3.0",
|
| 2589 |
+
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
|
| 2590 |
+
"integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
|
| 2591 |
+
"license": "MIT",
|
| 2592 |
+
"engines": {
|
| 2593 |
+
"node": ">=0.8.x"
|
| 2594 |
+
}
|
| 2595 |
+
},
|
| 2596 |
"node_modules/expand-template": {
|
| 2597 |
"version": "2.0.3",
|
| 2598 |
"resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",
|
|
|
|
| 2781 |
"node": ">=18"
|
| 2782 |
}
|
| 2783 |
},
|
| 2784 |
+
"node_modules/get-east-asian-width": {
|
| 2785 |
+
"version": "1.6.0",
|
| 2786 |
+
"resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz",
|
| 2787 |
+
"integrity": "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==",
|
| 2788 |
+
"license": "MIT",
|
| 2789 |
+
"engines": {
|
| 2790 |
+
"node": ">=18"
|
| 2791 |
+
},
|
| 2792 |
+
"funding": {
|
| 2793 |
+
"url": "https://github.com/sponsors/sindresorhus"
|
| 2794 |
+
}
|
| 2795 |
+
},
|
| 2796 |
"node_modules/github-from-package": {
|
| 2797 |
"version": "0.0.0",
|
| 2798 |
"resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz",
|
|
|
|
| 3387 |
"markdown-it": "bin/markdown-it.mjs"
|
| 3388 |
}
|
| 3389 |
},
|
| 3390 |
+
"node_modules/marked": {
|
| 3391 |
+
"version": "18.0.5",
|
| 3392 |
+
"resolved": "https://registry.npmjs.org/marked/-/marked-18.0.5.tgz",
|
| 3393 |
+
"integrity": "sha512-S6GcvALHg6K4ohtu4E7x0a1AqhAjp6cV8KhLSyN9qVapnzJkusVBxZRcIU9AeYsbe6P1hKDusSbEOzGyyuce6w==",
|
| 3394 |
+
"license": "MIT",
|
| 3395 |
+
"bin": {
|
| 3396 |
+
"marked": "bin/marked.js"
|
| 3397 |
+
},
|
| 3398 |
+
"engines": {
|
| 3399 |
+
"node": ">= 20"
|
| 3400 |
+
}
|
| 3401 |
+
},
|
| 3402 |
"node_modules/matcher": {
|
| 3403 |
"version": "3.0.0",
|
| 3404 |
"resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz",
|
app/package.json
CHANGED
|
@@ -8,13 +8,19 @@
|
|
| 8 |
"build": "vite build",
|
| 9 |
"preview": "vite preview --host 127.0.0.1",
|
| 10 |
"test": "node --test tests/*.test.mjs",
|
| 11 |
-
"test:browser": "node tests/browser.mjs"
|
|
|
|
| 12 |
},
|
| 13 |
"dependencies": {
|
| 14 |
"@earendil-works/pi-agent-core": "0.85.1",
|
| 15 |
"@earendil-works/pi-ai": "0.85.1",
|
|
|
|
| 16 |
"@huggingface/transformers": "4.2.0",
|
| 17 |
"@xmldom/xmldom": "0.9.12",
|
|
|
|
|
|
|
|
|
|
|
|
|
| 18 |
"hash-wasm": "4.12.0",
|
| 19 |
"just-bash": "3.4.2",
|
| 20 |
"markdown-it": "15.0.2"
|
|
|
|
| 8 |
"build": "vite build",
|
| 9 |
"preview": "vite preview --host 127.0.0.1",
|
| 10 |
"test": "node --test tests/*.test.mjs",
|
| 11 |
+
"test:browser": "node tests/browser.mjs",
|
| 12 |
+
"test:terminal": "node tests/terminal.mjs"
|
| 13 |
},
|
| 14 |
"dependencies": {
|
| 15 |
"@earendil-works/pi-agent-core": "0.85.1",
|
| 16 |
"@earendil-works/pi-ai": "0.85.1",
|
| 17 |
+
"@earendil-works/pi-tui": "0.85.1",
|
| 18 |
"@huggingface/transformers": "4.2.0",
|
| 19 |
"@xmldom/xmldom": "0.9.12",
|
| 20 |
+
"@xterm/addon-fit": "0.11.0",
|
| 21 |
+
"@xterm/xterm": "6.0.0",
|
| 22 |
+
"buffer": "6.0.3",
|
| 23 |
+
"events": "3.3.0",
|
| 24 |
"hash-wasm": "4.12.0",
|
| 25 |
"just-bash": "3.4.2",
|
| 26 |
"markdown-it": "15.0.2"
|
app/scripts/licenses.mjs
CHANGED
|
@@ -23,4 +23,5 @@ export async function collectLicenses() {
|
|
| 23 |
await scan('node_modules');
|
| 24 |
await mkdir('dist/licenses', { recursive: true });
|
| 25 |
await writeFile('dist/licenses/dependencies.txt', notices.sort().join('\n----------------------------------------\n\n'));
|
|
|
|
| 26 |
}
|
|
|
|
| 23 |
await scan('node_modules');
|
| 24 |
await mkdir('dist/licenses', { recursive: true });
|
| 25 |
await writeFile('dist/licenses/dependencies.txt', notices.sort().join('\n----------------------------------------\n\n'));
|
| 26 |
+
await writeFile('dist/licenses/pi-components.json', await readFile('vendor/pi-coding-agent/provenance.json'));
|
| 27 |
}
|
app/scripts/pi-browser.mjs
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { resolve } from 'node:path';
|
| 2 |
+
import { readFileSync } from 'node:fs';
|
| 3 |
+
import { createHash } from 'node:crypto';
|
| 4 |
+
|
| 5 |
+
// Adapt host services at build time; never edit installed or vendored Pi code.
|
| 6 |
+
// Exact package pin + component checksums make this boundary reviewable.
|
| 7 |
+
export function piBrowser() {
|
| 8 |
+
const sourceRoot = resolve('vendor/pi-coding-agent');
|
| 9 |
+
const provenance = JSON.parse(readFileSync(resolve(sourceRoot, 'provenance.json'), 'utf8'));
|
| 10 |
+
for (const [file, expected] of Object.entries(provenance.files)) {
|
| 11 |
+
const actual = createHash('sha256').update(readFileSync(resolve(sourceRoot, file))).digest('hex');
|
| 12 |
+
if (actual !== expected) throw Error(`Vendored Pi component differs from its upstream checksum: ${file}`);
|
| 13 |
+
}
|
| 14 |
+
const host = resolve('src/pi/browser-host.mjs');
|
| 15 |
+
return {
|
| 16 |
+
name: 'pi-browser-host', enforce: 'pre',
|
| 17 |
+
resolveId(source, importer = '') {
|
| 18 |
+
if (source === '@earendil-works/pi-tui') return resolve('src/pi/entry.mjs');
|
| 19 |
+
if (importer.includes('/@earendil-works/pi-tui/')) {
|
| 20 |
+
if (source.endsWith('/terminal-image.js')) return resolve('src/pi/capabilities.mjs');
|
| 21 |
+
if (source === 'node:perf_hooks') return host;
|
| 22 |
+
}
|
| 23 |
+
if (importer.includes('/vendor/pi-coding-agent/') && source === '../theme/theme.js') return resolve('src/pi/theme.mjs');
|
| 24 |
+
},
|
| 25 |
+
transform(code, id) {
|
| 26 |
+
if (!id.includes('/@earendil-works/pi-tui/') || !id.split('?')[0].endsWith('.js')) return;
|
| 27 |
+
if (!/\b(?:process|Buffer)\./.test(code)) return;
|
| 28 |
+
return { code: `import { browserProcess as process, Buffer } from ${JSON.stringify(host)};\n${code}`, map: null };
|
| 29 |
+
},
|
| 30 |
+
};
|
| 31 |
+
}
|
app/src/main.mjs
CHANGED
|
@@ -1,6 +1,5 @@
|
|
| 1 |
import './style.css';
|
| 2 |
-
import {
|
| 3 |
-
import { renderMarkdown } from './markdown.mjs';
|
| 4 |
|
| 5 |
const $ = selector => document.querySelector(selector);
|
| 6 |
const worker = new Worker(new URL('./agent.worker.mjs', import.meta.url), { type: 'module' });
|
|
@@ -8,7 +7,11 @@ const pending = new Map();
|
|
| 8 |
let nextId = 1, busy = false, loaded = false, selected, dirty = false, files = {}, messages = [], readyResolve;
|
| 9 |
const ready = new Promise(resolve => { readyResolve = resolve; });
|
| 10 |
const diagnostics = { events: [], generations: [], device: null };
|
| 11 |
-
const
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12 |
function rpc(action, args) {
|
| 13 |
return new Promise((resolve, reject) => {
|
| 14 |
const id = nextId++; pending.set(id, { resolve, reject }); worker.postMessage({ id, action, args });
|
|
@@ -23,13 +26,16 @@ function storageHelp(error) {
|
|
| 23 |
}
|
| 24 |
function controls() {
|
| 25 |
$('#load').disabled = busy || loaded;
|
| 26 |
-
$('#send').disabled = busy
|
|
|
|
|
|
|
| 27 |
$('#stop').hidden = !busy;
|
| 28 |
for (const id of ['#new-chat', '#shell-run', '#add-file', '#upload']) $(id).disabled = busy;
|
| 29 |
$('#save').disabled = busy || !selected || !dirty;
|
| 30 |
$('#editor').disabled = busy || !selected;
|
| 31 |
}
|
| 32 |
function renderFiles() {
|
|
|
|
| 33 |
$('#files').replaceChildren();
|
| 34 |
for (const file of Object.keys(files).sort()) {
|
| 35 |
const button = document.createElement('button'); button.textContent = file.replace(/^\/workspace\//, '');
|
|
@@ -43,39 +49,7 @@ function selectFile(file) {
|
|
| 43 |
selected = file; $('#filename').textContent = file; $('#editor').value = files[file] ?? '';
|
| 44 |
renderFiles(); controls();
|
| 45 |
}
|
| 46 |
-
function renderMessages() {
|
| 47 |
-
const log = $('#messages');
|
| 48 |
-
const nearBottom = log.scrollHeight - log.scrollTop - log.clientHeight < 90;
|
| 49 |
-
const opened = new Set([...log.querySelectorAll('details[open]')].map(d => d.dataset.id));
|
| 50 |
-
log.replaceChildren();
|
| 51 |
-
if (!messages.length) log.append(welcome.cloneNode(true));
|
| 52 |
-
const results = new Map(messages.filter(m => m.role === 'toolResult').map(m => [m.toolCallId, m]));
|
| 53 |
-
for (const m of messages) {
|
| 54 |
-
if (m.role === 'toolResult') continue;
|
| 55 |
-
const item = document.createElement('article'); item.className = 'message ' + m.role;
|
| 56 |
-
const label = document.createElement('div'); label.className = 'message-label'; label.textContent = m.role === 'user' ? 'You' : 'Pi'; item.append(label);
|
| 57 |
-
const content = typeof m.content === 'string' ? [{ type: 'text', text: m.content }] : m.content;
|
| 58 |
-
for (const block of content) {
|
| 59 |
-
if (block.type === 'text') {
|
| 60 |
-
const body = document.createElement('div'); body.className = 'message-body';
|
| 61 |
-
if (m.role === 'assistant') { body.classList.add('markdown'); body.innerHTML = renderMarkdown(block.text); }
|
| 62 |
-
else body.textContent = block.text;
|
| 63 |
-
item.append(body);
|
| 64 |
-
} else if (block.type === 'toolCall') {
|
| 65 |
-
const result = results.get(block.id), details = document.createElement('details');
|
| 66 |
-
details.className = 'tool'; details.dataset.id = block.id; details.open = opened.has(block.id) || !result;
|
| 67 |
-
const summary = document.createElement('summary');
|
| 68 |
-
summary.textContent = `${result ? result.isError ? 'Γ' : 'β' : 'Β·'} ${block.name} ${block.arguments.path ?? (block.arguments.command ?? '').slice(0, 65)}`;
|
| 69 |
-
const pre = document.createElement('pre'); pre.textContent = JSON.stringify(block.arguments, null, 2) + '\n\n' + (result ? textContent(result.content) : 'Runningβ¦');
|
| 70 |
-
details.append(summary, pre); item.append(details);
|
| 71 |
-
}
|
| 72 |
-
}
|
| 73 |
-
if (m.errorMessage) { const error = document.createElement('div'); error.className = 'error'; error.textContent = m.errorMessage; item.append(error); }
|
| 74 |
-
if (m.stopReason === 'length') { const note = document.createElement('div'); note.className = 'message-stats'; note.textContent = 'Output limit reached. Ask Pi to continue.'; item.append(note); }
|
| 75 |
-
log.append(item);
|
| 76 |
-
}
|
| 77 |
-
if (nearBottom) log.scrollTop = log.scrollHeight;
|
| 78 |
-
}
|
| 79 |
worker.onmessage = ({ data }) => {
|
| 80 |
if (data.type === 'reply') {
|
| 81 |
const request = pending.get(data.id); pending.delete(data.id);
|
|
@@ -110,6 +84,7 @@ worker.onmessage = ({ data }) => {
|
|
| 110 |
}
|
| 111 |
if (event.type === 'tool_execution_start') $('#activity').textContent = `Running ${event.toolName}β¦`;
|
| 112 |
if (event.type === 'turn_start') $('#activity').textContent = 'Pi is thinkingβ¦';
|
|
|
|
| 113 |
} else if (data.type === 'generation') { diagnostics.generations.push(data); if (diagnostics.generations.length > 100) diagnostics.generations.shift(); }
|
| 114 |
else if (data.type === 'context_trim') notice('Older turns were left out of the model context. Your full chat remains saved.');
|
| 115 |
else if (data.type === 'notice') notice(data.text);
|
|
@@ -135,11 +110,14 @@ async function prompt(text) {
|
|
| 135 |
notice(''); const result = await rpc('prompt', { text }); messages = result.messages; renderMessages(); return result;
|
| 136 |
}
|
| 137 |
$('#load').onclick = guarded(load);
|
| 138 |
-
$('#
|
| 139 |
-
$('#
|
| 140 |
-
$('#messages').onclick = event => { const button = event.target.closest('[data-prompt]'); if (button) { $('#prompt').value = button.dataset.prompt; $('#prompt').focus(); } };
|
| 141 |
$('#stop').onclick = guarded(() => rpc('stop'));
|
| 142 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 143 |
$('#editor').oninput = () => { dirty = true; controls(); };
|
| 144 |
$('#save').onclick = guarded(async () => { await rpc('write', { path: selected, content: $('#editor').value }); dirty = false; renderFiles(); controls(); notice(''); });
|
| 145 |
$('#add-file').onclick = guarded(async () => {
|
|
@@ -175,5 +153,5 @@ $('#shell-form').onsubmit = guarded(async () => {
|
|
| 175 |
$('#terminal-output').scrollTop = $('#terminal-output').scrollHeight;
|
| 176 |
});
|
| 177 |
// Browser verification exercises the same worker and real Pi / WebGPU paths as the UI.
|
| 178 |
-
window.browserPi = { ready, load, prompt, rpc, diagnostics, get loaded() { return loaded; }, get busy() { return busy; } };
|
| 179 |
controls();
|
|
|
|
| 1 |
import './style.css';
|
| 2 |
+
import { createTerminalUI } from './pi/terminal-ui.mjs';
|
|
|
|
| 3 |
|
| 4 |
const $ = selector => document.querySelector(selector);
|
| 5 |
const worker = new Worker(new URL('./agent.worker.mjs', import.meta.url), { type: 'module' });
|
|
|
|
| 7 |
let nextId = 1, busy = false, loaded = false, selected, dirty = false, files = {}, messages = [], readyResolve;
|
| 8 |
const ready = new Promise(resolve => { readyResolve = resolve; });
|
| 9 |
const diagnostics = { events: [], generations: [], device: null };
|
| 10 |
+
const terminalUI = createTerminalUI($('#pi-terminal'), {
|
| 11 |
+
load: () => ready.then(load), prompt: text => ready.then(() => prompt(text)),
|
| 12 |
+
newChat: () => ready.then(newChat), stop: () => rpc('stop'),
|
| 13 |
+
shell: command => { if (dirty) throw Error('Save your file edits before running commands.'); return rpc('shell', { command }); },
|
| 14 |
+
});
|
| 15 |
function rpc(action, args) {
|
| 16 |
return new Promise((resolve, reject) => {
|
| 17 |
const id = nextId++; pending.set(id, { resolve, reject }); worker.postMessage({ id, action, args });
|
|
|
|
| 26 |
}
|
| 27 |
function controls() {
|
| 28 |
$('#load').disabled = busy || loaded;
|
| 29 |
+
$('#send').disabled = busy;
|
| 30 |
+
$('#newline').disabled = busy;
|
| 31 |
+
terminalUI.setState({ busy, loaded, activity: $('#activity').textContent });
|
| 32 |
$('#stop').hidden = !busy;
|
| 33 |
for (const id of ['#new-chat', '#shell-run', '#add-file', '#upload']) $(id).disabled = busy;
|
| 34 |
$('#save').disabled = busy || !selected || !dirty;
|
| 35 |
$('#editor').disabled = busy || !selected;
|
| 36 |
}
|
| 37 |
function renderFiles() {
|
| 38 |
+
terminalUI.setFiles(files);
|
| 39 |
$('#files').replaceChildren();
|
| 40 |
for (const file of Object.keys(files).sort()) {
|
| 41 |
const button = document.createElement('button'); button.textContent = file.replace(/^\/workspace\//, '');
|
|
|
|
| 49 |
selected = file; $('#filename').textContent = file; $('#editor').value = files[file] ?? '';
|
| 50 |
renderFiles(); controls();
|
| 51 |
}
|
| 52 |
+
function renderMessages() { terminalUI.setMessages(messages); }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 53 |
worker.onmessage = ({ data }) => {
|
| 54 |
if (data.type === 'reply') {
|
| 55 |
const request = pending.get(data.id); pending.delete(data.id);
|
|
|
|
| 84 |
}
|
| 85 |
if (event.type === 'tool_execution_start') $('#activity').textContent = `Running ${event.toolName}β¦`;
|
| 86 |
if (event.type === 'turn_start') $('#activity').textContent = 'Pi is thinkingβ¦';
|
| 87 |
+
terminalUI.setState({ activity: $('#activity').textContent });
|
| 88 |
} else if (data.type === 'generation') { diagnostics.generations.push(data); if (diagnostics.generations.length > 100) diagnostics.generations.shift(); }
|
| 89 |
else if (data.type === 'context_trim') notice('Older turns were left out of the model context. Your full chat remains saved.');
|
| 90 |
else if (data.type === 'notice') notice(data.text);
|
|
|
|
| 110 |
notice(''); const result = await rpc('prompt', { text }); messages = result.messages; renderMessages(); return result;
|
| 111 |
}
|
| 112 |
$('#load').onclick = guarded(load);
|
| 113 |
+
$('#send').onclick = () => { terminalUI.submit(); terminalUI.focus(); };
|
| 114 |
+
$('#newline').onclick = () => terminalUI.newline();
|
|
|
|
| 115 |
$('#stop').onclick = guarded(() => rpc('stop'));
|
| 116 |
+
async function newChat() {
|
| 117 |
+
const state = await rpc('new_chat'); messages = state.messages;
|
| 118 |
+
terminalUI.setMessages(messages, { reset: true }); notice('');
|
| 119 |
+
}
|
| 120 |
+
$('#new-chat').onclick = guarded(newChat);
|
| 121 |
$('#editor').oninput = () => { dirty = true; controls(); };
|
| 122 |
$('#save').onclick = guarded(async () => { await rpc('write', { path: selected, content: $('#editor').value }); dirty = false; renderFiles(); controls(); notice(''); });
|
| 123 |
$('#add-file').onclick = guarded(async () => {
|
|
|
|
| 153 |
$('#terminal-output').scrollTop = $('#terminal-output').scrollHeight;
|
| 154 |
});
|
| 155 |
// Browser verification exercises the same worker and real Pi / WebGPU paths as the UI.
|
| 156 |
+
window.browserPi = { ready, load, prompt, rpc, diagnostics, terminal: terminalUI, get loaded() { return loaded; }, get busy() { return busy; } };
|
| 157 |
controls();
|
app/src/pi/browser-host.mjs
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Only Pi's terminal modules import this host adapter. No global Node shim.
|
| 2 |
+
export { Buffer } from 'buffer';
|
| 3 |
+
export const performance = globalThis.performance;
|
| 4 |
+
export const browserProcess = {
|
| 5 |
+
env: { TERM: 'xterm-256color', TERM_PROGRAM: 'xtermjs' },
|
| 6 |
+
platform: 'browser',
|
| 7 |
+
nextTick: callback => queueMicrotask(callback),
|
| 8 |
+
};
|
app/src/pi/capabilities.mjs
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// xterm.js renders text/ANSI; this app does not install Kitty/iTerm image addons.
|
| 2 |
+
// Pi's native image module also probes tmux via child_process. Keep that host
|
| 3 |
+
// dependency out of the browser and advertise only capabilities we implement.
|
| 4 |
+
let capabilities = { images: null, trueColor: true, hyperlinks: true };
|
| 5 |
+
let dimensions = { widthPx: 8, heightPx: 16 };
|
| 6 |
+
export const getCapabilities = () => capabilities;
|
| 7 |
+
export const setCapabilities = value => { capabilities = { ...value, images: null }; };
|
| 8 |
+
export const getCellDimensions = () => dimensions;
|
| 9 |
+
export const setCellDimensions = value => { dimensions = value; };
|
| 10 |
+
export const isImageLine = () => false;
|
| 11 |
+
export const getKittyImagePlacement = () => undefined;
|
| 12 |
+
export const getKittyImageMetadata = () => undefined;
|
| 13 |
+
export const cropKittyImageLine = line => line;
|
| 14 |
+
export const deleteKittyImage = () => '';
|
| 15 |
+
export const deleteAllKittyImages = () => '';
|
| 16 |
+
export const deleteAllKittyPlacements = () => '';
|
| 17 |
+
export function hyperlink(text, url) {
|
| 18 |
+
// Generated text may contain arbitrary URLs. Only explicit user clicks open
|
| 19 |
+
// HTTP(S) links, and no terminal control bytes may enter their OSC envelope.
|
| 20 |
+
try {
|
| 21 |
+
const parsed = new URL(url);
|
| 22 |
+
if (!['https:', 'http:'].includes(parsed.protocol) || /[\x00-\x20\x7f-\x9f]/.test(url)) return text;
|
| 23 |
+
return `\x1b]8;;${parsed.href}\x07${text}\x1b]8;;\x07`;
|
| 24 |
+
} catch { return text; }
|
| 25 |
+
}
|
app/src/pi/entry.mjs
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Import the real upstream components directly, excluding ProcessTerminal and
|
| 2 |
+
// native filesystem autocomplete from pi-tui's Node-oriented barrel export.
|
| 3 |
+
export { TuiAltScreen } from '@earendil-works/pi-tui/dist/tui-alt-screen.js';
|
| 4 |
+
export { Container } from '@earendil-works/pi-tui/dist/tui.js';
|
| 5 |
+
export { Editor } from '@earendil-works/pi-tui/dist/components/editor.js';
|
| 6 |
+
export { Text } from '@earendil-works/pi-tui/dist/components/text.js';
|
| 7 |
+
export { TruncatedText } from '@earendil-works/pi-tui/dist/components/truncated-text.js';
|
| 8 |
+
export { Markdown } from '@earendil-works/pi-tui/dist/components/markdown.js';
|
| 9 |
+
export { Box } from '@earendil-works/pi-tui/dist/components/box.js';
|
| 10 |
+
export { Spacer } from '@earendil-works/pi-tui/dist/components/spacer.js';
|
| 11 |
+
export { MouseRegion } from '@earendil-works/pi-tui/dist/components/mouse-region.js';
|
| 12 |
+
export { ScrollView } from '@earendil-works/pi-tui/dist/components/scroll-view.js';
|
| 13 |
+
export { VStack } from '@earendil-works/pi-tui/dist/components/v-stack.js';
|
| 14 |
+
export { KeybindingsManager, TUI_KEYBINDINGS, setKeybindings } from '@earendil-works/pi-tui/dist/keybindings.js';
|
| 15 |
+
export { matchesKey } from '@earendil-works/pi-tui/dist/keys.js';
|
| 16 |
+
export { StdinBuffer } from '@earendil-works/pi-tui/dist/stdin-buffer.js';
|
| 17 |
+
export { visibleWidth, stripTerminalSequences } from '@earendil-works/pi-tui/dist/utils.js';
|
app/src/pi/terminal-ui.mjs
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { Terminal } from '@xterm/xterm';
|
| 2 |
+
import { FitAddon } from '@xterm/addon-fit';
|
| 3 |
+
import '@xterm/xterm/css/xterm.css';
|
| 4 |
+
import { TuiAltScreen, Container, Text, TruncatedText, Box, Spacer, ScrollView, VStack,
|
| 5 |
+
KeybindingsManager, TUI_KEYBINDINGS, setKeybindings, stripTerminalSequences } from './entry.mjs';
|
| 6 |
+
import { CustomEditor } from '../../vendor/pi-coding-agent/components/custom-editor.js';
|
| 7 |
+
import { UserMessageComponent } from '../../vendor/pi-coding-agent/components/user-message.js';
|
| 8 |
+
import { AssistantMessageComponent } from '../../vendor/pi-coding-agent/components/assistant-message.js';
|
| 9 |
+
import { getEditorTheme, theme } from './theme.mjs';
|
| 10 |
+
import { XtermTerminal } from './xterm-terminal.mjs';
|
| 11 |
+
import { textContent } from '../protocol.mjs';
|
| 12 |
+
|
| 13 |
+
// Model output and workspace files are text, never terminal instructions.
|
| 14 |
+
export const terminalText = value => stripTerminalSequences(String(value ?? ''))
|
| 15 |
+
.replace(/\r\n?/g, '\n').replace(/[\x00-\x08\x0b-\x1f\x7f-\x9f]/g, '');
|
| 16 |
+
|
| 17 |
+
const commands = [
|
| 18 |
+
{ value: '/help', label: '/help', description: 'Browser terminal commands' },
|
| 19 |
+
{ value: '/load', label: '/load', description: 'Load MiniCPM on this device' },
|
| 20 |
+
{ value: '/new', label: '/new', description: 'Start a new conversation; keep files' },
|
| 21 |
+
{ value: '/clear', label: '/clear', description: 'Same as /new' },
|
| 22 |
+
];
|
| 23 |
+
|
| 24 |
+
export function createTerminalUI(element, actions) {
|
| 25 |
+
const xterm = new Terminal({
|
| 26 |
+
fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Consolas, monospace', fontSize: 13, lineHeight: 1.3,
|
| 27 |
+
theme: { background: '#ffffff', foreground: '#1f2328', cursor: '#5a8080', selectionBackground: '#d0d0e0' },
|
| 28 |
+
cursorBlink: false, scrollback: 2000, screenReaderMode: true, allowProposedApi: false,
|
| 29 |
+
});
|
| 30 |
+
const mount = document.createElement('div'); mount.className = 'pi-terminal-mount'; element.append(mount);
|
| 31 |
+
const fitAddon = new FitAddon(); xterm.loadAddon(fitAddon); xterm.open(mount);
|
| 32 |
+
const terminal = new XtermTerminal(xterm);
|
| 33 |
+
const ui = new TuiAltScreen(terminal, true, undefined, {
|
| 34 |
+
copyOnSelect: false,
|
| 35 |
+
copySelection: async text => { try { await navigator.clipboard.writeText(text); return true; } catch { return false; } },
|
| 36 |
+
openUrl: url => { if (/^https?:\/\//i.test(url)) window.open(url, '_blank', 'noopener,noreferrer'); },
|
| 37 |
+
scrollToEndIndicator: () => theme.fg('accent', ' β Latest '),
|
| 38 |
+
});
|
| 39 |
+
const keybindings = new KeybindingsManager({ ...TUI_KEYBINDINGS,
|
| 40 |
+
'app.interrupt': { defaultKeys: ['escape', 'ctrl+c'] }, 'app.exit': { defaultKeys: 'ctrl+d' },
|
| 41 |
+
'app.tools.expand': { defaultKeys: 'ctrl+o' },
|
| 42 |
+
'tui.editor.historyPrevious': { defaultKeys: 'ctrl+p' }, 'tui.editor.historyNext': { defaultKeys: 'ctrl+n' },
|
| 43 |
+
});
|
| 44 |
+
setKeybindings(keybindings);
|
| 45 |
+
const editor = new CustomEditor(ui, getEditorTheme(), keybindings, { paddingX: 1 });
|
| 46 |
+
const transcript = new Container();
|
| 47 |
+
const scroll = new ScrollView(transcript, { follow: 'end', primary: true, scrollbar: 'auto',
|
| 48 |
+
scrollbarTrackStyle: text => theme.fg('borderMuted', text), scrollbarThumbStyle: text => theme.fg('muted', text) });
|
| 49 |
+
const status = new TruncatedText('', 1, 0);
|
| 50 |
+
const footer = new TruncatedText(theme.fg('dim', '/workspace Β· MiniCPM5-2B Β· 4,096 context'), 1, 0);
|
| 51 |
+
ui.setLayoutRoot(new VStack([
|
| 52 |
+
{ component: scroll, basis: 0, grow: 1, minSize: 2 },
|
| 53 |
+
{ component: new VStack([status, editor, footer]), basis: 'auto', shrink: 0 },
|
| 54 |
+
]));
|
| 55 |
+
ui.setFocus(editor);
|
| 56 |
+
let state = { loaded: false, busy: false, activity: '' }, files = [], messages = [], expanded = false;
|
| 57 |
+
let inputPending = false;
|
| 58 |
+
const components = new Map();
|
| 59 |
+
const localEntries = [];
|
| 60 |
+
const history = new Set();
|
| 61 |
+
const welcome = new Container();
|
| 62 |
+
welcome.addChild(new Text(theme.fg('accent', theme.bold('Ο Pi')) + theme.fg('muted', ' browser workspace'), 1, 1));
|
| 63 |
+
welcome.addChild(new Text('Ask Pi to work with the files on the right.\n\n/load load MiniCPM\n/help show commands\n!ls run a workspace command', 1, 0));
|
| 64 |
+
welcome.addChild(new Spacer(1));
|
| 65 |
+
|
| 66 |
+
function addLocal(text, error = false) {
|
| 67 |
+
localEntries.push({ at: messages.length, component: new Text(theme.fg(error ? 'error' : 'muted', terminalText(text)), 1, 1) });
|
| 68 |
+
renderMessages(); ui.scrollToBottom();
|
| 69 |
+
}
|
| 70 |
+
function remember(text) {
|
| 71 |
+
if (!text || history.has(text)) return;
|
| 72 |
+
history.add(text); editor.addToHistory(text);
|
| 73 |
+
}
|
| 74 |
+
function toolComponent(call, result) {
|
| 75 |
+
const container = new Container();
|
| 76 |
+
container.addChild(new Spacer(1));
|
| 77 |
+
const box = new Box(1, 0, text => theme.bg(result ? result.isError ? 'toolErrorBg' : 'toolSuccessBg' : 'toolPendingBg', text));
|
| 78 |
+
const title = `${result ? result.isError ? 'Γ' : 'β' : 'Β·'} ${call.name} ${call.arguments?.path ?? call.arguments?.command ?? ''}`;
|
| 79 |
+
box.addChild(new Text(theme.bold(terminalText(title)), 0, 0));
|
| 80 |
+
const args = terminalText(JSON.stringify(call.arguments, null, 2));
|
| 81 |
+
const output = terminalText(result ? textContent(result.content) : 'Runningβ¦');
|
| 82 |
+
const lines = output.split('\n');
|
| 83 |
+
box.addChild(new Text(theme.fg('toolOutput', expanded ? args + '\n\n' + output : lines.slice(0, 8).join('\n')), 0, 0));
|
| 84 |
+
if (!expanded && lines.length > 8) box.addChild(new Text(theme.fg('dim', `β¦ ${lines.length - 8} more lines Β· Ctrl+O to expand`), 0, 0));
|
| 85 |
+
container.addChild(box); return container;
|
| 86 |
+
}
|
| 87 |
+
function renderMessages() {
|
| 88 |
+
transcript.clear(); transcript.addChild(welcome);
|
| 89 |
+
const results = new Map(messages.filter(m => m.role === 'toolResult').map(m => [m.toolCallId, m]));
|
| 90 |
+
const liveKeys = new Set();
|
| 91 |
+
for (let i = 0; i <= messages.length; i++) {
|
| 92 |
+
for (const local of localEntries) if (local.at === i) transcript.addChild(local.component);
|
| 93 |
+
const message = messages[i];
|
| 94 |
+
if (!message || message.role === 'toolResult') continue;
|
| 95 |
+
const key = message.role + ':' + message.timestamp + ':' + i; liveKeys.add(key);
|
| 96 |
+
let cached = components.get(key);
|
| 97 |
+
if (!cached) {
|
| 98 |
+
cached = { component: message.role === 'user' ? new UserMessageComponent(terminalText(textContent(message.content))) : new AssistantMessageComponent() };
|
| 99 |
+
components.set(key, cached);
|
| 100 |
+
}
|
| 101 |
+
if (message.role === 'user') remember(textContent(message.content));
|
| 102 |
+
if (message.role === 'assistant' && cached.message !== message) {
|
| 103 |
+
const clean = { ...message, content: message.content.map(block => block.type === 'text' ? { ...block, text: terminalText(block.text) } :
|
| 104 |
+
block.type === 'thinking' ? { ...block, thinking: terminalText(block.thinking) } : block),
|
| 105 |
+
errorMessage: message.errorMessage ? terminalText(message.errorMessage) : undefined };
|
| 106 |
+
cached.component.updateContent(clean, !message.stopReason);
|
| 107 |
+
}
|
| 108 |
+
cached.message = message; transcript.addChild(cached.component);
|
| 109 |
+
for (const call of Array.isArray(message.content) ? message.content : []) {
|
| 110 |
+
if (call.type !== 'toolCall') continue;
|
| 111 |
+
const result = results.get(call.id), toolKey = 'tool:' + call.id; liveKeys.add(toolKey);
|
| 112 |
+
const signature = JSON.stringify([call, result, expanded]);
|
| 113 |
+
let tool = components.get(toolKey);
|
| 114 |
+
if (tool?.signature !== signature) { tool = { signature, component: toolComponent(call, result) }; components.set(toolKey, tool); }
|
| 115 |
+
transcript.addChild(tool.component);
|
| 116 |
+
}
|
| 117 |
+
}
|
| 118 |
+
for (const key of components.keys()) if (!liveKeys.has(key)) components.delete(key);
|
| 119 |
+
ui.requestRender();
|
| 120 |
+
}
|
| 121 |
+
function refreshStatus() {
|
| 122 |
+
status.text = theme.fg(state.busy ? 'accent' : 'muted', terminalText(state.activity || (state.loaded ? 'Ready' : 'Load the model to chat Β· /help')));
|
| 123 |
+
editor.disableSubmit = state.busy || inputPending;
|
| 124 |
+
editor.borderColor = text => theme.fg(state.busy ? 'accent' : 'borderMuted', text);
|
| 125 |
+
actions.onInput?.(editor.getText()); ui.requestRender();
|
| 126 |
+
}
|
| 127 |
+
async function submit(text) {
|
| 128 |
+
if (!text.trim() || state.busy || inputPending) return;
|
| 129 |
+
inputPending = true; refreshStatus();
|
| 130 |
+
remember(text); ui.scrollToBottom();
|
| 131 |
+
try {
|
| 132 |
+
const trimmed = text.trim();
|
| 133 |
+
if (trimmed === '/help') {
|
| 134 |
+
addLocal('/load Load MiniCPM on this device\n/new Clear the conversation; keep workspace files\n!cmd Run cmd with just-bash\n\nEnter: send Β· Shift+Enter: newline\nβ/β: edit/history Β· Ctrl+P/N: history\nEsc or Ctrl+C: stop Β· Ctrl+O: expand tools\nCtrl+Shift+F: search transcript\n\nPiβs original terminal UI and agent core run here.\nNode CLI extensions, npm, Python and native binaries are unavailable.');
|
| 135 |
+
} else if (trimmed === '/load') { await actions.load(); }
|
| 136 |
+
else if (trimmed === '/new' || trimmed === '/clear') { await actions.newChat(); localEntries.length = 0; renderMessages(); }
|
| 137 |
+
else if (trimmed.startsWith('/')) { throw Error('Unknown browser command. Type /help for available commands.'); }
|
| 138 |
+
else if (trimmed.startsWith('!')) {
|
| 139 |
+
const command = trimmed.slice(1).trim(); if (!command) throw Error('Enter a shell command after !.');
|
| 140 |
+
const result = await actions.shell(command);
|
| 141 |
+
addLocal(`$ ${command}\n${result.stdout}${result.stderr}\n[exit ${result.exitCode}]`, result.exitCode !== 0);
|
| 142 |
+
} else { await actions.prompt(text); }
|
| 143 |
+
} catch (error) {
|
| 144 |
+
addLocal(error.message, true);
|
| 145 |
+
// Preserve a failed submission without overwriting a draft typed meanwhile.
|
| 146 |
+
if (!editor.getText()) editor.setText(text);
|
| 147 |
+
} finally { inputPending = false; refreshStatus(); }
|
| 148 |
+
}
|
| 149 |
+
editor.onSubmit = text => { void submit(text); };
|
| 150 |
+
editor.onChange = () => { actions.onInput?.(editor.getText()); ui.requestRender(); };
|
| 151 |
+
editor.onEscape = () => { if (state.busy) void actions.stop().catch(error => addLocal(error.message, true)); };
|
| 152 |
+
editor.onCtrlD = () => addLocal('This terminal lives in your browser. Close the tab to leave.');
|
| 153 |
+
editor.onAction('app.tools.expand', () => { expanded = !expanded; renderMessages(); });
|
| 154 |
+
editor.setAutocompleteProvider({
|
| 155 |
+
getSuggestions(lines, line, col) {
|
| 156 |
+
const before = lines[line].slice(0, col), prefix = before.match(/\S*$/)?.[0] ?? '';
|
| 157 |
+
if (!prefix) return null;
|
| 158 |
+
const candidates = before.startsWith('/') ? commands : files.map(path => ({ value: path, label: path, description: 'workspace file' }));
|
| 159 |
+
const items = candidates.filter(item => item.value.startsWith(prefix) && item.value !== prefix);
|
| 160 |
+
return items.length ? { items, prefix } : null;
|
| 161 |
+
},
|
| 162 |
+
applyCompletion(lines, line, col, item, prefix) {
|
| 163 |
+
const updated = [...lines]; updated[line] = lines[line].slice(0, col - prefix.length) + item.value + ' ' + lines[line].slice(col);
|
| 164 |
+
return { lines: updated, cursorLine: line, cursorCol: col - prefix.length + item.value.length + 1 };
|
| 165 |
+
},
|
| 166 |
+
});
|
| 167 |
+
xterm.attachCustomKeyEventHandler(event => {
|
| 168 |
+
if (event.type !== 'keydown' || event.isComposing) return true;
|
| 169 |
+
// Let the browser deliver its native paste event (no clipboard permission
|
| 170 |
+
// request). xterm then applies bracketed paste before Pi sees the text.
|
| 171 |
+
if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === 'v') return false;
|
| 172 |
+
// Legacy xterm input folds Ctrl+Shift+F into Ctrl+F. Preserve the modifier
|
| 173 |
+
// in CSI-u form for Pi's existing fullscreen transcript-search binding.
|
| 174 |
+
if (event.ctrlKey && event.shiftKey && event.code === 'KeyF') {
|
| 175 |
+
event.preventDefault(); terminal.inputData('\x1b[102;6u'); return false;
|
| 176 |
+
}
|
| 177 |
+
if (event.key === 'Enter' && event.shiftKey) { event.preventDefault(); terminal.inputData('\x1b\r'); return false; }
|
| 178 |
+
if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === 'c' && (xterm.hasSelection() || ui.hasActiveSelection())) {
|
| 179 |
+
if (ui.hasActiveSelection()) { event.preventDefault(); void ui.copyActiveSelectionToClipboard(); }
|
| 180 |
+
return false;
|
| 181 |
+
}
|
| 182 |
+
return true;
|
| 183 |
+
});
|
| 184 |
+
// xterm's alternate buffer has no native scrollback. Route touch gestures to
|
| 185 |
+
// Pi's ScrollView while leaving taps/IME handling with xterm itself.
|
| 186 |
+
let touchY;
|
| 187 |
+
element.addEventListener('touchstart', event => { if (event.touches.length === 1) touchY = event.touches[0].clientY; }, { passive: true });
|
| 188 |
+
element.addEventListener('touchmove', event => {
|
| 189 |
+
if (touchY === undefined || event.touches.length !== 1) return;
|
| 190 |
+
const y = event.touches[0].clientY, rows = Math.trunc((touchY - y) / 18);
|
| 191 |
+
if (rows) { ui.scrollBy(rows); touchY = y; event.preventDefault(); }
|
| 192 |
+
}, { passive: false });
|
| 193 |
+
element.addEventListener('touchend', () => { touchY = undefined; }, { passive: true });
|
| 194 |
+
function fit() {
|
| 195 |
+
if (element.clientWidth > 0 && element.clientHeight > 0) { fitAddon.fit(); ui.requestRender(); }
|
| 196 |
+
}
|
| 197 |
+
const observer = new ResizeObserver(fit); observer.observe(element);
|
| 198 |
+
fit(); ui.start(); renderMessages(); refreshStatus();
|
| 199 |
+
document.fonts.ready.then(fit);
|
| 200 |
+
xterm.textarea.setAttribute('aria-label', 'Message Pi');
|
| 201 |
+
xterm.textarea.setAttribute('autocapitalize', 'off');
|
| 202 |
+
xterm.textarea.setAttribute('spellcheck', 'false');
|
| 203 |
+
return {
|
| 204 |
+
setMessages(value, { reset = false } = {}) { messages = value; if (reset) { localEntries.length = 0; components.clear(); } renderMessages(); },
|
| 205 |
+
setFiles(value) { files = Object.keys(value).map(path => path.replace(/^\/workspace\//, '')); },
|
| 206 |
+
setState(value) { state = { ...state, ...value }; refreshStatus(); },
|
| 207 |
+
submit() { const text = editor.getExpandedText(); if (!editor.disableSubmit && text.trim()) { editor.setText(''); return submit(text); } },
|
| 208 |
+
focus() { xterm.focus(); },
|
| 209 |
+
newline() { terminal.inputData('\x1b\r'); xterm.focus(); },
|
| 210 |
+
get text() { return editor.getExpandedText(); },
|
| 211 |
+
get cols() { return xterm.cols; }, get rows() { return xterm.rows; },
|
| 212 |
+
get viewportTop() { return ui.viewportTop; },
|
| 213 |
+
readScreen() { return Array.from({ length: xterm.buffer.active.length }, (_, i) => xterm.buffer.active.getLine(i)?.translateToString(true) ?? '').join('\n'); },
|
| 214 |
+
dispose() { observer.disconnect(); ui.stop({ preserveScreen: true }); xterm.dispose(); },
|
| 215 |
+
};
|
| 216 |
+
}
|
app/src/pi/theme.mjs
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import palette from '../../vendor/pi-coding-agent/theme/light.json';
|
| 2 |
+
|
| 3 |
+
// Upstream Pi light palette; browser styling replaces the theme file watcher
|
| 4 |
+
// and Node's syntax highlighter. Code blocks use Pi's plain-code fallback.
|
| 5 |
+
function color(name, text, background = false) {
|
| 6 |
+
const value = palette.vars[palette.colors[name]] ?? palette.colors[name] ?? palette.vars.text;
|
| 7 |
+
const rgb = value.slice(1).match(/../g).map(part => parseInt(part, 16)).join(';');
|
| 8 |
+
const open = `\x1b[${background ? 48 : 38};2;${rgb}m`, close = `\x1b[${background ? 49 : 39}m`;
|
| 9 |
+
return open + String(text).replaceAll(close, open) + close;
|
| 10 |
+
}
|
| 11 |
+
export const theme = {
|
| 12 |
+
fg: (name, text) => color(name, text), bg: (name, text) => color(name, text, true),
|
| 13 |
+
bold: text => `\x1b[1m${text}\x1b[22m`, italic: text => `\x1b[3m${text}\x1b[23m`,
|
| 14 |
+
underline: text => `\x1b[4m${text}\x1b[24m`,
|
| 15 |
+
};
|
| 16 |
+
export function getMarkdownTheme() {
|
| 17 |
+
return {
|
| 18 |
+
...Object.fromEntries(Object.entries({ heading: 'mdHeading', link: 'mdLink', linkUrl: 'mdLinkUrl', code: 'mdCode',
|
| 19 |
+
codeBlock: 'mdCodeBlock', codeBlockBorder: 'mdCodeBlockBorder', quote: 'mdQuote', quoteBorder: 'mdQuoteBorder',
|
| 20 |
+
hr: 'mdHr', listBullet: 'mdListBullet' }).map(([key, value]) => [key, text => theme.fg(value, text)])),
|
| 21 |
+
bold: theme.bold, italic: theme.italic, underline: theme.underline,
|
| 22 |
+
strikethrough: text => `\x1b[9m${text}\x1b[29m`,
|
| 23 |
+
highlightCode: code => code.split('\n').map(line => theme.fg('mdCodeBlock', line)),
|
| 24 |
+
};
|
| 25 |
+
}
|
| 26 |
+
export function getEditorTheme() {
|
| 27 |
+
return { borderColor: text => theme.fg('borderMuted', text), selectList: {
|
| 28 |
+
selectedPrefix: text => theme.fg('accent', text), selectedText: text => theme.fg('accent', text),
|
| 29 |
+
description: text => theme.fg('muted', text), scrollInfo: text => theme.fg('muted', text), noMatch: text => theme.fg('muted', text),
|
| 30 |
+
} };
|
| 31 |
+
}
|
app/src/pi/xterm-terminal.mjs
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { StdinBuffer } from './entry.mjs';
|
| 2 |
+
|
| 3 |
+
// Implements Pi's Terminal interface using xterm.js instead of a Node PTY.
|
| 4 |
+
export class XtermTerminal {
|
| 5 |
+
constructor(xterm) { this.xterm = xterm; this.subscriptions = []; }
|
| 6 |
+
start(onInput, onResize) {
|
| 7 |
+
this.input = new StdinBuffer();
|
| 8 |
+
this.input.on('data', onInput);
|
| 9 |
+
this.input.on('paste', text => onInput(`\x1b[200~${text}\x1b[201~`));
|
| 10 |
+
this.subscriptions.push(this.xterm.onData(data => this.input.process(data)), this.xterm.onResize(onResize));
|
| 11 |
+
this.write('\x1b[?2004h');
|
| 12 |
+
}
|
| 13 |
+
stop() {
|
| 14 |
+
this.subscriptions.forEach(subscription => subscription.dispose()); this.subscriptions = [];
|
| 15 |
+
this.input?.destroy(); this.write('\x1b[?2004l');
|
| 16 |
+
}
|
| 17 |
+
async drainInput() { /* There is no parent process or SSH input to drain. */ }
|
| 18 |
+
write(data) { this.xterm.write(data); }
|
| 19 |
+
get columns() { return this.xterm.cols; }
|
| 20 |
+
get rows() { return this.xterm.rows; }
|
| 21 |
+
get kittyProtocolActive() { return false; }
|
| 22 |
+
moveBy(lines) { if (lines) this.write(`\x1b[${Math.abs(lines)}${lines > 0 ? 'B' : 'A'}`); }
|
| 23 |
+
hideCursor() { this.write('\x1b[?25l'); }
|
| 24 |
+
showCursor() { this.write('\x1b[?25h'); }
|
| 25 |
+
clearLine() { this.write('\x1b[K'); }
|
| 26 |
+
clearFromCursor() { this.write('\x1b[J'); }
|
| 27 |
+
clearScreen() { this.write('\x1b[2J\x1b[H'); }
|
| 28 |
+
setTitle(title) { this.xterm.element?.setAttribute('aria-label', title); }
|
| 29 |
+
setProgress(active) { this.xterm.element?.setAttribute('aria-busy', String(active)); }
|
| 30 |
+
inputData(data) { this.input?.process(data); }
|
| 31 |
+
}
|
app/src/style.css
CHANGED
|
@@ -64,30 +64,15 @@ progress { width: 100%; height: 5px; margin-top: 8px; accent-color: var(--accent
|
|
| 64 |
.muted { margin-top: 4px; color: var(--muted); font-size: 11px; }
|
| 65 |
.muted:empty { display: none; }
|
| 66 |
|
| 67 |
-
#
|
| 68 |
-
#
|
| 69 |
-
.
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
.
|
| 73 |
-
.suggestions button { display: flex; justify-content: space-between; padding: 8px 10px; background: transparent; text-align: left; font-size: 12px; }
|
| 74 |
-
.suggestions button:hover { background: #ededed; }
|
| 75 |
-
.suggestions span { color: var(--muted); }
|
| 76 |
-
|
| 77 |
-
.message { margin: 0 0 14px; line-height: 1.55; overflow-wrap: anywhere; }
|
| 78 |
-
.message-label { margin-bottom: 4px; color: var(--muted); font-size: 10px; font-weight: 600; }
|
| 79 |
-
.message-body { white-space: pre-wrap; }
|
| 80 |
-
.message.user { border-left: 2px solid #c9c9c9; padding-left: 10px; }
|
| 81 |
-
.tool { margin: 6px 0; border: 1px solid var(--line); border-radius: 4px; background: var(--panel); font-size: 12px; }
|
| 82 |
-
.tool summary { cursor: pointer; padding: 7px 9px; font-family: ui-monospace, monospace; }
|
| 83 |
-
.tool pre { max-height: 240px; margin: 0; padding: 0 10px 9px; overflow: auto; white-space: pre-wrap; overflow-wrap: anywhere; font-size: 11px; }
|
| 84 |
-
.error { color: #a12c28; }
|
| 85 |
-
.message-stats { margin-top: 4px; color: var(--muted); font-size: 10px; }
|
| 86 |
#notice { margin: 6px 0; padding: 8px 10px; border: 1px solid #cfcfcf; border-radius: 4px; background: #f0f0f0; font-size: 12px; }
|
| 87 |
|
| 88 |
-
|
| 89 |
-
#prompt { width: 100%; min-height: 48px; max-height: 160px; padding: 6px; resize: vertical; border: 0; background: transparent; font-size: 13px; line-height: 1.45; }
|
| 90 |
-
.composer-footer { display: flex; align-items: center; justify-content: flex-end; gap: 6px; }
|
| 91 |
.composer-footer span { margin-right: auto; padding-left: 6px; color: var(--muted); font-size: 10px; }
|
| 92 |
footer { display: flex; justify-content: space-between; gap: 8px; padding-top: 7px; color: var(--muted); font-size: 10px; }
|
| 93 |
|
|
@@ -116,21 +101,10 @@ footer { display: flex; justify-content: space-between; gap: 8px; padding-top: 7
|
|
| 116 |
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0; }
|
| 117 |
|
| 118 |
#open-tab { margin-top: 8px; font-size: 12px; color: var(--accent); text-decoration: none; white-space: normal; }
|
| 119 |
-
.markdown { white-space: normal; }
|
| 120 |
-
.markdown p { margin: 0 0 8px; }
|
| 121 |
-
.markdown pre { padding: 9px 10px; border: 1px solid var(--line); border-radius: 4px; background: #f0f0f0; overflow: auto; white-space: pre-wrap; overflow-wrap: anywhere; }
|
| 122 |
-
.markdown code { font: 12px/1.5 ui-monospace, monospace; }
|
| 123 |
-
.markdown :not(pre) > code { padding: 1px 4px; border-radius: 3px; background: #ededed; }
|
| 124 |
-
.markdown a { color: var(--accent); text-underline-offset: 2px; }
|
| 125 |
-
.markdown table { border-collapse: collapse; font-size: 12px; }
|
| 126 |
-
.markdown td, .markdown th { padding: 4px 7px; border: 1px solid var(--line); }
|
| 127 |
-
|
| 128 |
@media (max-width: 850px) {
|
| 129 |
header { padding: 0 12px; }
|
| 130 |
main { display: flex; flex-direction: column; height: auto; }
|
| 131 |
-
.conversation {
|
| 132 |
-
#messages { flex: 1; max-height: 65dvh; }
|
| 133 |
-
#welcome { margin: 18px 4px; }
|
| 134 |
.workspace { min-height: 520px; border-left: 0; border-top: 1px solid var(--line); }
|
| 135 |
.file-area { min-height: 280px; grid-template-columns: 115px minmax(0, 1fr); }
|
| 136 |
.header-right > span:not(.local-dot) { display: none; }
|
|
@@ -144,6 +118,7 @@ footer { display: flex; justify-content: space-between; gap: 8px; padding-top: 7
|
|
| 144 |
@media (pointer: coarse) {
|
| 145 |
button, .button, #files button { min-height: 38px; }
|
| 146 |
.editor-heading { min-height: 40px; }
|
| 147 |
-
.
|
| 148 |
-
#
|
|
|
|
| 149 |
}
|
|
|
|
| 64 |
.muted { margin-top: 4px; color: var(--muted); font-size: 11px; }
|
| 65 |
.muted:empty { display: none; }
|
| 66 |
|
| 67 |
+
#pi-terminal { flex: 1; min-height: 180px; min-width: 0; overflow: hidden; margin-top: 10px; padding: 8px; border: 1px solid var(--line); border-radius: 4px; background: #fff; }
|
| 68 |
+
#pi-terminal .pi-terminal-mount, #pi-terminal .xterm { height: 100%; }
|
| 69 |
+
#pi-terminal .xterm-viewport { background-color: #fff !important; }
|
| 70 |
+
#pi-terminal textarea:focus { box-shadow: none; }
|
| 71 |
+
.terminal-controls { display: flex; align-items: center; gap: 6px; padding-top: 7px; }
|
| 72 |
+
.terminal-controls > span { flex: 1; min-width: 0; color: var(--muted); font-size: 10px; }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 73 |
#notice { margin: 6px 0; padding: 8px 10px; border: 1px solid #cfcfcf; border-radius: 4px; background: #f0f0f0; font-size: 12px; }
|
| 74 |
|
| 75 |
+
footer { display: flex; align-items: center; justify-content: flex-end; gap: 6px; }
|
|
|
|
|
|
|
| 76 |
.composer-footer span { margin-right: auto; padding-left: 6px; color: var(--muted); font-size: 10px; }
|
| 77 |
footer { display: flex; justify-content: space-between; gap: 8px; padding-top: 7px; color: var(--muted); font-size: 10px; }
|
| 78 |
|
|
|
|
| 101 |
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0; }
|
| 102 |
|
| 103 |
#open-tab { margin-top: 8px; font-size: 12px; color: var(--accent); text-decoration: none; white-space: normal; }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 104 |
@media (max-width: 850px) {
|
| 105 |
header { padding: 0 12px; }
|
| 106 |
main { display: flex; flex-direction: column; height: auto; }
|
| 107 |
+
.conversation { height: calc(100dvh - 46px); min-height: 500px; padding: 10px; }
|
|
|
|
|
|
|
| 108 |
.workspace { min-height: 520px; border-left: 0; border-top: 1px solid var(--line); }
|
| 109 |
.file-area { min-height: 280px; grid-template-columns: 115px minmax(0, 1fr); }
|
| 110 |
.header-right > span:not(.local-dot) { display: none; }
|
|
|
|
| 118 |
@media (pointer: coarse) {
|
| 119 |
button, .button, #files button { min-height: 38px; }
|
| 120 |
.editor-heading { min-height: 40px; }
|
| 121 |
+
.terminal summary { padding-top: 11px; padding-bottom: 11px; }
|
| 122 |
+
#command { font-size: 16px; }
|
| 123 |
+
#pi-terminal .xterm-helper-textarea { font-size: 16px; }
|
| 124 |
}
|
app/tests/browser.mjs
CHANGED
|
@@ -2,8 +2,8 @@ import { chromium } from 'playwright';
|
|
| 2 |
import fs from 'node:fs/promises';
|
| 3 |
import assert from 'node:assert/strict';
|
| 4 |
|
| 5 |
-
const base = process.env.APP_URL ?? 'http://127.0.0.1:4173/?source=local';
|
| 6 |
-
const label = process.env.TEST_LABEL ?? 'agent-browser';
|
| 7 |
const profile = process.env.BROWSER_PROFILE ?? new URL('../../validation/agent-browser-profile', import.meta.url).pathname;
|
| 8 |
const output = new URL(`../../validation/${label}.json`, import.meta.url);
|
| 9 |
const result = { date: new Date().toISOString(), url: base, cases: [], errors: [], requests: [] };
|
|
@@ -73,9 +73,18 @@ try {
|
|
| 73 |
} },
|
| 74 |
];
|
| 75 |
for (const task of tasks) {
|
| 76 |
-
|
|
|
|
|
|
|
| 77 |
const start = Date.now();
|
| 78 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 79 |
const entry = { name: task.name, elapsedMs: Date.now() - start, ...state };
|
| 80 |
result.cases.push(entry); console.log('CASE', JSON.stringify(entry));
|
| 81 |
task.check(state);
|
|
@@ -84,7 +93,8 @@ try {
|
|
| 84 |
const running = page.evaluate(() => window.browserPi.prompt('Write a very long detailed tutorial about shell scripting. Do not use tools.'));
|
| 85 |
await page.waitForFunction(() => window.browserPi.busy);
|
| 86 |
await page.waitForTimeout(900);
|
| 87 |
-
await page.locator('
|
|
|
|
| 88 |
const stopped = await running;
|
| 89 |
assert.equal(stopped.messages.filter(m => m.role === 'assistant').at(-1).stopReason, 'aborted');
|
| 90 |
result.cancellation = { passed: true, completedGenerations: await page.evaluate(() => window.browserPi.diagnostics.generations.length) - beforeStop };
|
|
|
|
| 2 |
import fs from 'node:fs/promises';
|
| 3 |
import assert from 'node:assert/strict';
|
| 4 |
|
| 5 |
+
const base = process.argv[2] ?? process.env.APP_URL ?? 'http://127.0.0.1:4173/?source=local';
|
| 6 |
+
const label = process.argv[3] ?? process.env.TEST_LABEL ?? 'agent-browser';
|
| 7 |
const profile = process.env.BROWSER_PROFILE ?? new URL('../../validation/agent-browser-profile', import.meta.url).pathname;
|
| 8 |
const output = new URL(`../../validation/${label}.json`, import.meta.url);
|
| 9 |
const result = { date: new Date().toISOString(), url: base, cases: [], errors: [], requests: [] };
|
|
|
|
| 73 |
} },
|
| 74 |
];
|
| 75 |
for (const task of tasks) {
|
| 76 |
+
console.log('START', task.name);
|
| 77 |
+
await page.locator('#new-chat').click();
|
| 78 |
+
await page.waitForFunction(() => !window.browserPi.busy);
|
| 79 |
const start = Date.now();
|
| 80 |
+
await page.locator('.xterm-helper-textarea').focus();
|
| 81 |
+
await page.keyboard.type(task.prompt);
|
| 82 |
+
console.log('TYPED', await page.evaluate(() => window.browserPi.terminal.text));
|
| 83 |
+
await page.waitForFunction(prompt => window.browserPi.terminal.text === prompt, task.prompt, { timeout: 15_000 });
|
| 84 |
+
await page.locator('#send').click();
|
| 85 |
+
await page.waitForFunction(() => window.browserPi.busy);
|
| 86 |
+
await page.waitForFunction(() => !window.browserPi.busy);
|
| 87 |
+
const state = await page.evaluate(() => window.browserPi.rpc('snapshot'));
|
| 88 |
const entry = { name: task.name, elapsedMs: Date.now() - start, ...state };
|
| 89 |
result.cases.push(entry); console.log('CASE', JSON.stringify(entry));
|
| 90 |
task.check(state);
|
|
|
|
| 93 |
const running = page.evaluate(() => window.browserPi.prompt('Write a very long detailed tutorial about shell scripting. Do not use tools.'));
|
| 94 |
await page.waitForFunction(() => window.browserPi.busy);
|
| 95 |
await page.waitForTimeout(900);
|
| 96 |
+
await page.locator('.xterm-helper-textarea').focus();
|
| 97 |
+
await page.keyboard.press('Escape');
|
| 98 |
const stopped = await running;
|
| 99 |
assert.equal(stopped.messages.filter(m => m.role === 'assistant').at(-1).stopReason, 'aborted');
|
| 100 |
result.cancellation = { passed: true, completedGenerations: await page.evaluate(() => window.browserPi.diagnostics.generations.length) - beforeStop };
|
app/tests/space-embedded.mjs
CHANGED
|
@@ -36,7 +36,8 @@ try {
|
|
| 36 |
result.loadMs = Date.now() - start;
|
| 37 |
result.device = await app.evaluate(() => window.browserPi.diagnostics.device);
|
| 38 |
await app.locator('#new-chat').click(); await app.waitForFunction(() => !window.browserPi.busy);
|
| 39 |
-
await app.locator('
|
|
|
|
| 40 |
await app.locator('#send').click();
|
| 41 |
await app.waitForFunction(() => window.browserPi.busy);
|
| 42 |
await app.waitForFunction(() => !window.browserPi.busy);
|
|
|
|
| 36 |
result.loadMs = Date.now() - start;
|
| 37 |
result.device = await app.evaluate(() => window.browserPi.diagnostics.device);
|
| 38 |
await app.locator('#new-chat').click(); await app.waitForFunction(() => !window.browserPi.busy);
|
| 39 |
+
await app.locator('.xterm-helper-textarea').focus();
|
| 40 |
+
await page.keyboard.type('Read sales.csv, calculate total revenue using bash, and save a short report to report.md. Verify the file.');
|
| 41 |
await app.locator('#send').click();
|
| 42 |
await app.waitForFunction(() => window.browserPi.busy);
|
| 43 |
await app.waitForFunction(() => !window.browserPi.busy);
|
app/tests/terminal.mjs
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { chromium } from 'playwright';
|
| 2 |
+
import fs from 'node:fs/promises';
|
| 3 |
+
import assert from 'node:assert/strict';
|
| 4 |
+
|
| 5 |
+
const url = process.argv[2] ?? process.env.APP_URL ?? 'http://127.0.0.1:4173/';
|
| 6 |
+
const label = process.argv[3] ?? process.env.TEST_LABEL ?? 'agent-terminal';
|
| 7 |
+
const result = { date: new Date().toISOString(), url, errors: [], requests: [], checks: [] };
|
| 8 |
+
const browser = await chromium.launch({ executablePath: '/usr/bin/chromium', headless: true });
|
| 9 |
+
const context = await browser.newContext({ viewport: { width: 1440, height: 1000 } });
|
| 10 |
+
context.setDefaultTimeout(15_000);
|
| 11 |
+
const page = await context.newPage();
|
| 12 |
+
page.on('pageerror', error => { result.errors.push(error.stack); console.log('PAGEERROR', error.stack); });
|
| 13 |
+
page.on('console', message => { if (message.type() === 'error') console.log('CONSOLE', message.text().slice(0, 500)); });
|
| 14 |
+
context.on('request', request => result.requests.push(request.url()));
|
| 15 |
+
const screen = () => page.evaluate(() => window.browserPi.terminal.readScreen());
|
| 16 |
+
const text = () => page.evaluate(() => window.browserPi.terminal.text);
|
| 17 |
+
async function type(value) { await page.locator('.xterm-helper-textarea').focus(); await page.keyboard.type(value); }
|
| 18 |
+
async function waitScreen(value) { await page.waitForFunction(value => window.browserPi.terminal.readScreen().includes(value), value); }
|
| 19 |
+
try {
|
| 20 |
+
await page.goto(url);
|
| 21 |
+
await page.waitForFunction(() => window.browserPi);
|
| 22 |
+
await page.evaluate(() => window.browserPi.ready);
|
| 23 |
+
await waitScreen('browser workspace');
|
| 24 |
+
result.initial = await screen();
|
| 25 |
+
assert(result.initial.includes('/load'));
|
| 26 |
+
result.checks.push('Original Pi TUI rendered through xterm');
|
| 27 |
+
|
| 28 |
+
await type('/help'); await page.keyboard.press('Enter'); await waitScreen('Ctrl+O');
|
| 29 |
+
result.checks.push('Enter submits /help without loading a model');
|
| 30 |
+
await page.keyboard.press('Control+Shift+f'); await type('browser');
|
| 31 |
+
await page.waitForFunction(() => window.browserPi.terminal.text === '' && /\d+\/\d+/.test(window.browserPi.terminal.readScreen()));
|
| 32 |
+
await page.keyboard.press('Escape'); await page.waitForTimeout(30);
|
| 33 |
+
result.checks.push('Pi fullscreen transcript search preserves the editor draft');
|
| 34 |
+
await type('one'); await page.keyboard.press('Shift+Enter'); await type('two');
|
| 35 |
+
assert.equal(await text(), 'one\ntwo');
|
| 36 |
+
await page.keyboard.press('Backspace'); await page.keyboard.press('Control+a'); await type('X');
|
| 37 |
+
assert.equal(await text(), 'one\nXtw');
|
| 38 |
+
await page.keyboard.press('Control+u'); await page.keyboard.press('Control+k');
|
| 39 |
+
await page.keyboard.press('Backspace'); await page.keyboard.press('Control+u');
|
| 40 |
+
assert.equal(await text(), '');
|
| 41 |
+
result.checks.push('Pi multiline editor: Shift+Enter, cursor, deletion, Ctrl+U/K');
|
| 42 |
+
await page.keyboard.press('Control+p'); assert.equal(await text(), '/help');
|
| 43 |
+
await page.keyboard.press('Control+n'); assert.equal(await text(), '');
|
| 44 |
+
result.checks.push('Pi prompt history');
|
| 45 |
+
await type('/he'); await page.keyboard.press('Tab');
|
| 46 |
+
await page.waitForFunction(() => window.browserPi.terminal.text === '/help ');
|
| 47 |
+
await page.keyboard.press('Enter'); await waitScreen('Ctrl+O');
|
| 48 |
+
result.checks.push('Pi autocomplete: /he β /help');
|
| 49 |
+
|
| 50 |
+
await type("!printf 'terminal-works\\n' > terminal.txt; cat terminal.txt");
|
| 51 |
+
await page.keyboard.press('Escape'); // Dismiss optional workspace completion.
|
| 52 |
+
await page.waitForTimeout(30); // A lone ESC is delimited by Pi's 10ms input timer.
|
| 53 |
+
await page.keyboard.press('Enter'); await waitScreen('[exit 0]');
|
| 54 |
+
await page.waitForFunction(() => !window.browserPi.busy);
|
| 55 |
+
const state = await page.evaluate(() => window.browserPi.rpc('snapshot'));
|
| 56 |
+
assert.equal(state.files['/workspace/terminal.txt'], 'terminal-works\n');
|
| 57 |
+
assert((await screen()).includes('[exit 0]'));
|
| 58 |
+
result.checks.push('xterm keyboard β Pi editor β real just-bash β workspace file');
|
| 59 |
+
|
| 60 |
+
// Model/file text must not be able to clear the terminal or inject OSC URLs.
|
| 61 |
+
await type("!printf 'before\\033[2Jafter\\n'");
|
| 62 |
+
await page.locator('#send').click(); await waitScreen('beforeafter');
|
| 63 |
+
result.checks.push('Shell escape sequences render as inert text');
|
| 64 |
+
|
| 65 |
+
// Paste uses xterm's actual bracketed-paste path through Pi's StdinBuffer.
|
| 66 |
+
await context.grantPermissions(['clipboard-read', 'clipboard-write']);
|
| 67 |
+
const largePaste = '!cat > pasted.txt <<\'EOF\'\n' + Array.from({ length: 15 }, (_, i) => 'row-' + i).join('\n') + '\nEOF';
|
| 68 |
+
await page.evaluate(text => navigator.clipboard.writeText(text), largePaste);
|
| 69 |
+
await page.locator('.xterm-helper-textarea').focus(); await page.keyboard.press('Control+v');
|
| 70 |
+
await page.waitForFunction(text => window.browserPi.terminal.text === text, largePaste);
|
| 71 |
+
await page.locator('#send').click();
|
| 72 |
+
await page.waitForFunction(() => document.querySelector('#files').textContent.includes('pasted.txt'));
|
| 73 |
+
const pastedState = await page.evaluate(() => window.browserPi.rpc('snapshot'));
|
| 74 |
+
assert.equal(pastedState.files['/workspace/pasted.txt'], Array.from({ length: 15 }, (_, i) => 'row-' + i).join('\n') + '\n');
|
| 75 |
+
result.checks.push('Large Pi paste markers expand before Send');
|
| 76 |
+
const pasted = 'hello δΈη π¦\nline two';
|
| 77 |
+
await page.evaluate(text => navigator.clipboard.writeText(text), pasted);
|
| 78 |
+
await page.locator('.xterm-helper-textarea').focus(); await page.keyboard.press('Control+v');
|
| 79 |
+
await page.waitForFunction(text => window.browserPi.terminal.text === text, pasted);
|
| 80 |
+
result.checks.push('Unicode multiline clipboard paste');
|
| 81 |
+
await page.locator('#newline').click(); assert.equal(await text(), pasted + '\n');
|
| 82 |
+
result.checks.push('Touch-accessible newline button');
|
| 83 |
+
|
| 84 |
+
// Resize while preserving a multiline draft and Pi's own viewport renderer.
|
| 85 |
+
const desktop = await page.evaluate(() => ({ cols: window.browserPi.terminal.cols, rows: window.browserPi.terminal.rows }));
|
| 86 |
+
await page.screenshot({ path: `../validation/${label}-desktop.png`, fullPage: true });
|
| 87 |
+
await page.setViewportSize({ width: 390, height: 844 });
|
| 88 |
+
await page.waitForFunction(cols => window.browserPi.terminal.cols < cols, desktop.cols);
|
| 89 |
+
assert.equal(await text(), pasted + '\n');
|
| 90 |
+
assert(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth));
|
| 91 |
+
result.sizes = { desktop, mobile: await page.evaluate(() => ({ cols: window.browserPi.terminal.cols, rows: window.browserPi.terminal.rows })) };
|
| 92 |
+
assert(result.sizes.mobile.cols >= 30); assert(result.sizes.mobile.rows >= 15);
|
| 93 |
+
await page.screenshot({ path: `../validation/${label}-mobile.png`, fullPage: true });
|
| 94 |
+
result.checks.push('390px layout and live terminal resize preserve the draft');
|
| 95 |
+
result.mobileLimitation = 'Desktop Chromium viewport simulation; physical mobile keyboards and GPUs not tested.';
|
| 96 |
+
assert.equal(result.errors.length, 0);
|
| 97 |
+
assert.equal(result.requests.filter(url => /^https?:/.test(url) && new URL(url).origin !== new URL(result.url).origin).length, 0);
|
| 98 |
+
result.passed = true; console.log(JSON.stringify({ passed: true, checks: result.checks, sizes: result.sizes }));
|
| 99 |
+
} catch (error) {
|
| 100 |
+
result.failure = error.stack;
|
| 101 |
+
result.screen = await screen().catch(() => 'App failed before initialization');
|
| 102 |
+
await page.screenshot({ path: `../validation/${label}-failure.png`, fullPage: true });
|
| 103 |
+
throw error;
|
| 104 |
+
} finally { await fs.writeFile(`../validation/${label}.json`, JSON.stringify(result, null, 2)); await browser.close(); }
|
app/vendor/pi-coding-agent/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
MIT License
|
| 2 |
+
|
| 3 |
+
Copyright (c) 2025 Mario Zechner
|
| 4 |
+
|
| 5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 6 |
+
of this software and associated documentation files (the "Software"), to deal
|
| 7 |
+
in the Software without restriction, including without limitation the rights
|
| 8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 9 |
+
copies of the Software, and to permit persons to whom the Software is
|
| 10 |
+
furnished to do so, subject to the following conditions:
|
| 11 |
+
|
| 12 |
+
The above copyright notice and this permission notice shall be included in all
|
| 13 |
+
copies or substantial portions of the Software.
|
| 14 |
+
|
| 15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
| 21 |
+
SOFTWARE.
|
app/vendor/pi-coding-agent/README.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Pi CLI presentation components
|
| 2 |
+
|
| 3 |
+
These files are **unmodified copies** from the published
|
| 4 |
+
`@earendil-works/pi-coding-agent@0.85.1` npm tarball, under `dist/modes/interactive/`.
|
| 5 |
+
`provenance.json` records their SHA-256 digests. Pi's MIT license is included.
|
| 6 |
+
|
| 7 |
+
- `CustomEditor` is the CLI's actual editor subclass, extending `pi-tui`'s editor.
|
| 8 |
+
- `UserMessageComponent` and `AssistantMessageComponent` are its actual message renderers.
|
| 9 |
+
- `markdown-transform.js` is their upstream helper.
|
| 10 |
+
- `light.json` is the upstream light theme palette.
|
| 11 |
+
|
| 12 |
+
The build redirects their `@earendil-works/pi-tui` import to a selective export
|
| 13 |
+
of the installed **real pi-tui** package. It redirects their Node theme service
|
| 14 |
+
to `src/pi/theme.mjs`, which uses the same palette without filesystem watchers or
|
| 15 |
+
Node syntax highlighting. See `scripts/pi-browser.mjs` for all host adaptations.
|
| 16 |
+
|
| 17 |
+
The rest of the Node CLI is not vendored. Browser shell tool cards, slash-command
|
| 18 |
+
dispatch, worker integration, and workspace controls are app code. They do not
|
| 19 |
+
claim to implement the full CLI's sessions, extensions, or provider management.
|
| 20 |
+
|
| 21 |
+
Upstream: https://github.com/earendil-works/pi/tree/v0.85.1/packages/coding-agent
|
app/vendor/pi-coding-agent/components/assistant-message.js
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { Container, Markdown, MouseRegion, Spacer, Text } from "@earendil-works/pi-tui";
|
| 2 |
+
import { getMarkdownTheme, theme } from "../theme/theme.js";
|
| 3 |
+
import { createMarkdownTransform } from "./markdown-transform.js";
|
| 4 |
+
const OSC133_ZONE_START = "\x1b]133;A\x07";
|
| 5 |
+
const OSC133_ZONE_END = "\x1b]133;B\x07";
|
| 6 |
+
const OSC133_ZONE_FINAL = "\x1b]133;C\x07";
|
| 7 |
+
/**
|
| 8 |
+
* Component that renders a complete assistant message
|
| 9 |
+
*/
|
| 10 |
+
export class AssistantMessageComponent extends Container {
|
| 11 |
+
contentContainer;
|
| 12 |
+
hideThinkingBlock;
|
| 13 |
+
markdownTheme;
|
| 14 |
+
hiddenThinkingLabel;
|
| 15 |
+
outputPad;
|
| 16 |
+
markdownTransformers;
|
| 17 |
+
lastMessage;
|
| 18 |
+
hasToolCalls = false;
|
| 19 |
+
isStreaming = false;
|
| 20 |
+
thinkingVisibilityOverrides = new Map();
|
| 21 |
+
constructor(message, hideThinkingBlock = false, markdownTheme = getMarkdownTheme(), hiddenThinkingLabel = "Thinking...", outputPad = 1, markdownTransformers = []) {
|
| 22 |
+
super();
|
| 23 |
+
this.hideThinkingBlock = hideThinkingBlock;
|
| 24 |
+
this.markdownTheme = markdownTheme;
|
| 25 |
+
this.hiddenThinkingLabel = hiddenThinkingLabel;
|
| 26 |
+
this.outputPad = outputPad;
|
| 27 |
+
this.markdownTransformers = markdownTransformers;
|
| 28 |
+
// Container for text/thinking content
|
| 29 |
+
this.contentContainer = new Container();
|
| 30 |
+
this.addChild(this.contentContainer);
|
| 31 |
+
if (message) {
|
| 32 |
+
this.updateContent(message);
|
| 33 |
+
}
|
| 34 |
+
}
|
| 35 |
+
invalidate() {
|
| 36 |
+
super.invalidate();
|
| 37 |
+
if (this.lastMessage) {
|
| 38 |
+
this.updateContent(this.lastMessage);
|
| 39 |
+
}
|
| 40 |
+
}
|
| 41 |
+
setHideThinkingBlock(hide) {
|
| 42 |
+
this.hideThinkingBlock = hide;
|
| 43 |
+
this.thinkingVisibilityOverrides.clear();
|
| 44 |
+
if (this.lastMessage) {
|
| 45 |
+
this.updateContent(this.lastMessage);
|
| 46 |
+
}
|
| 47 |
+
}
|
| 48 |
+
setHiddenThinkingLabel(label) {
|
| 49 |
+
this.hiddenThinkingLabel = label;
|
| 50 |
+
if (this.lastMessage) {
|
| 51 |
+
this.updateContent(this.lastMessage);
|
| 52 |
+
}
|
| 53 |
+
}
|
| 54 |
+
setOutputPad(padding) {
|
| 55 |
+
this.outputPad = padding;
|
| 56 |
+
if (this.lastMessage) {
|
| 57 |
+
this.updateContent(this.lastMessage);
|
| 58 |
+
}
|
| 59 |
+
}
|
| 60 |
+
render(width) {
|
| 61 |
+
const lines = super.render(width);
|
| 62 |
+
if (this.hasToolCalls || lines.length === 0) {
|
| 63 |
+
return lines;
|
| 64 |
+
}
|
| 65 |
+
lines[0] = OSC133_ZONE_START + lines[0];
|
| 66 |
+
lines[lines.length - 1] = OSC133_ZONE_END + OSC133_ZONE_FINAL + lines[lines.length - 1];
|
| 67 |
+
return lines;
|
| 68 |
+
}
|
| 69 |
+
updateContent(message, isStreaming = this.isStreaming) {
|
| 70 |
+
this.lastMessage = message;
|
| 71 |
+
this.isStreaming = isStreaming;
|
| 72 |
+
// Clear content container
|
| 73 |
+
this.contentContainer.clear();
|
| 74 |
+
const hasVisibleContent = message.content.some((c) => (c.type === "text" && c.text.trim()) || (c.type === "thinking" && c.thinking.trim()));
|
| 75 |
+
if (hasVisibleContent) {
|
| 76 |
+
this.contentContainer.addChild(new Spacer(1));
|
| 77 |
+
}
|
| 78 |
+
// Render content in order
|
| 79 |
+
let thinkingRunIndex = 0;
|
| 80 |
+
for (let i = 0; i < message.content.length; i++) {
|
| 81 |
+
const content = message.content[i];
|
| 82 |
+
if (content.type === "text" && content.text.trim()) {
|
| 83 |
+
// Assistant text messages with no background - trim the text
|
| 84 |
+
// Set paddingY=0 to avoid extra spacing before tool executions
|
| 85 |
+
this.contentContainer.addChild(new Markdown(content.text.trim(), this.outputPad, 0, this.markdownTheme, undefined, {
|
| 86 |
+
transform: createMarkdownTransform("assistant", this.isStreaming, this.markdownTransformers),
|
| 87 |
+
}));
|
| 88 |
+
}
|
| 89 |
+
else if (content.type === "thinking") {
|
| 90 |
+
const thinkingBlocks = [];
|
| 91 |
+
for (; i < message.content.length; i++) {
|
| 92 |
+
const thinkingContent = message.content[i];
|
| 93 |
+
if (thinkingContent.type !== "thinking") {
|
| 94 |
+
break;
|
| 95 |
+
}
|
| 96 |
+
const thinking = thinkingContent.thinking.trim();
|
| 97 |
+
if (thinking) {
|
| 98 |
+
thinkingBlocks.push(thinking);
|
| 99 |
+
}
|
| 100 |
+
}
|
| 101 |
+
i--;
|
| 102 |
+
if (thinkingBlocks.length === 0) {
|
| 103 |
+
continue;
|
| 104 |
+
}
|
| 105 |
+
// Add spacing only when another visible assistant content block follows.
|
| 106 |
+
// This avoids a superfluous blank line before separately-rendered tool execution blocks.
|
| 107 |
+
const hasVisibleContentAfter = message.content
|
| 108 |
+
.slice(i + 1)
|
| 109 |
+
.some((c) => (c.type === "text" && c.text.trim()) || (c.type === "thinking" && c.thinking.trim()));
|
| 110 |
+
const runIndex = thinkingRunIndex++;
|
| 111 |
+
const hidden = this.thinkingVisibilityOverrides.get(runIndex) ?? this.hideThinkingBlock;
|
| 112 |
+
const thinkingComponent = hidden
|
| 113 |
+
? new Text(theme.italic(theme.fg("thinkingText", this.hiddenThinkingLabel)), this.outputPad, 0)
|
| 114 |
+
: new Markdown(thinkingBlocks.join("\n\n"), this.outputPad, 0, this.markdownTheme, {
|
| 115 |
+
color: (text) => theme.fg("thinkingText", text),
|
| 116 |
+
italic: true,
|
| 117 |
+
}, {
|
| 118 |
+
transform: createMarkdownTransform("assistant-thinking", this.isStreaming, this.markdownTransformers),
|
| 119 |
+
});
|
| 120 |
+
this.contentContainer.addChild(new MouseRegion(thinkingComponent, (event) => {
|
| 121 |
+
if (event.type !== "click" || event.button !== "left")
|
| 122 |
+
return undefined;
|
| 123 |
+
this.thinkingVisibilityOverrides.set(runIndex, !hidden);
|
| 124 |
+
if (this.lastMessage)
|
| 125 |
+
this.updateContent(this.lastMessage);
|
| 126 |
+
return { handled: true };
|
| 127 |
+
}));
|
| 128 |
+
if (hasVisibleContentAfter) {
|
| 129 |
+
this.contentContainer.addChild(new Spacer(1));
|
| 130 |
+
}
|
| 131 |
+
}
|
| 132 |
+
}
|
| 133 |
+
// Check if incomplete/failed - show after partial content.
|
| 134 |
+
// For aborted/error tool calls, tool execution components show the error.
|
| 135 |
+
// Length stops can happen before a tool call is complete, so surface them here too.
|
| 136 |
+
const hasToolCalls = message.content.some((c) => c.type === "toolCall");
|
| 137 |
+
this.hasToolCalls = hasToolCalls;
|
| 138 |
+
if (message.stopReason === "length") {
|
| 139 |
+
this.contentContainer.addChild(new Spacer(1));
|
| 140 |
+
this.contentContainer.addChild(new Text(theme.fg("error", "Response was truncated before completion."), this.outputPad, 0));
|
| 141 |
+
}
|
| 142 |
+
else if (!hasToolCalls) {
|
| 143 |
+
if (message.stopReason === "aborted") {
|
| 144 |
+
const abortMessage = message.errorMessage && message.errorMessage !== "Request was aborted"
|
| 145 |
+
? message.errorMessage
|
| 146 |
+
: "Operation aborted";
|
| 147 |
+
this.contentContainer.addChild(new Spacer(1));
|
| 148 |
+
this.contentContainer.addChild(new Text(theme.fg("error", abortMessage), this.outputPad, 0));
|
| 149 |
+
}
|
| 150 |
+
else if (message.stopReason === "error") {
|
| 151 |
+
const errorMsg = message.errorMessage || "Unknown error";
|
| 152 |
+
this.contentContainer.addChild(new Spacer(1));
|
| 153 |
+
this.contentContainer.addChild(new Text(theme.fg("error", `Error: ${errorMsg}`), this.outputPad, 0));
|
| 154 |
+
}
|
| 155 |
+
}
|
| 156 |
+
}
|
| 157 |
+
}
|
| 158 |
+
//# sourceMappingURL=assistant-message.js.map
|
app/vendor/pi-coding-agent/components/custom-editor.js
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { Editor, visibleWidth } from "@earendil-works/pi-tui";
|
| 2 |
+
/**
|
| 3 |
+
* Custom editor that handles app-level keybindings for coding-agent.
|
| 4 |
+
*/
|
| 5 |
+
export class CustomEditor extends Editor {
|
| 6 |
+
keybindings;
|
| 7 |
+
workingStatusIndicator;
|
| 8 |
+
embedWorkingStatus;
|
| 9 |
+
actionHandlers = new Map();
|
| 10 |
+
// Special handlers that can be dynamically replaced
|
| 11 |
+
onEscape;
|
| 12 |
+
onCtrlD;
|
| 13 |
+
onPasteImage;
|
| 14 |
+
/** Handler for extension-registered shortcuts. Returns true if handled. */
|
| 15 |
+
onExtensionShortcut;
|
| 16 |
+
constructor(tui, theme, keybindings, options) {
|
| 17 |
+
super(tui, theme, options);
|
| 18 |
+
this.keybindings = keybindings;
|
| 19 |
+
this.embedWorkingStatus = options?.embedWorkingStatus ?? false;
|
| 20 |
+
}
|
| 21 |
+
setWorkingStatusIndicator(indicator) {
|
| 22 |
+
this.workingStatusIndicator = indicator;
|
| 23 |
+
}
|
| 24 |
+
renderTopBorder(width, hiddenLineCount) {
|
| 25 |
+
if (!this.embedWorkingStatus || !this.workingStatusIndicator || width <= 0) {
|
| 26 |
+
return super.renderTopBorder(width, hiddenLineCount);
|
| 27 |
+
}
|
| 28 |
+
let status = this.workingStatusIndicator.renderInBorder(Math.max(1, width - 5));
|
| 29 |
+
let statusWidth = visibleWidth(status);
|
| 30 |
+
if (statusWidth === 0)
|
| 31 |
+
return super.renderTopBorder(width, hiddenLineCount);
|
| 32 |
+
const overflowLabel = hiddenLineCount > 0 ? ` β ${hiddenLineCount} more ` : undefined;
|
| 33 |
+
const overflowLabelWidth = overflowLabel ? visibleWidth(overflowLabel) : 0;
|
| 34 |
+
const overflowStart = Math.floor((width - overflowLabelWidth) / 2);
|
| 35 |
+
const canFitOverflow = () => overflowLabel !== undefined && overflowLabelWidth + 2 <= width && overflowStart - (3 + statusWidth + 1) >= 1;
|
| 36 |
+
if (overflowLabel && !canFitOverflow()) {
|
| 37 |
+
status = this.workingStatusIndicator.renderSpinnerInBorder(width);
|
| 38 |
+
statusWidth = visibleWidth(status);
|
| 39 |
+
}
|
| 40 |
+
if (canFitOverflow()) {
|
| 41 |
+
const leftBlockWidth = 3 + statusWidth + 1;
|
| 42 |
+
return (this.borderColor("ββ ") +
|
| 43 |
+
status +
|
| 44 |
+
this.borderColor(` ${"β".repeat(overflowStart - leftBlockWidth)}${overflowLabel}${"β".repeat(width - overflowStart - overflowLabelWidth)}`));
|
| 45 |
+
}
|
| 46 |
+
if (width >= statusWidth + 5) {
|
| 47 |
+
return this.borderColor("ββ ") + status + this.borderColor(` ${"β".repeat(width - statusWidth - 4)}`);
|
| 48 |
+
}
|
| 49 |
+
status = this.workingStatusIndicator.renderSpinnerInBorder(width);
|
| 50 |
+
statusWidth = visibleWidth(status);
|
| 51 |
+
const prefixWidth = Math.min(3, Math.max(0, width - statusWidth));
|
| 52 |
+
return (this.borderColor("β".repeat(prefixWidth)) +
|
| 53 |
+
status +
|
| 54 |
+
this.borderColor("β".repeat(Math.max(0, width - prefixWidth - statusWidth))));
|
| 55 |
+
}
|
| 56 |
+
/**
|
| 57 |
+
* Register a handler for an app action.
|
| 58 |
+
*/
|
| 59 |
+
onAction(action, handler) {
|
| 60 |
+
this.actionHandlers.set(action, handler);
|
| 61 |
+
}
|
| 62 |
+
handleInput(data) {
|
| 63 |
+
// Check extension-registered shortcuts first
|
| 64 |
+
if (this.onExtensionShortcut?.(data)) {
|
| 65 |
+
return;
|
| 66 |
+
}
|
| 67 |
+
// Check for clipboard paste keybinding
|
| 68 |
+
if (this.keybindings.matches(data, "app.clipboard.pasteImage")) {
|
| 69 |
+
this.onPasteImage?.();
|
| 70 |
+
return;
|
| 71 |
+
}
|
| 72 |
+
// Check app keybindings first
|
| 73 |
+
// Escape/interrupt - only if autocomplete is NOT active
|
| 74 |
+
if (this.keybindings.matches(data, "app.interrupt")) {
|
| 75 |
+
if (!this.isShowingAutocomplete()) {
|
| 76 |
+
// Use dynamic onEscape if set, otherwise registered handler
|
| 77 |
+
const handler = this.onEscape ?? this.actionHandlers.get("app.interrupt");
|
| 78 |
+
if (handler) {
|
| 79 |
+
handler();
|
| 80 |
+
return;
|
| 81 |
+
}
|
| 82 |
+
}
|
| 83 |
+
// Let parent handle escape for autocomplete cancellation
|
| 84 |
+
super.handleInput(data);
|
| 85 |
+
return;
|
| 86 |
+
}
|
| 87 |
+
// Exit (Ctrl+D) - only when editor is empty
|
| 88 |
+
if (this.keybindings.matches(data, "app.exit")) {
|
| 89 |
+
if (this.getText().length === 0) {
|
| 90 |
+
const handler = this.onCtrlD ?? this.actionHandlers.get("app.exit");
|
| 91 |
+
if (handler)
|
| 92 |
+
handler();
|
| 93 |
+
return;
|
| 94 |
+
}
|
| 95 |
+
// Fall through to editor handling for delete-char-forward when not empty
|
| 96 |
+
}
|
| 97 |
+
// Explicit history bindings take precedence over app actions while the editor is focused.
|
| 98 |
+
// This lets users bind Ctrl+P even though it cycles models by default.
|
| 99 |
+
if (this.keybindings.matches(data, "tui.editor.historyPrevious") ||
|
| 100 |
+
this.keybindings.matches(data, "tui.editor.historyNext")) {
|
| 101 |
+
super.handleInput(data);
|
| 102 |
+
return;
|
| 103 |
+
}
|
| 104 |
+
// Check all other app actions
|
| 105 |
+
for (const [action, handler] of this.actionHandlers) {
|
| 106 |
+
if (action !== "app.interrupt" && action !== "app.exit" && this.keybindings.matches(data, action)) {
|
| 107 |
+
handler();
|
| 108 |
+
return;
|
| 109 |
+
}
|
| 110 |
+
}
|
| 111 |
+
// Pass to parent for editor handling
|
| 112 |
+
super.handleInput(data);
|
| 113 |
+
}
|
| 114 |
+
}
|
| 115 |
+
//# sourceMappingURL=custom-editor.js.map
|
app/vendor/pi-coding-agent/components/markdown-transform.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
export function createMarkdownTransform(messageType, isStreaming, transformers) {
|
| 2 |
+
return (markdown, availableWidth) => applyMarkdownTransformers(markdown, { messageType, isStreaming, availableWidth }, transformers);
|
| 3 |
+
}
|
| 4 |
+
function applyMarkdownTransformers(markdown, context, transformers) {
|
| 5 |
+
let transformedMarkdown = markdown;
|
| 6 |
+
for (const transformer of transformers) {
|
| 7 |
+
try {
|
| 8 |
+
const transformed = transformer(transformedMarkdown, context);
|
| 9 |
+
if (typeof transformed === "string") {
|
| 10 |
+
transformedMarkdown = transformed;
|
| 11 |
+
}
|
| 12 |
+
}
|
| 13 |
+
catch {
|
| 14 |
+
// Keep the current Markdown and continue with the next transformer.
|
| 15 |
+
}
|
| 16 |
+
}
|
| 17 |
+
return transformedMarkdown;
|
| 18 |
+
}
|
| 19 |
+
//# sourceMappingURL=markdown-transform.js.map
|
app/vendor/pi-coding-agent/components/user-message.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { Box, Container, Markdown } from "@earendil-works/pi-tui";
|
| 2 |
+
import { getMarkdownTheme, theme } from "../theme/theme.js";
|
| 3 |
+
import { createMarkdownTransform } from "./markdown-transform.js";
|
| 4 |
+
const OSC133_ZONE_START = "\x1b]133;A\x07";
|
| 5 |
+
const OSC133_ZONE_END = "\x1b]133;B\x07";
|
| 6 |
+
const OSC133_ZONE_FINAL = "\x1b]133;C\x07";
|
| 7 |
+
/**
|
| 8 |
+
* Component that renders a user message
|
| 9 |
+
*/
|
| 10 |
+
export class UserMessageComponent extends Container {
|
| 11 |
+
text;
|
| 12 |
+
markdownTheme;
|
| 13 |
+
outputPad;
|
| 14 |
+
markdownTransformers;
|
| 15 |
+
constructor(text, markdownTheme = getMarkdownTheme(), outputPad = 1, markdownTransformers = []) {
|
| 16 |
+
super();
|
| 17 |
+
this.text = text;
|
| 18 |
+
this.markdownTheme = markdownTheme;
|
| 19 |
+
this.outputPad = outputPad;
|
| 20 |
+
this.markdownTransformers = markdownTransformers;
|
| 21 |
+
this.rebuild();
|
| 22 |
+
}
|
| 23 |
+
setOutputPad(padding) {
|
| 24 |
+
this.outputPad = padding;
|
| 25 |
+
this.rebuild();
|
| 26 |
+
}
|
| 27 |
+
rebuild() {
|
| 28 |
+
this.clear();
|
| 29 |
+
const contentBox = new Box(this.outputPad, 1, (content) => theme.bg("userMessageBg", content));
|
| 30 |
+
contentBox.addChild(new Markdown(this.text, 0, 0, this.markdownTheme, {
|
| 31 |
+
color: (content) => theme.fg("userMessageText", content),
|
| 32 |
+
}, {
|
| 33 |
+
preserveOrderedListMarkers: true,
|
| 34 |
+
preserveBackslashEscapes: true,
|
| 35 |
+
transform: createMarkdownTransform("user", false, this.markdownTransformers),
|
| 36 |
+
}));
|
| 37 |
+
this.addChild(contentBox);
|
| 38 |
+
}
|
| 39 |
+
render(width) {
|
| 40 |
+
const lines = super.render(width);
|
| 41 |
+
if (lines.length === 0) {
|
| 42 |
+
return lines;
|
| 43 |
+
}
|
| 44 |
+
lines[0] = OSC133_ZONE_START + lines[0];
|
| 45 |
+
lines[lines.length - 1] = OSC133_ZONE_END + OSC133_ZONE_FINAL + lines[lines.length - 1];
|
| 46 |
+
return lines;
|
| 47 |
+
}
|
| 48 |
+
}
|
| 49 |
+
//# sourceMappingURL=user-message.js.map
|
app/vendor/pi-coding-agent/provenance.json
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"package": "@earendil-works/pi-coding-agent",
|
| 3 |
+
"version": "0.85.1",
|
| 4 |
+
"source": "https://github.com/earendil-works/pi/tree/v0.85.1/packages/coding-agent",
|
| 5 |
+
"files": {
|
| 6 |
+
"components/custom-editor.js": "000b0056f2bf645537537468bdd6f6ee0c82e4110f5417c1c70c2e8fefc04f29",
|
| 7 |
+
"components/user-message.js": "398a34b8a979daea5614be4cbbe6ff66e7eca97e9fd855508791dcddce994bc8",
|
| 8 |
+
"components/assistant-message.js": "edbd1e712234609d2ad1078edcf960e3b6f549669075f1c5d1a7a4481e2b3d19",
|
| 9 |
+
"components/markdown-transform.js": "31ad2245189328104a31bb9b12f58b0a12e4af9c7924775a242c9dcd526d2c1a",
|
| 10 |
+
"theme/light.json": "14c7172ba7e75eab6f509504de806af0f2d9ff817bfb45d9533bff15c7e6e657"
|
| 11 |
+
}
|
| 12 |
+
}
|
app/vendor/pi-coding-agent/theme/light.json
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"$schema": "https://raw.githubusercontent.com/earendil-works/pi/main/packages/coding-agent/src/modes/interactive/theme/theme-schema.json",
|
| 3 |
+
"name": "light",
|
| 4 |
+
"vars": {
|
| 5 |
+
"teal": "#5a8080",
|
| 6 |
+
"blue": "#547da7",
|
| 7 |
+
"green": "#588458",
|
| 8 |
+
"red": "#aa5555",
|
| 9 |
+
"yellow": "#9a7326",
|
| 10 |
+
"text": "#1f2328",
|
| 11 |
+
"mediumGray": "#6c6c6c",
|
| 12 |
+
"dimGray": "#767676",
|
| 13 |
+
"lightGray": "#b0b0b0",
|
| 14 |
+
"selectedBg": "#d0d0e0",
|
| 15 |
+
"userMsgBg": "#e8e8e8",
|
| 16 |
+
"toolPendingBg": "#e8e8f0",
|
| 17 |
+
"toolSuccessBg": "#e8f0e8",
|
| 18 |
+
"toolErrorBg": "#f0e8e8",
|
| 19 |
+
"customMsgBg": "#ede7f6"
|
| 20 |
+
},
|
| 21 |
+
"colors": {
|
| 22 |
+
"accent": "teal",
|
| 23 |
+
"border": "blue",
|
| 24 |
+
"borderAccent": "teal",
|
| 25 |
+
"borderMuted": "lightGray",
|
| 26 |
+
"success": "green",
|
| 27 |
+
"error": "red",
|
| 28 |
+
"warning": "yellow",
|
| 29 |
+
"muted": "mediumGray",
|
| 30 |
+
"dim": "dimGray",
|
| 31 |
+
"text": "text",
|
| 32 |
+
"thinkingText": "mediumGray",
|
| 33 |
+
|
| 34 |
+
"selectedBg": "selectedBg",
|
| 35 |
+
"scrollbarTrack": "lightGray",
|
| 36 |
+
"scrollbarThumb": "text",
|
| 37 |
+
"searchMatchBg": "selectedBg",
|
| 38 |
+
"searchMatchText": "text",
|
| 39 |
+
"userMessageBg": "userMsgBg",
|
| 40 |
+
"userMessageText": "text",
|
| 41 |
+
"customMessageBg": "customMsgBg",
|
| 42 |
+
"customMessageText": "text",
|
| 43 |
+
"customMessageLabel": "#7e57c2",
|
| 44 |
+
"toolPendingBg": "toolPendingBg",
|
| 45 |
+
"toolSuccessBg": "toolSuccessBg",
|
| 46 |
+
"toolErrorBg": "toolErrorBg",
|
| 47 |
+
"toolTitle": "text",
|
| 48 |
+
"toolOutput": "mediumGray",
|
| 49 |
+
|
| 50 |
+
"mdHeading": "yellow",
|
| 51 |
+
"mdLink": "blue",
|
| 52 |
+
"mdLinkUrl": "dimGray",
|
| 53 |
+
"mdCode": "teal",
|
| 54 |
+
"mdCodeBlock": "green",
|
| 55 |
+
"mdCodeBlockBorder": "mediumGray",
|
| 56 |
+
"mdQuote": "mediumGray",
|
| 57 |
+
"mdQuoteBorder": "mediumGray",
|
| 58 |
+
"mdHr": "mediumGray",
|
| 59 |
+
"mdListBullet": "green",
|
| 60 |
+
|
| 61 |
+
"toolDiffAdded": "green",
|
| 62 |
+
"toolDiffRemoved": "red",
|
| 63 |
+
"toolDiffContext": "mediumGray",
|
| 64 |
+
|
| 65 |
+
"syntaxComment": "#008000",
|
| 66 |
+
"syntaxKeyword": "#0000FF",
|
| 67 |
+
"syntaxFunction": "#795E26",
|
| 68 |
+
"syntaxVariable": "#001080",
|
| 69 |
+
"syntaxString": "#A31515",
|
| 70 |
+
"syntaxNumber": "#098658",
|
| 71 |
+
"syntaxType": "#267F99",
|
| 72 |
+
"syntaxOperator": "#000000",
|
| 73 |
+
"syntaxPunctuation": "#000000",
|
| 74 |
+
|
| 75 |
+
"thinkingOff": "lightGray",
|
| 76 |
+
"thinkingMinimal": "#767676",
|
| 77 |
+
"thinkingLow": "blue",
|
| 78 |
+
"thinkingMedium": "teal",
|
| 79 |
+
"thinkingHigh": "#875f87",
|
| 80 |
+
"thinkingXhigh": "#8b008b",
|
| 81 |
+
"thinkingMax": "#af005f",
|
| 82 |
+
|
| 83 |
+
"bashMode": "green"
|
| 84 |
+
},
|
| 85 |
+
"export": {
|
| 86 |
+
"pageBg": "#f8f8f8",
|
| 87 |
+
"cardBg": "#ffffff",
|
| 88 |
+
"infoBg": "#fffae6"
|
| 89 |
+
}
|
| 90 |
+
}
|
app/vite.config.mjs
CHANGED
|
@@ -2,14 +2,16 @@ import { defineConfig } from 'vite';
|
|
| 2 |
import { cpSync, mkdirSync, readdirSync, createReadStream, statSync } from 'node:fs';
|
| 3 |
import { resolve } from 'node:path';
|
| 4 |
import { collectLicenses } from './scripts/licenses.mjs';
|
|
|
|
| 5 |
|
| 6 |
export default defineConfig({
|
| 7 |
base: './',
|
| 8 |
worker: { format: 'es' },
|
|
|
|
| 9 |
resolve: { alias: { '@huggingface/transformers': resolve('node_modules/@huggingface/transformers/dist/transformers.web.js') } },
|
| 10 |
server: { proxy: { '/models': 'http://127.0.0.1:8765' } },
|
| 11 |
preview: { proxy: { '/models': 'http://127.0.0.1:8765' } },
|
| 12 |
-
plugins: [{
|
| 13 |
name: 'local-ort-runtime',
|
| 14 |
configureServer(server) {
|
| 15 |
server.middlewares.use('/runtime', (req, res, next) => {
|
|
|
|
| 2 |
import { cpSync, mkdirSync, readdirSync, createReadStream, statSync } from 'node:fs';
|
| 3 |
import { resolve } from 'node:path';
|
| 4 |
import { collectLicenses } from './scripts/licenses.mjs';
|
| 5 |
+
import { piBrowser } from './scripts/pi-browser.mjs';
|
| 6 |
|
| 7 |
export default defineConfig({
|
| 8 |
base: './',
|
| 9 |
worker: { format: 'es' },
|
| 10 |
+
optimizeDeps: { exclude: ['@earendil-works/pi-tui'], include: ['events', 'buffer'] },
|
| 11 |
resolve: { alias: { '@huggingface/transformers': resolve('node_modules/@huggingface/transformers/dist/transformers.web.js') } },
|
| 12 |
server: { proxy: { '/models': 'http://127.0.0.1:8765' } },
|
| 13 |
preview: { proxy: { '/models': 'http://127.0.0.1:8765' } },
|
| 14 |
+
plugins: [piBrowser(), {
|
| 15 |
name: 'local-ort-runtime',
|
| 16 |
configureServer(server) {
|
| 17 |
server.middlewares.use('/runtime', (req, res, next) => {
|
assets/index-BRKUEVCq.js
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
assets/index-Cfkr7NLu.css
DELETED
|
@@ -1 +0,0 @@
|
|
| 1 |
-
:root{font-synthesis:none;color:#262626;--surface:#fff;--panel:#fafafa;--line:#dedede;--muted:#686868;--accent:#303030;background:#f5f5f5;font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;font-size:13px}*{box-sizing:border-box}body{margin:0}button,input,textarea{font:inherit}button,.button{cursor:pointer;border:1px solid var(--line);background:var(--surface);min-height:28px;color:inherit;white-space:nowrap;border-radius:4px;padding:5px 9px;font-size:12px;line-height:1.4}button:hover,.button:hover{background:#ededed}button:disabled{opacity:.45;cursor:default}.primary{background:var(--accent);border-color:var(--accent);color:#fff}.primary:hover{background:#171717}.quiet{background:0 0;border-color:#0000;font-size:11px}textarea,input{outline:none}textarea:focus,input:focus{box-shadow:inset 0 0 0 1px #888}button:focus-visible,.button:focus-visible{outline-offset:2px;outline:2px solid #737373}[hidden]{display:none!important}header{border-bottom:1px solid var(--line);background:#f7f7f7;justify-content:space-between;align-items:center;height:46px;padding:0 16px;display:flex}.brand{color:inherit;align-items:center;gap:8px;font-size:15px;font-weight:600;text-decoration:none;display:flex}.pi{font-family:Georgia,serif;font-size:28px;line-height:1}.brand small{color:var(--muted);margin-left:8px;font-size:11px;font-weight:400;display:inline}.header-right{align-items:center;gap:6px;font-size:11px;display:flex}.header-right button{margin-left:12px}.local-dot{background:#737373;border-radius:50%;width:5px;height:5px}main{grid-template-columns:minmax(420px,1fr) minmax(370px,.83fr);max-width:1800px;height:calc(100dvh - 46px);margin:auto;display:grid}.conversation{flex-direction:column;min-width:0;min-height:0;padding:12px 14px 8px;display:flex}.model-panel{border:1px solid var(--line);background:var(--panel);border-radius:4px;padding:9px 10px}.model-heading{justify-content:space-between;align-items:center;gap:8px;display:flex}.model-heading>div{flex-wrap:wrap;align-items:center;gap:7px;display:flex}.model-heading strong{font-size:12px;font-weight:600}.model-panel p{color:var(--muted);margin:5px 0 0;font-size:11px;line-height:1.45}.badge{color:#595959;background:#f0f0f0;border:1px solid #d9d9d9;border-radius:3px;padding:2px 5px;font-size:10px}progress{width:100%;height:5px;accent-color:var(--accent);margin-top:8px}.muted{color:var(--muted);margin-top:4px;font-size:11px}.muted:empty{display:none}#messages{scrollbar-width:thin;flex:1;min-height:120px;padding:14px 2px;overflow:auto}#welcome{max-width:470px;margin:22px 8px 0}.eyebrow{color:var(--muted);font-size:11px}h1{letter-spacing:-.025em;margin:8px 0;font-family:inherit;font-size:22px;font-weight:600;line-height:1.25}#welcome p{color:var(--muted);margin:8px 0;font-size:12px;line-height:1.6}.suggestions{flex-direction:column;gap:6px;margin-top:16px;display:flex}.suggestions button{text-align:left;background:0 0;justify-content:space-between;padding:8px 10px;font-size:12px;display:flex}.suggestions button:hover{background:#ededed}.suggestions span{color:var(--muted)}.message{overflow-wrap:anywhere;margin:0 0 14px;line-height:1.55}.message-label{color:var(--muted);margin-bottom:4px;font-size:10px;font-weight:600}.message-body{white-space:pre-wrap}.message.user{border-left:2px solid #c9c9c9;padding-left:10px}.tool{border:1px solid var(--line);background:var(--panel);border-radius:4px;margin:6px 0;font-size:12px}.tool summary{cursor:pointer;padding:7px 9px;font-family:ui-monospace,monospace}.tool pre{white-space:pre-wrap;overflow-wrap:anywhere;max-height:240px;margin:0;padding:0 10px 9px;font-size:11px;overflow:auto}.error{color:#a12c28}.message-stats{color:var(--muted);margin-top:4px;font-size:10px}#notice{background:#f0f0f0;border:1px solid #cfcfcf;border-radius:4px;margin:6px 0;padding:8px 10px;font-size:12px}#chat-form{background:var(--surface);border:1px solid #c8c8c8;border-radius:4px;padding:6px}#prompt{resize:vertical;background:0 0;border:0;width:100%;min-height:48px;max-height:160px;padding:6px;font-size:13px;line-height:1.45}.composer-footer{justify-content:flex-end;align-items:center;gap:6px;display:flex}.composer-footer span{color:var(--muted);margin-right:auto;padding-left:6px;font-size:10px}footer{color:var(--muted);justify-content:space-between;gap:8px;padding-top:7px;font-size:10px;display:flex}.workspace{border-left:1px solid var(--line);background:#f1f1f1;flex-direction:column;min-width:0;min-height:0;display:flex}.workspace-heading{justify-content:space-between;align-items:center;min-height:40px;padding:5px 10px;display:flex}.workspace-heading h2{margin:0;font-size:12px;font-weight:600}.workspace-heading>div{gap:0;display:flex}.button{display:inline-block}.file-area{border-top:1px solid var(--line);border-bottom:1px solid var(--line);flex:1;grid-template-columns:140px minmax(0,1fr);min-height:180px;display:grid}#files{border-right:1px solid var(--line);padding:5px 4px;overflow:auto}#files button{text-align:left;white-space:normal;overflow-wrap:anywhere;background:0 0;border:none;border-radius:3px;width:100%;padding:5px 7px;font-family:ui-monospace,monospace;font-size:11px;line-height:1.45;display:block}#files button:hover{background:#e9e9e9}#files button.selected{color:#202020;background:#dedede}.editor-area{background:var(--surface);flex-direction:column;min-width:0;min-height:0;display:flex}.editor-heading{border-bottom:1px solid var(--line);background:var(--panel);justify-content:space-between;align-items:center;gap:6px;height:32px;padding:0 6px 0 10px;display:flex}#filename{text-overflow:ellipsis;white-space:nowrap;font-family:ui-monospace,monospace;font-size:11px;overflow:hidden}#editor{resize:none;color:#333;tab-size:2;white-space:pre;background:0 0;border:0;flex:1;min-height:140px;padding:10px 12px;font:12px/1.6 ui-monospace,SFMono-Regular,Consolas,monospace;overflow:auto}.terminal{background:#f5f5f5;padding:0 10px}.terminal summary{cursor:pointer;padding:9px 0;font-size:11px;font-weight:500}.terminal summary span{float:right;color:var(--muted);font-size:10px;font-weight:400}#terminal-output{white-space:pre-wrap;overflow-wrap:anywhere;height:112px;margin:0;font:11px/1.5 ui-monospace,monospace;overflow:auto}#shell-form{border-top:1px solid var(--line);align-items:center;gap:6px;padding:4px 0;font-family:ui-monospace,monospace;display:flex}#command{background:0 0;border:0;width:100%;min-width:0;padding:6px 0;font-size:12px}.workspace-note{color:var(--muted);margin:8px 10px;font-size:10px;line-height:1.45}.sr-only{clip:rect(0,0,0,0);white-space:nowrap;border:0;width:1px;height:1px;padding:0;position:absolute;overflow:hidden}#open-tab{color:var(--accent);white-space:normal;margin-top:8px;font-size:12px;text-decoration:none}.markdown{white-space:normal}.markdown p{margin:0 0 8px}.markdown pre{border:1px solid var(--line);white-space:pre-wrap;overflow-wrap:anywhere;background:#f0f0f0;border-radius:4px;padding:9px 10px;overflow:auto}.markdown code{font:12px/1.5 ui-monospace,monospace}.markdown :not(pre)>code{background:#ededed;border-radius:3px;padding:1px 4px}.markdown a{color:var(--accent);text-underline-offset:2px}.markdown table{border-collapse:collapse;font-size:12px}.markdown td,.markdown th{border:1px solid var(--line);padding:4px 7px}@media (width<=850px){header{padding:0 12px}main{flex-direction:column;height:auto;display:flex}.conversation{min-height:calc(100dvh - 46px);padding:10px}#messages{flex:1;max-height:65dvh}#welcome{margin:18px 4px}.workspace{border-left:0;border-top:1px solid var(--line);min-height:520px}.file-area{grid-template-columns:115px minmax(0,1fr);min-height:280px}.header-right>span:not(.local-dot){display:none}.header-right button{margin-left:0}h1{font-size:20px}.badge{font-size:9px}.model-panel{padding:8px}footer{font-size:9px}}@media (pointer:coarse){button,.button,#files button{min-height:38px}.editor-heading{min-height:40px}.tool summary,.terminal summary{padding-top:11px;padding-bottom:11px}#prompt,#command{font-size:16px}}
|
|
|
|
|
|
assets/index-DL-Ot0hN.css
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
:root{font-synthesis:none;color:#262626;--surface:#fff;--panel:#fafafa;--line:#dedede;--muted:#686868;--accent:#303030;background:#f5f5f5;font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;font-size:13px}*{box-sizing:border-box}body{margin:0}button,input,textarea{font:inherit}button,.button{cursor:pointer;border:1px solid var(--line);background:var(--surface);min-height:28px;color:inherit;white-space:nowrap;border-radius:4px;padding:5px 9px;font-size:12px;line-height:1.4}button:hover,.button:hover{background:#ededed}button:disabled{opacity:.45;cursor:default}.primary{background:var(--accent);border-color:var(--accent);color:#fff}.primary:hover{background:#171717}.quiet{background:0 0;border-color:#0000;font-size:11px}textarea,input{outline:none}textarea:focus,input:focus{box-shadow:inset 0 0 0 1px #888}button:focus-visible,.button:focus-visible{outline-offset:2px;outline:2px solid #737373}[hidden]{display:none!important}header{border-bottom:1px solid var(--line);background:#f7f7f7;justify-content:space-between;align-items:center;height:46px;padding:0 16px;display:flex}.brand{color:inherit;align-items:center;gap:8px;font-size:15px;font-weight:600;text-decoration:none;display:flex}.pi{font-family:Georgia,serif;font-size:28px;line-height:1}.brand small{color:var(--muted);margin-left:8px;font-size:11px;font-weight:400;display:inline}.header-right{align-items:center;gap:6px;font-size:11px;display:flex}.header-right button{margin-left:12px}.local-dot{background:#737373;border-radius:50%;width:5px;height:5px}main{grid-template-columns:minmax(420px,1fr) minmax(370px,.83fr);max-width:1800px;height:calc(100dvh - 46px);margin:auto;display:grid}.conversation{flex-direction:column;min-width:0;min-height:0;padding:12px 14px 8px;display:flex}.model-panel{border:1px solid var(--line);background:var(--panel);border-radius:4px;padding:9px 10px}.model-heading{justify-content:space-between;align-items:center;gap:8px;display:flex}.model-heading>div{flex-wrap:wrap;align-items:center;gap:7px;display:flex}.model-heading strong{font-size:12px;font-weight:600}.model-panel p{color:var(--muted);margin:5px 0 0;font-size:11px;line-height:1.45}.badge{color:#595959;background:#f0f0f0;border:1px solid #d9d9d9;border-radius:3px;padding:2px 5px;font-size:10px}progress{width:100%;height:5px;accent-color:var(--accent);margin-top:8px}.muted{color:var(--muted);margin-top:4px;font-size:11px}.muted:empty{display:none}#pi-terminal{border:1px solid var(--line);background:#fff;border-radius:4px;flex:1;min-width:0;min-height:180px;margin-top:10px;padding:8px;overflow:hidden}#pi-terminal .pi-terminal-mount,#pi-terminal .xterm{height:100%}#pi-terminal .xterm-viewport{background-color:#fff!important}#pi-terminal textarea:focus{box-shadow:none}.terminal-controls{align-items:center;gap:6px;padding-top:7px;display:flex}.terminal-controls>span{min-width:0;color:var(--muted);flex:1;font-size:10px}#notice{background:#f0f0f0;border:1px solid #cfcfcf;border-radius:4px;margin:6px 0;padding:8px 10px;font-size:12px}footer{justify-content:flex-end;align-items:center;gap:6px;display:flex}.composer-footer span{color:var(--muted);margin-right:auto;padding-left:6px;font-size:10px}footer{color:var(--muted);justify-content:space-between;gap:8px;padding-top:7px;font-size:10px;display:flex}.workspace{border-left:1px solid var(--line);background:#f1f1f1;flex-direction:column;min-width:0;min-height:0;display:flex}.workspace-heading{justify-content:space-between;align-items:center;min-height:40px;padding:5px 10px;display:flex}.workspace-heading h2{margin:0;font-size:12px;font-weight:600}.workspace-heading>div{gap:0;display:flex}.button{display:inline-block}.file-area{border-top:1px solid var(--line);border-bottom:1px solid var(--line);flex:1;grid-template-columns:140px minmax(0,1fr);min-height:180px;display:grid}#files{border-right:1px solid var(--line);padding:5px 4px;overflow:auto}#files button{text-align:left;white-space:normal;overflow-wrap:anywhere;background:0 0;border:none;border-radius:3px;width:100%;padding:5px 7px;font-family:ui-monospace,monospace;font-size:11px;line-height:1.45;display:block}#files button:hover{background:#e9e9e9}#files button.selected{color:#202020;background:#dedede}.editor-area{background:var(--surface);flex-direction:column;min-width:0;min-height:0;display:flex}.editor-heading{border-bottom:1px solid var(--line);background:var(--panel);justify-content:space-between;align-items:center;gap:6px;height:32px;padding:0 6px 0 10px;display:flex}#filename{text-overflow:ellipsis;white-space:nowrap;font-family:ui-monospace,monospace;font-size:11px;overflow:hidden}#editor{resize:none;color:#333;tab-size:2;white-space:pre;background:0 0;border:0;flex:1;min-height:140px;padding:10px 12px;font:12px/1.6 ui-monospace,SFMono-Regular,Consolas,monospace;overflow:auto}.terminal{background:#f5f5f5;padding:0 10px}.terminal summary{cursor:pointer;padding:9px 0;font-size:11px;font-weight:500}.terminal summary span{float:right;color:var(--muted);font-size:10px;font-weight:400}#terminal-output{white-space:pre-wrap;overflow-wrap:anywhere;height:112px;margin:0;font:11px/1.5 ui-monospace,monospace;overflow:auto}#shell-form{border-top:1px solid var(--line);align-items:center;gap:6px;padding:4px 0;font-family:ui-monospace,monospace;display:flex}#command{background:0 0;border:0;width:100%;min-width:0;padding:6px 0;font-size:12px}.workspace-note{color:var(--muted);margin:8px 10px;font-size:10px;line-height:1.45}.sr-only{clip:rect(0,0,0,0);white-space:nowrap;border:0;width:1px;height:1px;padding:0;position:absolute;overflow:hidden}#open-tab{color:var(--accent);white-space:normal;margin-top:8px;font-size:12px;text-decoration:none}@media (width<=850px){header{padding:0 12px}main{flex-direction:column;height:auto;display:flex}.conversation{height:calc(100dvh - 46px);min-height:500px;padding:10px}.workspace{border-left:0;border-top:1px solid var(--line);min-height:520px}.file-area{grid-template-columns:115px minmax(0,1fr);min-height:280px}.header-right>span:not(.local-dot){display:none}.header-right button{margin-left:0}h1{font-size:20px}.badge{font-size:9px}.model-panel{padding:8px}footer{font-size:9px}}@media (pointer:coarse){button,.button,#files button{min-height:38px}.editor-heading{min-height:40px}.terminal summary{padding-top:11px;padding-bottom:11px}#command,#pi-terminal .xterm-helper-textarea{font-size:16px}}.xterm{cursor:text;-webkit-user-select:none;user-select:none;position:relative}.xterm.focus,.xterm:focus{outline:none}.xterm .xterm-helpers{z-index:5;position:absolute;top:0}.xterm .xterm-helper-textarea{opacity:0;z-index:-5;white-space:nowrap;resize:none;border:0;width:0;height:0;margin:0;padding:0;position:absolute;top:0;left:-9999em;overflow:hidden}.xterm .composition-view{color:#fff;white-space:nowrap;z-index:1;background:#000;display:none;position:absolute}.xterm .composition-view.active{display:block}.xterm .xterm-viewport{cursor:default;background-color:#000;position:absolute;inset:0;overflow-y:scroll}.xterm .xterm-screen{position:relative}.xterm .xterm-screen canvas{position:absolute;top:0;left:0}.xterm-char-measure-element{visibility:hidden;line-height:normal;display:inline-block;position:absolute;top:0;left:-9999em}.xterm.enable-mouse-events{cursor:default}.xterm.xterm-cursor-pointer,.xterm .xterm-cursor-pointer{cursor:pointer}.xterm.column-select.focus{cursor:crosshair}.xterm .xterm-accessibility:not(.debug),.xterm .xterm-message{z-index:10;color:#0000;pointer-events:none;position:absolute;inset:0}.xterm .xterm-accessibility-tree:not(.debug) ::selection{color:#0000}.xterm .xterm-accessibility-tree{-webkit-user-select:text;user-select:text;white-space:pre;font-family:monospace}.xterm .xterm-accessibility-tree>div{transform-origin:0;width:fit-content}.xterm .live-region{width:1px;height:1px;position:absolute;left:-9999px;overflow:hidden}.xterm-dim{opacity:1!important}.xterm-underline-1{text-decoration:underline}.xterm-underline-2{-webkit-text-decoration:underline double;text-decoration:underline double}.xterm-underline-3{-webkit-text-decoration:underline wavy;text-decoration:underline wavy}.xterm-underline-4{-webkit-text-decoration:underline dotted;text-decoration:underline dotted}.xterm-underline-5{-webkit-text-decoration:underline dashed;text-decoration:underline dashed}.xterm-overline{text-decoration:overline}.xterm-overline.xterm-underline-1{text-decoration:underline overline}.xterm-overline.xterm-underline-2{-webkit-text-decoration:overline double underline;text-decoration:overline double underline}.xterm-overline.xterm-underline-3{-webkit-text-decoration:overline wavy underline;text-decoration:overline wavy underline}.xterm-overline.xterm-underline-4{-webkit-text-decoration:overline dotted underline;text-decoration:overline dotted underline}.xterm-overline.xterm-underline-5{-webkit-text-decoration:overline dashed underline;text-decoration:overline dashed underline}.xterm-strikethrough{text-decoration:line-through}.xterm-screen .xterm-decoration-container .xterm-decoration{z-index:6;position:absolute}.xterm-screen .xterm-decoration-container .xterm-decoration.xterm-decoration-top-layer{z-index:7}.xterm-decoration-overview-ruler{z-index:8;pointer-events:none;position:absolute;top:0;right:0}.xterm-decoration-top{z-index:2;position:relative}.xterm .xterm-scrollable-element>.scrollbar{cursor:default}.xterm .xterm-scrollable-element>.scrollbar>.scra{cursor:pointer;font-size:11px!important}.xterm .xterm-scrollable-element>.visible{opacity:1;z-index:11;background:0 0;transition:opacity .1s linear}.xterm .xterm-scrollable-element>.invisible{opacity:0;pointer-events:none}.xterm .xterm-scrollable-element>.invisible.fade{transition:opacity .8s linear}.xterm .xterm-scrollable-element>.shadow{display:none;position:absolute}.xterm .xterm-scrollable-element>.shadow.top{width:100%;height:3px;box-shadow:var(--vscode-scrollbar-shadow,#000) 0 6px 6px -6px inset;display:block;top:0;left:3px}.xterm .xterm-scrollable-element>.shadow.left{width:3px;height:100%;box-shadow:var(--vscode-scrollbar-shadow,#000) 6px 0 6px -6px inset;display:block;top:3px;left:0}.xterm .xterm-scrollable-element>.shadow.top-left-corner{width:3px;height:3px;display:block;top:0;left:0}.xterm .xterm-scrollable-element>.shadow.top.left{box-shadow:var(--vscode-scrollbar-shadow,#000) 6px 0 6px -6px inset}
|
assets/index-LA8xezhP.js
DELETED
|
The diff for this file is too large to render.
See raw diff
|
|
|
deployment-manifest.json
CHANGED
|
@@ -13,28 +13,28 @@
|
|
| 13 |
"sha256": "7b2f4725b2cb4fc1d22031a311857d5ac8754c882bf4e01b5ab1a9778b2417f0"
|
| 14 |
},
|
| 15 |
"README.md": {
|
| 16 |
-
"bytes":
|
| 17 |
-
"sha256": "
|
| 18 |
},
|
| 19 |
"SPACE_README.md": {
|
| 20 |
-
"bytes":
|
| 21 |
-
"sha256": "
|
| 22 |
},
|
| 23 |
"app/README.md": {
|
| 24 |
-
"bytes":
|
| 25 |
-
"sha256": "
|
| 26 |
},
|
| 27 |
"app/index.html": {
|
| 28 |
-
"bytes":
|
| 29 |
-
"sha256": "
|
| 30 |
},
|
| 31 |
"app/package-lock.json": {
|
| 32 |
-
"bytes":
|
| 33 |
-
"sha256": "
|
| 34 |
},
|
| 35 |
"app/package.json": {
|
| 36 |
-
"bytes":
|
| 37 |
-
"sha256": "
|
| 38 |
},
|
| 39 |
"app/public/favicon.svg": {
|
| 40 |
"bytes": 227,
|
|
@@ -53,8 +53,12 @@
|
|
| 53 |
"sha256": "0457f5bcec3b3b211605dfb5d1a49042fd638f3686a410fe099c24a25af13c48"
|
| 54 |
},
|
| 55 |
"app/scripts/licenses.mjs": {
|
| 56 |
-
"bytes":
|
| 57 |
-
"sha256": "
|
|
|
|
|
|
|
|
|
|
|
|
|
| 58 |
},
|
| 59 |
"app/src/agent.worker.mjs": {
|
| 60 |
"bytes": 4265,
|
|
@@ -69,8 +73,8 @@
|
|
| 69 |
"sha256": "04375923fd4d2cfd6a58e17fb2eb966ddf4fb766fb050a88459e2d8e7358926c"
|
| 70 |
},
|
| 71 |
"app/src/main.mjs": {
|
| 72 |
-
"bytes":
|
| 73 |
-
"sha256": "
|
| 74 |
},
|
| 75 |
"app/src/markdown.mjs": {
|
| 76 |
"bytes": 699,
|
|
@@ -80,6 +84,30 @@
|
|
| 80 |
"bytes": 3656,
|
| 81 |
"sha256": "67bf7df79a547f8183a6321269e9c250fd2d2aabeebbecd13949f75c4cce8bb9"
|
| 82 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 83 |
"app/src/protocol.mjs": {
|
| 84 |
"bytes": 4981,
|
| 85 |
"sha256": "1d4416d6a77b60aeb847d88e6d98ff9a580e8dcb163add5f42923f2d145bf52e"
|
|
@@ -89,16 +117,16 @@
|
|
| 89 |
"sha256": "dec9e2962a0f5c076512368395017fb5af7c9f7bfbe8cfed10105072dbcb76fe"
|
| 90 |
},
|
| 91 |
"app/src/style.css": {
|
| 92 |
-
"bytes":
|
| 93 |
-
"sha256": "
|
| 94 |
},
|
| 95 |
"app/src/workspace.mjs": {
|
| 96 |
"bytes": 5206,
|
| 97 |
"sha256": "d3bf4e77e6ab855cd4d10f11330f52531068a7ca13456ddf4c357d03bfee40e0"
|
| 98 |
},
|
| 99 |
"app/tests/browser.mjs": {
|
| 100 |
-
"bytes":
|
| 101 |
-
"sha256": "
|
| 102 |
},
|
| 103 |
"app/tests/dev-smoke.mjs": {
|
| 104 |
"bytes": 1352,
|
|
@@ -117,28 +145,64 @@
|
|
| 117 |
"sha256": "8cc0750f0861f5cf3d63e155c9ea4091bae5432a5a6719a55941d63599456e55"
|
| 118 |
},
|
| 119 |
"app/tests/space-embedded.mjs": {
|
| 120 |
-
"bytes":
|
| 121 |
-
"sha256": "
|
| 122 |
},
|
| 123 |
"app/tests/space-restricted.mjs": {
|
| 124 |
"bytes": 2580,
|
| 125 |
"sha256": "f71fb23f85cbf021cbf3f368e27687e64fb25cddf232dc1224202a920de78458"
|
| 126 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 127 |
"app/vite.config.mjs": {
|
| 128 |
-
"bytes":
|
| 129 |
-
"sha256": "
|
| 130 |
},
|
| 131 |
"assets/agent.worker-DqQzo9yL.js": {
|
| 132 |
"bytes": 2092911,
|
| 133 |
"sha256": "1fc0006217cf28bf42951d1738e67d235b9516f9d579fa46064718759fd7ba28"
|
| 134 |
},
|
| 135 |
-
"assets/index-
|
| 136 |
-
"bytes":
|
| 137 |
-
"sha256": "
|
| 138 |
},
|
| 139 |
-
"assets/index-
|
| 140 |
-
"bytes":
|
| 141 |
-
"sha256": "
|
| 142 |
},
|
| 143 |
"assets/ort-wasm-simd-threaded.asyncify-DMmc6YqF.wasm": {
|
| 144 |
"bytes": 23567050,
|
|
@@ -149,12 +213,12 @@
|
|
| 149 |
"sha256": "c5cf939c058ae5b63f0cdab8d1becc4544582d6ed74098e9c8460d3acb78309e"
|
| 150 |
},
|
| 151 |
"index.html": {
|
| 152 |
-
"bytes":
|
| 153 |
-
"sha256": "
|
| 154 |
},
|
| 155 |
"licenses/dependencies.txt": {
|
| 156 |
-
"bytes":
|
| 157 |
-
"sha256": "
|
| 158 |
},
|
| 159 |
"licenses/onnxruntime-LICENSE": {
|
| 160 |
"bytes": 1073,
|
|
@@ -168,13 +232,17 @@
|
|
| 168 |
"bytes": 1069,
|
| 169 |
"sha256": "0457f5bcec3b3b211605dfb5d1a49042fd638f3686a410fe099c24a25af13c48"
|
| 170 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
| 171 |
"requirements-convert.txt": {
|
| 172 |
"bytes": 254,
|
| 173 |
"sha256": "2a2dca97d6a9480afff1ed0da8f5924610552cfe509d5081788455f9c1d71f8b"
|
| 174 |
},
|
| 175 |
"research/browser-agent/REPORT.md": {
|
| 176 |
-
"bytes":
|
| 177 |
-
"sha256": "
|
| 178 |
},
|
| 179 |
"research/browser-agent/just-bash-README.md": {
|
| 180 |
"bytes": 29503,
|
|
@@ -237,8 +305,8 @@
|
|
| 237 |
"sha256": "b9bb39d5bd7bceb260d5eafa633f5af2e47a35d6132c8453d908ea6a4ad8a0f9"
|
| 238 |
},
|
| 239 |
"scripts/prepare_space.py": {
|
| 240 |
-
"bytes":
|
| 241 |
-
"sha256": "
|
| 242 |
},
|
| 243 |
"scripts/reference.py": {
|
| 244 |
"bytes": 2652,
|
|
@@ -292,6 +360,18 @@
|
|
| 292 |
"bytes": 2244,
|
| 293 |
"sha256": "64f5f41f482beb1886a922d4f5872a8f82ad61b701ff45c9e21fb7980acd4ff7"
|
| 294 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 295 |
"validation/benchmark-summary.json": {
|
| 296 |
"bytes": 1500,
|
| 297 |
"sha256": "5bc51fbdbc43b58caa2036b621ded2bd3b4c1c95ba487b07bb5b84f19e97ebc0"
|
|
|
|
| 13 |
"sha256": "7b2f4725b2cb4fc1d22031a311857d5ac8754c882bf4e01b5ab1a9778b2417f0"
|
| 14 |
},
|
| 15 |
"README.md": {
|
| 16 |
+
"bytes": 8768,
|
| 17 |
+
"sha256": "3c928e6cc25c94390c8564c09bf207d6ef3520ec69b8eae78bfea2f9272942b9"
|
| 18 |
},
|
| 19 |
"SPACE_README.md": {
|
| 20 |
+
"bytes": 8768,
|
| 21 |
+
"sha256": "3c928e6cc25c94390c8564c09bf207d6ef3520ec69b8eae78bfea2f9272942b9"
|
| 22 |
},
|
| 23 |
"app/README.md": {
|
| 24 |
+
"bytes": 8286,
|
| 25 |
+
"sha256": "d7f0c4e8efd9b7237de4a938dc6f8dd3937c58139e09f87ca22041e83a44ae97"
|
| 26 |
},
|
| 27 |
"app/index.html": {
|
| 28 |
+
"bytes": 3254,
|
| 29 |
+
"sha256": "fddf34b58f9bc96fd7812fbfc8946562d75d34d4d08bf4283e5ab2c27b48f1e8"
|
| 30 |
},
|
| 31 |
"app/package-lock.json": {
|
| 32 |
+
"bytes": 159289,
|
| 33 |
+
"sha256": "abf0ce0c8b9240388940b2c1d666fbdcaa4f453eebdde9d2f80744f5cc461ac7"
|
| 34 |
},
|
| 35 |
"app/package.json": {
|
| 36 |
+
"bytes": 856,
|
| 37 |
+
"sha256": "83ec2cd1c54a65c91b6b8d3162fc2a4f9543aeec7e520fe6958aba3a1c7fe06a"
|
| 38 |
},
|
| 39 |
"app/public/favicon.svg": {
|
| 40 |
"bytes": 227,
|
|
|
|
| 53 |
"sha256": "0457f5bcec3b3b211605dfb5d1a49042fd638f3686a410fe099c24a25af13c48"
|
| 54 |
},
|
| 55 |
"app/scripts/licenses.mjs": {
|
| 56 |
+
"bytes": 1536,
|
| 57 |
+
"sha256": "8f04bf1c94122d5ad9fe8cb6964b69f8fdf71a5c1005921be960d5c8c6d14739"
|
| 58 |
+
},
|
| 59 |
+
"app/scripts/pi-browser.mjs": {
|
| 60 |
+
"bytes": 1635,
|
| 61 |
+
"sha256": "bd061a3825e46db09624261b39150f2dfab1c838bc252d54599d0f950f246d3f"
|
| 62 |
},
|
| 63 |
"app/src/agent.worker.mjs": {
|
| 64 |
"bytes": 4265,
|
|
|
|
| 73 |
"sha256": "04375923fd4d2cfd6a58e17fb2eb966ddf4fb766fb050a88459e2d8e7358926c"
|
| 74 |
},
|
| 75 |
"app/src/main.mjs": {
|
| 76 |
+
"bytes": 10043,
|
| 77 |
+
"sha256": "a0a818a35e52bf8edb21d011e7167848531efe01146a4e10dd9d49a4817e93ce"
|
| 78 |
},
|
| 79 |
"app/src/markdown.mjs": {
|
| 80 |
"bytes": 699,
|
|
|
|
| 84 |
"bytes": 3656,
|
| 85 |
"sha256": "67bf7df79a547f8183a6321269e9c250fd2d2aabeebbecd13949f75c4cce8bb9"
|
| 86 |
},
|
| 87 |
+
"app/src/pi/browser-host.mjs": {
|
| 88 |
+
"bytes": 329,
|
| 89 |
+
"sha256": "27db1727103727a5a01e455daefa45634fb3be62af76cc883b21abb7069a1d87"
|
| 90 |
+
},
|
| 91 |
+
"app/src/pi/capabilities.mjs": {
|
| 92 |
+
"bytes": 1385,
|
| 93 |
+
"sha256": "62f09aa13c695980717c7bc7e25ad6aba66254faa994bf81f39d2a912a89ff83"
|
| 94 |
+
},
|
| 95 |
+
"app/src/pi/entry.mjs": {
|
| 96 |
+
"bytes": 1351,
|
| 97 |
+
"sha256": "f7e6ae0ba428b78126c6873f9784dbf7d9220421cd6510ad893819492ed5ff9b"
|
| 98 |
+
},
|
| 99 |
+
"app/src/pi/terminal-ui.mjs": {
|
| 100 |
+
"bytes": 13938,
|
| 101 |
+
"sha256": "0c8bbef004dd83f2f979fc8963d8ebba376f33e35973abe20f134a081f38c6ed"
|
| 102 |
+
},
|
| 103 |
+
"app/src/pi/theme.mjs": {
|
| 104 |
+
"bytes": 1835,
|
| 105 |
+
"sha256": "2fb5e6d44ff8f68a369c8a4cc203bd3f1bb6cdf8dd64a50674ea83118961d9e8"
|
| 106 |
+
},
|
| 107 |
+
"app/src/pi/xterm-terminal.mjs": {
|
| 108 |
+
"bytes": 1496,
|
| 109 |
+
"sha256": "152d6d8cf26932e4a01efcb9613513bb30bd892aa4f3096800b98884346cba48"
|
| 110 |
+
},
|
| 111 |
"app/src/protocol.mjs": {
|
| 112 |
"bytes": 4981,
|
| 113 |
"sha256": "1d4416d6a77b60aeb847d88e6d98ff9a580e8dcb163add5f42923f2d145bf52e"
|
|
|
|
| 117 |
"sha256": "dec9e2962a0f5c076512368395017fb5af7c9f7bfbe8cfed10105072dbcb76fe"
|
| 118 |
},
|
| 119 |
"app/src/style.css": {
|
| 120 |
+
"bytes": 7512,
|
| 121 |
+
"sha256": "b6423d7323eb469f40c322ccb81e0b9f724a01e3c8c9794365222ccd63252dcb"
|
| 122 |
},
|
| 123 |
"app/src/workspace.mjs": {
|
| 124 |
"bytes": 5206,
|
| 125 |
"sha256": "d3bf4e77e6ab855cd4d10f11330f52531068a7ca13456ddf4c357d03bfee40e0"
|
| 126 |
},
|
| 127 |
"app/tests/browser.mjs": {
|
| 128 |
+
"bytes": 9598,
|
| 129 |
+
"sha256": "01f506319c6654b0deaa853bc8e79c3a034fb47b1f7a4e16fbbe7ca4ba257b8f"
|
| 130 |
},
|
| 131 |
"app/tests/dev-smoke.mjs": {
|
| 132 |
"bytes": 1352,
|
|
|
|
| 145 |
"sha256": "8cc0750f0861f5cf3d63e155c9ea4091bae5432a5a6719a55941d63599456e55"
|
| 146 |
},
|
| 147 |
"app/tests/space-embedded.mjs": {
|
| 148 |
+
"bytes": 3599,
|
| 149 |
+
"sha256": "c5475b6eda91e9dbbacc8e81dbaaf6f89ec41be390182d37739076c81157bf7f"
|
| 150 |
},
|
| 151 |
"app/tests/space-restricted.mjs": {
|
| 152 |
"bytes": 2580,
|
| 153 |
"sha256": "f71fb23f85cbf021cbf3f368e27687e64fb25cddf232dc1224202a920de78458"
|
| 154 |
},
|
| 155 |
+
"app/tests/terminal.mjs": {
|
| 156 |
+
"bytes": 7329,
|
| 157 |
+
"sha256": "d19a8df4e4559f75a6c8f5397dde82b42e522da49e637fb5a7ed5496603b8013"
|
| 158 |
+
},
|
| 159 |
+
"app/vendor/pi-coding-agent/LICENSE": {
|
| 160 |
+
"bytes": 1069,
|
| 161 |
+
"sha256": "0457f5bcec3b3b211605dfb5d1a49042fd638f3686a410fe099c24a25af13c48"
|
| 162 |
+
},
|
| 163 |
+
"app/vendor/pi-coding-agent/README.md": {
|
| 164 |
+
"bytes": 1185,
|
| 165 |
+
"sha256": "b7d5406662cbece954f1bcd53d99a15b41a867adbf225380ac40d08f3487da4b"
|
| 166 |
+
},
|
| 167 |
+
"app/vendor/pi-coding-agent/components/assistant-message.js": {
|
| 168 |
+
"bytes": 7410,
|
| 169 |
+
"sha256": "edbd1e712234609d2ad1078edcf960e3b6f549669075f1c5d1a7a4481e2b3d19"
|
| 170 |
+
},
|
| 171 |
+
"app/vendor/pi-coding-agent/components/custom-editor.js": {
|
| 172 |
+
"bytes": 5095,
|
| 173 |
+
"sha256": "000b0056f2bf645537537468bdd6f6ee0c82e4110f5417c1c70c2e8fefc04f29"
|
| 174 |
+
},
|
| 175 |
+
"app/vendor/pi-coding-agent/components/markdown-transform.js": {
|
| 176 |
+
"bytes": 786,
|
| 177 |
+
"sha256": "31ad2245189328104a31bb9b12f58b0a12e4af9c7924775a242c9dcd526d2c1a"
|
| 178 |
+
},
|
| 179 |
+
"app/vendor/pi-coding-agent/components/user-message.js": {
|
| 180 |
+
"bytes": 1791,
|
| 181 |
+
"sha256": "398a34b8a979daea5614be4cbbe6ff66e7eca97e9fd855508791dcddce994bc8"
|
| 182 |
+
},
|
| 183 |
+
"app/vendor/pi-coding-agent/provenance.json": {
|
| 184 |
+
"bytes": 690,
|
| 185 |
+
"sha256": "9f8797b8d55c60820d422d88f31cb5eec6d5e9283cd4d6f5a374649f7d535b0b"
|
| 186 |
+
},
|
| 187 |
+
"app/vendor/pi-coding-agent/theme/light.json": {
|
| 188 |
+
"bytes": 2236,
|
| 189 |
+
"sha256": "14c7172ba7e75eab6f509504de806af0f2d9ff817bfb45d9533bff15c7e6e657"
|
| 190 |
+
},
|
| 191 |
"app/vite.config.mjs": {
|
| 192 |
+
"bytes": 1679,
|
| 193 |
+
"sha256": "eab9d66533efd95e346d22f3f455c4c77a85c1386e82e14f3e72c4fdbb8d9c16"
|
| 194 |
},
|
| 195 |
"assets/agent.worker-DqQzo9yL.js": {
|
| 196 |
"bytes": 2092911,
|
| 197 |
"sha256": "1fc0006217cf28bf42951d1738e67d235b9516f9d579fa46064718759fd7ba28"
|
| 198 |
},
|
| 199 |
+
"assets/index-BRKUEVCq.js": {
|
| 200 |
+
"bytes": 696106,
|
| 201 |
+
"sha256": "d5eb8ca63b1d540c55787b03658c4c74dcb541f5f6fd18165bae838c42a21077"
|
| 202 |
},
|
| 203 |
+
"assets/index-DL-Ot0hN.css": {
|
| 204 |
+
"bytes": 10345,
|
| 205 |
+
"sha256": "8e50e0599e5a5e56558fbcf529b549cf55c4b609eb598772a19b8fb3db7437b1"
|
| 206 |
},
|
| 207 |
"assets/ort-wasm-simd-threaded.asyncify-DMmc6YqF.wasm": {
|
| 208 |
"bytes": 23567050,
|
|
|
|
| 213 |
"sha256": "c5cf939c058ae5b63f0cdab8d1becc4544582d6ed74098e9c8460d3acb78309e"
|
| 214 |
},
|
| 215 |
"index.html": {
|
| 216 |
+
"bytes": 3351,
|
| 217 |
+
"sha256": "a32849e547e267c36f069003b8816134513c476ecade94bd82eb33ade4f641de"
|
| 218 |
},
|
| 219 |
"licenses/dependencies.txt": {
|
| 220 |
+
"bytes": 1384222,
|
| 221 |
+
"sha256": "6e11a0fa7b1140bc41a9c2f50d06bfa2f12906a45437f26e1f8048f30210d4ff"
|
| 222 |
},
|
| 223 |
"licenses/onnxruntime-LICENSE": {
|
| 224 |
"bytes": 1073,
|
|
|
|
| 232 |
"bytes": 1069,
|
| 233 |
"sha256": "0457f5bcec3b3b211605dfb5d1a49042fd638f3686a410fe099c24a25af13c48"
|
| 234 |
},
|
| 235 |
+
"licenses/pi-components.json": {
|
| 236 |
+
"bytes": 690,
|
| 237 |
+
"sha256": "9f8797b8d55c60820d422d88f31cb5eec6d5e9283cd4d6f5a374649f7d535b0b"
|
| 238 |
+
},
|
| 239 |
"requirements-convert.txt": {
|
| 240 |
"bytes": 254,
|
| 241 |
"sha256": "2a2dca97d6a9480afff1ed0da8f5924610552cfe509d5081788455f9c1d71f8b"
|
| 242 |
},
|
| 243 |
"research/browser-agent/REPORT.md": {
|
| 244 |
+
"bytes": 14214,
|
| 245 |
+
"sha256": "29fd4bdec42d8eb0e6c13698badd6182efc24697cc44585a9783c7ecc52eff00"
|
| 246 |
},
|
| 247 |
"research/browser-agent/just-bash-README.md": {
|
| 248 |
"bytes": 29503,
|
|
|
|
| 305 |
"sha256": "b9bb39d5bd7bceb260d5eafa633f5af2e47a35d6132c8453d908ea6a4ad8a0f9"
|
| 306 |
},
|
| 307 |
"scripts/prepare_space.py": {
|
| 308 |
+
"bytes": 3867,
|
| 309 |
+
"sha256": "7f39c89f4df965e29ba05b7a5ee4fe4ec71e225f8e04ed0f6fb933c471938502"
|
| 310 |
},
|
| 311 |
"scripts/reference.py": {
|
| 312 |
"bytes": 2652,
|
|
|
|
| 360 |
"bytes": 2244,
|
| 361 |
"sha256": "64f5f41f482beb1886a922d4f5872a8f82ad61b701ff45c9e21fb7980acd4ff7"
|
| 362 |
},
|
| 363 |
+
"validation/agent-terminal-dev.json": {
|
| 364 |
+
"bytes": 12592,
|
| 365 |
+
"sha256": "3c0dc1cad52da619a31abc0b86c892b37c32ac0fd44b62afb43f326bf5ac8d66"
|
| 366 |
+
},
|
| 367 |
+
"validation/agent-terminal-model.json": {
|
| 368 |
+
"bytes": 55713,
|
| 369 |
+
"sha256": "1cf1b205095b6cb15f62a3e799e3398ebefa0d1c265fedc18a83b0e24f6ae077"
|
| 370 |
+
},
|
| 371 |
+
"validation/agent-terminal.json": {
|
| 372 |
+
"bytes": 2874,
|
| 373 |
+
"sha256": "1f0bf618a027c1a5252c247f8a82da1d9ca1b10cb8e50575994effe4c0245e37"
|
| 374 |
+
},
|
| 375 |
"validation/benchmark-summary.json": {
|
| 376 |
"bytes": 1500,
|
| 377 |
"sha256": "5bc51fbdbc43b58caa2036b621ded2bd3b4c1c95ba487b07bb5b84f19e97ebc0"
|
index.html
CHANGED
|
@@ -6,8 +6,8 @@
|
|
| 6 |
<link rel="icon" href="./favicon.svg" type="image/svg+xml">
|
| 7 |
<meta name="description" content="Pi, MiniCPM5 and a bash workspace, running entirely in your browser.">
|
| 8 |
<title>Pi Β· Browser workspace</title>
|
| 9 |
-
<script type="module" crossorigin src="./assets/index-
|
| 10 |
-
<link rel="stylesheet" crossorigin href="./assets/index-
|
| 11 |
</head>
|
| 12 |
<body>
|
| 13 |
<header><a class="brand" href="./"><span class="pi">Ο</span><span>Pi <small>browser workspace</small></span></a>
|
|
@@ -22,17 +22,10 @@
|
|
| 22 |
<div id="load-detail" class="muted"></div>
|
| 23 |
<a id="open-tab" class="button" target="_blank" rel="noopener noreferrer" hidden>Open workspace in a new tab β</a>
|
| 24 |
</div>
|
| 25 |
-
<div id="
|
| 26 |
-
<div id="welcome"><div class="eyebrow">A small agent. A real workspace.</div><h1>What shall we work on?</h1>
|
| 27 |
-
<p>Chat with Pi, work with files, and run shell commands.<br>The model and every tool run here, in your browser.</p>
|
| 28 |
-
<div class="suggestions"><button data-prompt="Read sales.csv, calculate total revenue using bash, and save a short report to report.md. Verify the file.">Analyze the sample data <span>β</span></button><button data-prompt="Create a bash script called hello.sh that prints Hello from the browser, run it, and report the output.">Write and run a script <span>β</span></button></div>
|
| 29 |
-
</div>
|
| 30 |
-
</div>
|
| 31 |
<div id="notice" role="status" hidden></div>
|
| 32 |
-
<
|
| 33 |
-
|
| 34 |
-
</form>
|
| 35 |
-
<footer>Pi agent core Β· just-bash Β· Transformers.js <span>4,096 token context</span></footer>
|
| 36 |
</section>
|
| 37 |
<aside class="workspace" aria-label="Workspace">
|
| 38 |
<div class="workspace-heading"><h2>Workspace</h2><div><button id="add-file" class="quiet">+ File</button><label class="button quiet" for="upload">Import</label><input id="upload" type="file" multiple hidden><button id="export" class="quiet">Export</button></div></div>
|
|
|
|
| 6 |
<link rel="icon" href="./favicon.svg" type="image/svg+xml">
|
| 7 |
<meta name="description" content="Pi, MiniCPM5 and a bash workspace, running entirely in your browser.">
|
| 8 |
<title>Pi Β· Browser workspace</title>
|
| 9 |
+
<script type="module" crossorigin src="./assets/index-BRKUEVCq.js"></script>
|
| 10 |
+
<link rel="stylesheet" crossorigin href="./assets/index-DL-Ot0hN.css">
|
| 11 |
</head>
|
| 12 |
<body>
|
| 13 |
<header><a class="brand" href="./"><span class="pi">Ο</span><span>Pi <small>browser workspace</small></span></a>
|
|
|
|
| 22 |
<div id="load-detail" class="muted"></div>
|
| 23 |
<a id="open-tab" class="button" target="_blank" rel="noopener noreferrer" hidden>Open workspace in a new tab β</a>
|
| 24 |
</div>
|
| 25 |
+
<div id="pi-terminal" aria-label="Pi terminal"></div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 26 |
<div id="notice" role="status" hidden></div>
|
| 27 |
+
<div class="terminal-controls"><span id="activity" role="status">Files and chat are saved on this device.</span><button id="newline" type="button" title="Insert a newline (Shift+Enter)">β΅ Newline</button><button id="stop" type="button" hidden>Stop</button><button id="send" class="primary">Send β</button></div>
|
| 28 |
+
<footer>Pi TUI Β· xterm.js <span>Model and tools run in your browser</span></footer>
|
|
|
|
|
|
|
| 29 |
</section>
|
| 30 |
<aside class="workspace" aria-label="Workspace">
|
| 31 |
<div class="workspace-heading"><h2>Workspace</h2><div><button id="add-file" class="quiet">+ File</button><label class="button quiet" for="upload">Import</label><input id="upload" type="file" multiple hidden><button id="export" class="quiet">Export</button></div></div>
|
licenses/dependencies.txt
CHANGED
|
@@ -4375,6 +4375,11 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
|
|
| 4375 |
@earendil-works/pi-telemetry@0.85.1 (MIT)
|
| 4376 |
|
| 4377 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4378 |
----------------------------------------
|
| 4379 |
|
| 4380 |
@esbuild/linux-x64@0.28.1 (MIT)
|
|
@@ -8088,6 +8093,56 @@ The above copyright notice and this permission notice shall be included in all c
|
|
| 8088 |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
| 8089 |
|
| 8090 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8091 |
----------------------------------------
|
| 8092 |
|
| 8093 |
adm-zip@0.5.18 (MIT)
|
|
@@ -8441,7 +8496,7 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
| 8441 |
|
| 8442 |
----------------------------------------
|
| 8443 |
|
| 8444 |
-
buffer@
|
| 8445 |
The MIT License (MIT)
|
| 8446 |
|
| 8447 |
Copyright (c) Feross Aboukhadijeh, and other contributors.
|
|
@@ -9263,6 +9318,33 @@ The above copyright notice and this permission notice shall be included in all c
|
|
| 9263 |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
| 9264 |
|
| 9265 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9266 |
----------------------------------------
|
| 9267 |
|
| 9268 |
expand-template@2.0.3 ((MIT OR WTFPL))
|
|
@@ -10122,6 +10204,20 @@ gcp-metadata@8.1.2 (Apache-2.0)
|
|
| 10122 |
limitations under the License.
|
| 10123 |
|
| 10124 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10125 |
----------------------------------------
|
| 10126 |
|
| 10127 |
github-from-package@0.0.0 (MIT)
|
|
@@ -12213,6 +12309,55 @@ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
| 12213 |
OTHER DEALINGS IN THE SOFTWARE.
|
| 12214 |
|
| 12215 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12216 |
----------------------------------------
|
| 12217 |
|
| 12218 |
matcher@3.0.0 (MIT)
|
|
@@ -12596,6 +12741,202 @@ SOFTWARE.
|
|
| 12596 |
|
| 12597 |
|
| 12598 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12599 |
----------------------------------------
|
| 12600 |
|
| 12601 |
object-keys@1.1.1 (MIT)
|
|
|
|
| 4375 |
@earendil-works/pi-telemetry@0.85.1 (MIT)
|
| 4376 |
|
| 4377 |
|
| 4378 |
+
----------------------------------------
|
| 4379 |
+
|
| 4380 |
+
@earendil-works/pi-tui@0.85.1 (MIT)
|
| 4381 |
+
|
| 4382 |
+
|
| 4383 |
----------------------------------------
|
| 4384 |
|
| 4385 |
@esbuild/linux-x64@0.28.1 (MIT)
|
|
|
|
| 8093 |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
| 8094 |
|
| 8095 |
|
| 8096 |
+
----------------------------------------
|
| 8097 |
+
|
| 8098 |
+
@xterm/addon-fit@0.11.0 (MIT)
|
| 8099 |
+
Copyright (c) 2019, The xterm.js authors (https://github.com/xtermjs/xterm.js)
|
| 8100 |
+
|
| 8101 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 8102 |
+
of this software and associated documentation files (the "Software"), to deal
|
| 8103 |
+
in the Software without restriction, including without limitation the rights
|
| 8104 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 8105 |
+
copies of the Software, and to permit persons to whom the Software is
|
| 8106 |
+
furnished to do so, subject to the following conditions:
|
| 8107 |
+
|
| 8108 |
+
The above copyright notice and this permission notice shall be included in
|
| 8109 |
+
all copies or substantial portions of the Software.
|
| 8110 |
+
|
| 8111 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 8112 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 8113 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 8114 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 8115 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 8116 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
| 8117 |
+
THE SOFTWARE.
|
| 8118 |
+
|
| 8119 |
+
|
| 8120 |
+
----------------------------------------
|
| 8121 |
+
|
| 8122 |
+
@xterm/xterm@6.0.0 (MIT)
|
| 8123 |
+
Copyright (c) 2017-2019, The xterm.js authors (https://github.com/xtermjs/xterm.js)
|
| 8124 |
+
Copyright (c) 2014-2016, SourceLair Private Company (https://www.sourcelair.com)
|
| 8125 |
+
Copyright (c) 2012-2013, Christopher Jeffrey (https://github.com/chjj/)
|
| 8126 |
+
|
| 8127 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 8128 |
+
of this software and associated documentation files (the "Software"), to deal
|
| 8129 |
+
in the Software without restriction, including without limitation the rights
|
| 8130 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 8131 |
+
copies of the Software, and to permit persons to whom the Software is
|
| 8132 |
+
furnished to do so, subject to the following conditions:
|
| 8133 |
+
|
| 8134 |
+
The above copyright notice and this permission notice shall be included in
|
| 8135 |
+
all copies or substantial portions of the Software.
|
| 8136 |
+
|
| 8137 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 8138 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 8139 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 8140 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 8141 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 8142 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
| 8143 |
+
THE SOFTWARE.
|
| 8144 |
+
|
| 8145 |
+
|
| 8146 |
----------------------------------------
|
| 8147 |
|
| 8148 |
adm-zip@0.5.18 (MIT)
|
|
|
|
| 8496 |
|
| 8497 |
----------------------------------------
|
| 8498 |
|
| 8499 |
+
buffer@6.0.3 (MIT)
|
| 8500 |
The MIT License (MIT)
|
| 8501 |
|
| 8502 |
Copyright (c) Feross Aboukhadijeh, and other contributors.
|
|
|
|
| 9318 |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
| 9319 |
|
| 9320 |
|
| 9321 |
+
----------------------------------------
|
| 9322 |
+
|
| 9323 |
+
events@3.3.0 (MIT)
|
| 9324 |
+
MIT
|
| 9325 |
+
|
| 9326 |
+
Copyright Joyent, Inc. and other Node contributors.
|
| 9327 |
+
|
| 9328 |
+
Permission is hereby granted, free of charge, to any person obtaining a
|
| 9329 |
+
copy of this software and associated documentation files (the
|
| 9330 |
+
"Software"), to deal in the Software without restriction, including
|
| 9331 |
+
without limitation the rights to use, copy, modify, merge, publish,
|
| 9332 |
+
distribute, sublicense, and/or sell copies of the Software, and to permit
|
| 9333 |
+
persons to whom the Software is furnished to do so, subject to the
|
| 9334 |
+
following conditions:
|
| 9335 |
+
|
| 9336 |
+
The above copyright notice and this permission notice shall be included
|
| 9337 |
+
in all copies or substantial portions of the Software.
|
| 9338 |
+
|
| 9339 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
| 9340 |
+
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
| 9341 |
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
| 9342 |
+
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
| 9343 |
+
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
| 9344 |
+
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
| 9345 |
+
USE OR OTHER DEALINGS IN THE SOFTWARE.
|
| 9346 |
+
|
| 9347 |
+
|
| 9348 |
----------------------------------------
|
| 9349 |
|
| 9350 |
expand-template@2.0.3 ((MIT OR WTFPL))
|
|
|
|
| 10204 |
limitations under the License.
|
| 10205 |
|
| 10206 |
|
| 10207 |
+
----------------------------------------
|
| 10208 |
+
|
| 10209 |
+
get-east-asian-width@1.6.0 (MIT)
|
| 10210 |
+
MIT License
|
| 10211 |
+
|
| 10212 |
+
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (https://sindresorhus.com)
|
| 10213 |
+
|
| 10214 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
| 10215 |
+
|
| 10216 |
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
| 10217 |
+
|
| 10218 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
| 10219 |
+
|
| 10220 |
+
|
| 10221 |
----------------------------------------
|
| 10222 |
|
| 10223 |
github-from-package@0.0.0 (MIT)
|
|
|
|
| 12309 |
OTHER DEALINGS IN THE SOFTWARE.
|
| 12310 |
|
| 12311 |
|
| 12312 |
+
----------------------------------------
|
| 12313 |
+
|
| 12314 |
+
marked@18.0.5 (MIT)
|
| 12315 |
+
# License information
|
| 12316 |
+
|
| 12317 |
+
## Contribution License Agreement
|
| 12318 |
+
|
| 12319 |
+
If you contribute code to this project, you are implicitly allowing your code
|
| 12320 |
+
to be distributed under the MIT license. You are also implicitly verifying that
|
| 12321 |
+
all code is your original work. `</legalese>`
|
| 12322 |
+
|
| 12323 |
+
## Marked
|
| 12324 |
+
|
| 12325 |
+
Copyright (c) 2018+, MarkedJS (https://github.com/markedjs/)
|
| 12326 |
+
Copyright (c) 2011-2018, Christopher Jeffrey (https://github.com/chjj/)
|
| 12327 |
+
|
| 12328 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 12329 |
+
of this software and associated documentation files (the "Software"), to deal
|
| 12330 |
+
in the Software without restriction, including without limitation the rights
|
| 12331 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 12332 |
+
copies of the Software, and to permit persons to whom the Software is
|
| 12333 |
+
furnished to do so, subject to the following conditions:
|
| 12334 |
+
|
| 12335 |
+
The above copyright notice and this permission notice shall be included in
|
| 12336 |
+
all copies or substantial portions of the Software.
|
| 12337 |
+
|
| 12338 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 12339 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 12340 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 12341 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 12342 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 12343 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
| 12344 |
+
THE SOFTWARE.
|
| 12345 |
+
|
| 12346 |
+
## Markdown
|
| 12347 |
+
|
| 12348 |
+
Copyright Β© 2004, John Gruber
|
| 12349 |
+
http://daringfireball.net/
|
| 12350 |
+
All rights reserved.
|
| 12351 |
+
|
| 12352 |
+
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
| 12353 |
+
|
| 12354 |
+
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
| 12355 |
+
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
| 12356 |
+
* Neither the name βMarkdownβ nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
| 12357 |
+
|
| 12358 |
+
This software is provided by the copyright holders and contributors βas isβ and any express or implied warranties, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose are disclaimed. In no event shall the copyright owner or contributors be liable for any direct, indirect, incidental, special, exemplary, or consequential damages (including, but not limited to, procurement of substitute goods or services; loss of use, data, or profits; or business interruption) however caused and on any theory of liability, whether in contract, strict liability, or tort (including negligence or otherwise) arising in any way out of the use of this software, even if advised of the possibility of such damage.
|
| 12359 |
+
|
| 12360 |
+
|
| 12361 |
----------------------------------------
|
| 12362 |
|
| 12363 |
matcher@3.0.0 (MIT)
|
|
|
|
| 12741 |
|
| 12742 |
|
| 12743 |
|
| 12744 |
+
----------------------------------------
|
| 12745 |
+
|
| 12746 |
+
node-gyp-build@4.8.4 (MIT)
|
| 12747 |
+
The MIT License (MIT)
|
| 12748 |
+
|
| 12749 |
+
Copyright (c) 2017 Mathias Buus
|
| 12750 |
+
|
| 12751 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 12752 |
+
of this software and associated documentation files (the "Software"), to deal
|
| 12753 |
+
in the Software without restriction, including without limitation the rights
|
| 12754 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 12755 |
+
copies of the Software, and to permit persons to whom the Software is
|
| 12756 |
+
furnished to do so, subject to the following conditions:
|
| 12757 |
+
|
| 12758 |
+
The above copyright notice and this permission notice shall be included in
|
| 12759 |
+
all copies or substantial portions of the Software.
|
| 12760 |
+
|
| 12761 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 12762 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 12763 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 12764 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 12765 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 12766 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
| 12767 |
+
THE SOFTWARE.
|
| 12768 |
+
|
| 12769 |
+
|
| 12770 |
+
----------------------------------------
|
| 12771 |
+
|
| 12772 |
+
node-liblzma@2.2.0 (LGPL-3.0)
|
| 12773 |
+
GNU LESSER GENERAL PUBLIC LICENSE
|
| 12774 |
+
Version 3, 29 June 2007
|
| 12775 |
+
|
| 12776 |
+
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
| 12777 |
+
Everyone is permitted to copy and distribute verbatim copies
|
| 12778 |
+
of this license document, but changing it is not allowed.
|
| 12779 |
+
|
| 12780 |
+
|
| 12781 |
+
This version of the GNU Lesser General Public License incorporates
|
| 12782 |
+
the terms and conditions of version 3 of the GNU General Public
|
| 12783 |
+
License, supplemented by the additional permissions listed below.
|
| 12784 |
+
|
| 12785 |
+
0. Additional Definitions.
|
| 12786 |
+
|
| 12787 |
+
As used herein, "this License" refers to version 3 of the GNU Lesser
|
| 12788 |
+
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
| 12789 |
+
General Public License.
|
| 12790 |
+
|
| 12791 |
+
"The Library" refers to a covered work governed by this License,
|
| 12792 |
+
other than an Application or a Combined Work as defined below.
|
| 12793 |
+
|
| 12794 |
+
An "Application" is any work that makes use of an interface provided
|
| 12795 |
+
by the Library, but which is not otherwise based on the Library.
|
| 12796 |
+
Defining a subclass of a class defined by the Library is deemed a mode
|
| 12797 |
+
of using an interface provided by the Library.
|
| 12798 |
+
|
| 12799 |
+
A "Combined Work" is a work produced by combining or linking an
|
| 12800 |
+
Application with the Library. The particular version of the Library
|
| 12801 |
+
with which the Combined Work was made is also called the "Linked
|
| 12802 |
+
Version".
|
| 12803 |
+
|
| 12804 |
+
The "Minimal Corresponding Source" for a Combined Work means the
|
| 12805 |
+
Corresponding Source for the Combined Work, excluding any source code
|
| 12806 |
+
for portions of the Combined Work that, considered in isolation, are
|
| 12807 |
+
based on the Application, and not on the Linked Version.
|
| 12808 |
+
|
| 12809 |
+
The "Corresponding Application Code" for a Combined Work means the
|
| 12810 |
+
object code and/or source code for the Application, including any data
|
| 12811 |
+
and utility programs needed for reproducing the Combined Work from the
|
| 12812 |
+
Application, but excluding the System Libraries of the Combined Work.
|
| 12813 |
+
|
| 12814 |
+
1. Exception to Section 3 of the GNU GPL.
|
| 12815 |
+
|
| 12816 |
+
You may convey a covered work under sections 3 and 4 of this License
|
| 12817 |
+
without being bound by section 3 of the GNU GPL.
|
| 12818 |
+
|
| 12819 |
+
2. Conveying Modified Versions.
|
| 12820 |
+
|
| 12821 |
+
If you modify a copy of the Library, and, in your modifications, a
|
| 12822 |
+
facility refers to a function or data to be supplied by an Application
|
| 12823 |
+
that uses the facility (other than as an argument passed when the
|
| 12824 |
+
facility is invoked), then you may convey a copy of the modified
|
| 12825 |
+
version:
|
| 12826 |
+
|
| 12827 |
+
a) under this License, provided that you make a good faith effort to
|
| 12828 |
+
ensure that, in the event an Application does not supply the
|
| 12829 |
+
function or data, the facility still operates, and performs
|
| 12830 |
+
whatever part of its purpose remains meaningful, or
|
| 12831 |
+
|
| 12832 |
+
b) under the GNU GPL, with none of the additional permissions of
|
| 12833 |
+
this License applicable to that copy.
|
| 12834 |
+
|
| 12835 |
+
3. Object Code Incorporating Material from Library Header Files.
|
| 12836 |
+
|
| 12837 |
+
The object code form of an Application may incorporate material from
|
| 12838 |
+
a header file that is part of the Library. You may convey such object
|
| 12839 |
+
code under terms of your choice, provided that, if the incorporated
|
| 12840 |
+
material is not limited to numerical parameters, data structure
|
| 12841 |
+
layouts and accessors, or small macros, inline functions and templates
|
| 12842 |
+
(ten or fewer lines in length), you do both of the following:
|
| 12843 |
+
|
| 12844 |
+
a) Give prominent notice with each copy of the object code that the
|
| 12845 |
+
Library is used in it and that the Library and its use are
|
| 12846 |
+
covered by this License.
|
| 12847 |
+
|
| 12848 |
+
b) Accompany the object code with a copy of the GNU GPL and this license
|
| 12849 |
+
document.
|
| 12850 |
+
|
| 12851 |
+
4. Combined Works.
|
| 12852 |
+
|
| 12853 |
+
You may convey a Combined Work under terms of your choice that,
|
| 12854 |
+
taken together, effectively do not restrict modification of the
|
| 12855 |
+
portions of the Library contained in the Combined Work and reverse
|
| 12856 |
+
engineering for debugging such modifications, if you also do each of
|
| 12857 |
+
the following:
|
| 12858 |
+
|
| 12859 |
+
a) Give prominent notice with each copy of the Combined Work that
|
| 12860 |
+
the Library is used in it and that the Library and its use are
|
| 12861 |
+
covered by this License.
|
| 12862 |
+
|
| 12863 |
+
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
| 12864 |
+
document.
|
| 12865 |
+
|
| 12866 |
+
c) For a Combined Work that displays copyright notices during
|
| 12867 |
+
execution, include the copyright notice for the Library among
|
| 12868 |
+
these notices, as well as a reference directing the user to the
|
| 12869 |
+
copies of the GNU GPL and this license document.
|
| 12870 |
+
|
| 12871 |
+
d) Do one of the following:
|
| 12872 |
+
|
| 12873 |
+
0) Convey the Minimal Corresponding Source under the terms of this
|
| 12874 |
+
License, and the Corresponding Application Code in a form
|
| 12875 |
+
suitable for, and under terms that permit, the user to
|
| 12876 |
+
recombine or relink the Application with a modified version of
|
| 12877 |
+
the Linked Version to produce a modified Combined Work, in the
|
| 12878 |
+
manner specified by section 6 of the GNU GPL for conveying
|
| 12879 |
+
Corresponding Source.
|
| 12880 |
+
|
| 12881 |
+
1) Use a suitable shared library mechanism for linking with the
|
| 12882 |
+
Library. A suitable mechanism is one that (a) uses at run time
|
| 12883 |
+
a copy of the Library already present on the user's computer
|
| 12884 |
+
system, and (b) will operate properly with a modified version
|
| 12885 |
+
of the Library that is interface-compatible with the Linked
|
| 12886 |
+
Version.
|
| 12887 |
+
|
| 12888 |
+
e) Provide Installation Information, but only if you would otherwise
|
| 12889 |
+
be required to provide such information under section 6 of the
|
| 12890 |
+
GNU GPL, and only to the extent that such information is
|
| 12891 |
+
necessary to install and execute a modified version of the
|
| 12892 |
+
Combined Work produced by recombining or relinking the
|
| 12893 |
+
Application with a modified version of the Linked Version. (If
|
| 12894 |
+
you use option 4d0, the Installation Information must accompany
|
| 12895 |
+
the Minimal Corresponding Source and Corresponding Application
|
| 12896 |
+
Code. If you use option 4d1, you must provide the Installation
|
| 12897 |
+
Information in the manner specified by section 6 of the GNU GPL
|
| 12898 |
+
for conveying Corresponding Source.)
|
| 12899 |
+
|
| 12900 |
+
5. Combined Libraries.
|
| 12901 |
+
|
| 12902 |
+
You may place library facilities that are a work based on the
|
| 12903 |
+
Library side by side in a single library together with other library
|
| 12904 |
+
facilities that are not Applications and are not covered by this
|
| 12905 |
+
License, and convey such a combined library under terms of your
|
| 12906 |
+
choice, if you do both of the following:
|
| 12907 |
+
|
| 12908 |
+
a) Accompany the combined library with a copy of the same work based
|
| 12909 |
+
on the Library, uncombined with any other library facilities,
|
| 12910 |
+
conveyed under the terms of this License.
|
| 12911 |
+
|
| 12912 |
+
b) Give prominent notice with the combined library that part of it
|
| 12913 |
+
is a work based on the Library, and explaining where to find the
|
| 12914 |
+
accompanying uncombined form of the same work.
|
| 12915 |
+
|
| 12916 |
+
6. Revised Versions of the GNU Lesser General Public License.
|
| 12917 |
+
|
| 12918 |
+
The Free Software Foundation may publish revised and/or new versions
|
| 12919 |
+
of the GNU Lesser General Public License from time to time. Such new
|
| 12920 |
+
versions will be similar in spirit to the present version, but may
|
| 12921 |
+
differ in detail to address new problems or concerns.
|
| 12922 |
+
|
| 12923 |
+
Each version is given a distinguishing version number. If the
|
| 12924 |
+
Library as you received it specifies that a certain numbered version
|
| 12925 |
+
of the GNU Lesser General Public License "or any later version"
|
| 12926 |
+
applies to it, you have the option of following the terms and
|
| 12927 |
+
conditions either of that published version or of any later version
|
| 12928 |
+
published by the Free Software Foundation. If the Library as you
|
| 12929 |
+
received it does not specify a version number of the GNU Lesser
|
| 12930 |
+
General Public License, you may choose any version of the GNU Lesser
|
| 12931 |
+
General Public License ever published by the Free Software Foundation.
|
| 12932 |
+
|
| 12933 |
+
If the Library as you received it specifies that a proxy can decide
|
| 12934 |
+
whether future versions of the GNU Lesser General Public License shall
|
| 12935 |
+
apply, that proxy's public statement of acceptance of any version is
|
| 12936 |
+
permanent authorization for you to choose that version for the
|
| 12937 |
+
Library.
|
| 12938 |
+
|
| 12939 |
+
|
| 12940 |
----------------------------------------
|
| 12941 |
|
| 12942 |
object-keys@1.1.1 (MIT)
|
licenses/pi-components.json
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"package": "@earendil-works/pi-coding-agent",
|
| 3 |
+
"version": "0.85.1",
|
| 4 |
+
"source": "https://github.com/earendil-works/pi/tree/v0.85.1/packages/coding-agent",
|
| 5 |
+
"files": {
|
| 6 |
+
"components/custom-editor.js": "000b0056f2bf645537537468bdd6f6ee0c82e4110f5417c1c70c2e8fefc04f29",
|
| 7 |
+
"components/user-message.js": "398a34b8a979daea5614be4cbbe6ff66e7eca97e9fd855508791dcddce994bc8",
|
| 8 |
+
"components/assistant-message.js": "edbd1e712234609d2ad1078edcf960e3b6f549669075f1c5d1a7a4481e2b3d19",
|
| 9 |
+
"components/markdown-transform.js": "31ad2245189328104a31bb9b12f58b0a12e4af9c7924775a242c9dcd526d2c1a",
|
| 10 |
+
"theme/light.json": "14c7172ba7e75eab6f509504de806af0f2d9ff817bfb45d9533bff15c7e6e657"
|
| 11 |
+
}
|
| 12 |
+
}
|
research/browser-agent/REPORT.md
CHANGED
|
@@ -28,7 +28,7 @@ The three README snapshots in this directory are upstream documentation fetched
|
|
| 28 |
|
| 29 |
## Current validation artifacts
|
| 30 |
|
| 31 |
-
- `validation/agent-browser.json`:
|
| 32 |
- `validation/agent-browser-hub.json`: public model download, interrupted-load retry, actual offline agent workflows, editing, tool-error handling, and persistence.
|
| 33 |
- `validation/agent-browser-limit128.json`: the same runtime under smaller GPU buffer limits.
|
| 34 |
- `app/tests/protocol.test.mjs`: XML/CDATAs, malformed call rejection, history/context conversion, real just-bash commands, lossless workspace restoration, and inert Markdown rendering.
|
|
@@ -52,3 +52,22 @@ The browser workspace is published to [Mike0021/MiniCPM5-2B-WebGPU](https://hugg
|
|
| 52 |
The embedded page initially exposed denied OPFS, IndexedDB, and Web Locks access. Inspection identified Playwright 1.58's default `--disable-features` list as explicitly disabling `ThirdPartyStoragePartitioning`. Restoring only normal Chromium storage partitioning allowed the real Hugging Face iframe to load the model and complete the CSV/report task through its actual chat controls. The app also provides a direct new-tab link when a browser does deny embedded storage. This does not change browser privacy settings or grant storage permission automatically. Hugging Face documents the cross-domain iframe structure and related restrictions in its [cookie limitations guide](https://huggingface.co/docs/hub/spaces-cookie-limitations).
|
| 53 |
|
| 54 |
`validation/agent-space-deployment.json`, `agent-space-embedded.json`, and `agent-space-checksums.json` contain the deployed results. The restricted-storage/new-tab path has a separate browser test in `app/tests/space-restricted.mjs`.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 28 |
|
| 29 |
## Current validation artifacts
|
| 30 |
|
| 31 |
+
- `validation/agent-browser.json`: latest actual-model integration and reload evidence (also saved as `agent-terminal-model.json` for the terminal integration).
|
| 32 |
- `validation/agent-browser-hub.json`: public model download, interrupted-load retry, actual offline agent workflows, editing, tool-error handling, and persistence.
|
| 33 |
- `validation/agent-browser-limit128.json`: the same runtime under smaller GPU buffer limits.
|
| 34 |
- `app/tests/protocol.test.mjs`: XML/CDATAs, malformed call rejection, history/context conversion, real just-bash commands, lossless workspace restoration, and inert Markdown rendering.
|
|
|
|
| 52 |
The embedded page initially exposed denied OPFS, IndexedDB, and Web Locks access. Inspection identified Playwright 1.58's default `--disable-features` list as explicitly disabling `ThirdPartyStoragePartitioning`. Restoring only normal Chromium storage partitioning allowed the real Hugging Face iframe to load the model and complete the CSV/report task through its actual chat controls. The app also provides a direct new-tab link when a browser does deny embedded storage. This does not change browser privacy settings or grant storage permission automatically. Hugging Face documents the cross-domain iframe structure and related restrictions in its [cookie limitations guide](https://huggingface.co/docs/hub/spaces-cookie-limitations).
|
| 53 |
|
| 54 |
`validation/agent-space-deployment.json`, `agent-space-embedded.json`, and `agent-space-checksums.json` contain the deployed results. The restricted-storage/new-tab path has a separate browser test in `app/tests/space-restricted.mjs`.
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
## Pi terminal UI in xterm.js β 2026-09-12
|
| 58 |
+
|
| 59 |
+
The left column now uses Pi's original terminal UI through xterm.js. The user chose to keep all execution in the browser. This is a browser host for Pi's presentation components and agent core; it does not launch the full Node CLI.
|
| 60 |
+
|
| 61 |
+
Pi's published `Terminal` interface accepts input, output, resize, and lifecycle operations, making xterm.js a suitable replacement for `ProcessTerminal`. The browser imports `TuiAltScreen`, `Editor`, Markdown, containers, scroll/search handling, keybindings, and `StdinBuffer` from `@earendil-works/pi-tui@0.85.1`. It also uses the unmodified CLI `CustomEditor`, `UserMessageComponent`, and `AssistantMessageComponent` copied from the matching npm tarball, with SHA-256 provenance and MIT licensing. [Pi TUI documentation](https://github.com/earendil-works/pi/tree/v0.85.1/packages/tui), [CLI presentation source](https://github.com/earendil-works/pi/tree/v0.85.1/packages/coding-agent/src/modes/interactive/components), [xterm fit addon documentation](https://xtermjs.org/docs/guides/using-addons/).
|
| 62 |
+
|
| 63 |
+
The original CLI entry point depends on Node filesystem/process services and native terminal discovery. Browser-specific host adapters instead supply the terminal, scheduling, Buffer/EventEmitter, clipboard, text-only terminal capabilities, and the upstream light theme without its file watcher. The build selects only the required TUI exports, leaving Node startup and provider/extension management out. Every vendored component is verified against its recorded upstream checksum when Vite loads its config. There are no edits to installed or vendored Pi source files.
|
| 64 |
+
|
| 65 |
+
App code connects Pi's editor to the existing worker and implements `/help`, `/load`, `/new`, `/clear`, `!command`, virtual-file completion, and just-bash tool cards. The worker bundle remains `agent.worker-DqQzo9yL.js`: model inference, quantization, downloads, and agent tool execution are unchanged. The terminal uses xterm's default renderer, without a second WebGPU/WebGL rendering context.
|
| 66 |
+
|
| 67 |
+
The browser adapter preserves native clipboard paste and sends Shift+Enter as Alt+Enter. It translates Ctrl+Shift+F into CSI-u because legacy terminal input otherwise loses that modifier distinction. Large Pi paste markers are expanded before the Send button submits text. Text from tools, files, and model messages is stripped of terminal control sequences before presentation; only user-clicked HTTP(S) Markdown links may open a tab. Images do not download.
|
| 68 |
+
|
| 69 |
+
`validation/agent-terminal.json` and `agent-terminal-dev.json` record the production/development keyboard checks: multiline editing, cursor/deletion operations, prompt history, slash completion, shell file writes, escape-sequence stripping, Unicode clipboard paste, large-paste expansion, and live resizing from 90 to 40 columns while preserving a draft. A dedicated inner mount keeps xterm's fit calculation inside the panel's padding. Development mode explicitly bundles the CommonJS EventEmitter/Buffer dependencies and adapts versioned module URLs.
|
| 70 |
+
|
| 71 |
+
`validation/agent-terminal-model.json` records five real MiniCPM workflows submitted through xterm keyboard input and Pi's editor: unseen-file read, CSV calculation/report, script creation/execution, edit/verification, and recovery from a missing-file error. All passed. Escape stopped generation; editor/import/manual shell, persistence, and cache reload passed. Verified cached model setup took 2.281 seconds and the subsequent reload 2.122 seconds in this run. The five workflows made zero network requests with networking disabled. Reload fetched zero weight shards. These are integration observations on the desktop AMD GPU, not a new quality or throughput benchmark.
|
| 72 |
+
|
| 73 |
+
An early automation attempt used CDP bulk `insertText`, which did not traverse xterm's normal keyboard input path and left the test waiting for a draft. The final model suite uses actual keyboard events. Physical Android/iOS input methods, mobile GPU execution, and embedded hosting of this new terminal build have not been verified. The previous deployed Space evidence remains evidence for the earlier chat UI.
|
scripts/prepare_space.py
CHANGED
|
@@ -23,7 +23,7 @@ app_out = out / 'app'
|
|
| 23 |
app_out.mkdir()
|
| 24 |
for name in ['package.json', 'package-lock.json', 'index.html', 'vite.config.mjs', 'README.md']:
|
| 25 |
shutil.copyfile(root / 'app' / name, app_out / name)
|
| 26 |
-
for name in ['src', 'scripts', 'public', 'tests']:
|
| 27 |
shutil.copytree(root / 'app' / name, app_out / name)
|
| 28 |
|
| 29 |
validation = out / 'validation'
|
|
@@ -37,6 +37,7 @@ for name in [
|
|
| 37 |
'agent-browser-no-gpu.json', 'agent-completion.json',
|
| 38 |
'agent-space-deployment.json', 'agent-space-embedded.json', 'agent-space-restricted.json',
|
| 39 |
'agent-space-checksums.json',
|
|
|
|
| 40 |
]:
|
| 41 |
source = root / 'validation' / name
|
| 42 |
if source.is_file():
|
|
|
|
| 23 |
app_out.mkdir()
|
| 24 |
for name in ['package.json', 'package-lock.json', 'index.html', 'vite.config.mjs', 'README.md']:
|
| 25 |
shutil.copyfile(root / 'app' / name, app_out / name)
|
| 26 |
+
for name in ['src', 'scripts', 'public', 'tests', 'vendor']:
|
| 27 |
shutil.copytree(root / 'app' / name, app_out / name)
|
| 28 |
|
| 29 |
validation = out / 'validation'
|
|
|
|
| 37 |
'agent-browser-no-gpu.json', 'agent-completion.json',
|
| 38 |
'agent-space-deployment.json', 'agent-space-embedded.json', 'agent-space-restricted.json',
|
| 39 |
'agent-space-checksums.json',
|
| 40 |
+
'agent-terminal.json', 'agent-terminal-model.json', 'agent-terminal-dev.json',
|
| 41 |
]:
|
| 42 |
source = root / 'validation' / name
|
| 43 |
if source.is_file():
|
validation/agent-terminal-dev.json
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"date": "2026-09-12T08:25:07.051Z",
|
| 3 |
+
"url": "http://127.0.0.1:5173/",
|
| 4 |
+
"errors": [],
|
| 5 |
+
"requests": [
|
| 6 |
+
"http://127.0.0.1:5173/",
|
| 7 |
+
"http://127.0.0.1:5173/@vite/client",
|
| 8 |
+
"http://127.0.0.1:5173/src/main.mjs?t=1789201506558",
|
| 9 |
+
"http://127.0.0.1:5173/node_modules/vite/dist/client/env.mjs",
|
| 10 |
+
"http://127.0.0.1:5173/src/style.css",
|
| 11 |
+
"http://127.0.0.1:5173/src/pi/terminal-ui.mjs?t=1789201506558",
|
| 12 |
+
"http://127.0.0.1:5173/node_modules/.vite/deps/@xterm_xterm.js?v=55d56926",
|
| 13 |
+
"http://127.0.0.1:5173/node_modules/.vite/deps/@xterm_addon-fit.js?v=55d56926",
|
| 14 |
+
"http://127.0.0.1:5173/node_modules/@xterm/xterm/css/xterm.css",
|
| 15 |
+
"http://127.0.0.1:5173/src/pi/entry.mjs",
|
| 16 |
+
"http://127.0.0.1:5173/vendor/pi-coding-agent/components/custom-editor.js",
|
| 17 |
+
"http://127.0.0.1:5173/vendor/pi-coding-agent/components/user-message.js",
|
| 18 |
+
"http://127.0.0.1:5173/vendor/pi-coding-agent/components/assistant-message.js",
|
| 19 |
+
"http://127.0.0.1:5173/src/pi/theme.mjs",
|
| 20 |
+
"http://127.0.0.1:5173/src/pi/xterm-terminal.mjs",
|
| 21 |
+
"http://127.0.0.1:5173/src/protocol.mjs",
|
| 22 |
+
"http://127.0.0.1:5173/vendor/pi-coding-agent/components/markdown-transform.js",
|
| 23 |
+
"http://127.0.0.1:5173/node_modules/@earendil-works/pi-tui/dist/tui-alt-screen.js?v=55d56926",
|
| 24 |
+
"http://127.0.0.1:5173/node_modules/@earendil-works/pi-tui/dist/tui.js?v=55d56926",
|
| 25 |
+
"http://127.0.0.1:5173/node_modules/@earendil-works/pi-tui/dist/components/editor.js?v=55d56926",
|
| 26 |
+
"http://127.0.0.1:5173/node_modules/@earendil-works/pi-tui/dist/components/text.js?v=55d56926",
|
| 27 |
+
"http://127.0.0.1:5173/node_modules/@earendil-works/pi-tui/dist/components/truncated-text.js?v=55d56926",
|
| 28 |
+
"http://127.0.0.1:5173/node_modules/@earendil-works/pi-tui/dist/components/markdown.js?v=55d56926",
|
| 29 |
+
"http://127.0.0.1:5173/node_modules/@earendil-works/pi-tui/dist/components/box.js?v=55d56926",
|
| 30 |
+
"http://127.0.0.1:5173/node_modules/@earendil-works/pi-tui/dist/components/spacer.js?v=55d56926",
|
| 31 |
+
"http://127.0.0.1:5173/node_modules/@earendil-works/pi-tui/dist/components/mouse-region.js?v=55d56926",
|
| 32 |
+
"http://127.0.0.1:5173/node_modules/@earendil-works/pi-tui/dist/components/scroll-view.js?v=55d56926",
|
| 33 |
+
"http://127.0.0.1:5173/node_modules/@earendil-works/pi-tui/dist/components/v-stack.js?v=55d56926",
|
| 34 |
+
"http://127.0.0.1:5173/node_modules/@earendil-works/pi-tui/dist/keybindings.js?v=55d56926",
|
| 35 |
+
"http://127.0.0.1:5173/node_modules/@earendil-works/pi-tui/dist/keys.js?v=55d56926",
|
| 36 |
+
"http://127.0.0.1:5173/node_modules/@earendil-works/pi-tui/dist/stdin-buffer.js?v=55d56926",
|
| 37 |
+
"http://127.0.0.1:5173/node_modules/@earendil-works/pi-tui/dist/utils.js?v=55d56926",
|
| 38 |
+
"http://127.0.0.1:5173/",
|
| 39 |
+
"http://127.0.0.1:5173/vendor/pi-coding-agent/theme/light.json?import",
|
| 40 |
+
"http://127.0.0.1:5173/node_modules/.vite/deps/@xmldom_xmldom.js?v=55d56926",
|
| 41 |
+
"http://127.0.0.1:5173/src/pi/browser-host.mjs",
|
| 42 |
+
"http://127.0.0.1:5173/node_modules/@earendil-works/pi-tui/dist/terminal-colors.js?v=55d56926",
|
| 43 |
+
"http://127.0.0.1:5173/src/pi/capabilities.mjs",
|
| 44 |
+
"http://127.0.0.1:5173/node_modules/@earendil-works/pi-tui/dist/alt-screen-search.js?v=55d56926",
|
| 45 |
+
"http://127.0.0.1:5173/node_modules/@earendil-works/pi-tui/dist/components/alt-screen-flash.js?v=55d56926",
|
| 46 |
+
"http://127.0.0.1:5173/node_modules/@earendil-works/pi-tui/dist/layout.js?v=55d56926",
|
| 47 |
+
"http://127.0.0.1:5173/node_modules/@earendil-works/pi-tui/dist/layout-node.js?v=55d56926",
|
| 48 |
+
"http://127.0.0.1:5173/node_modules/marked/lib/marked.esm.js?v=55d56926",
|
| 49 |
+
"http://127.0.0.1:5173/node_modules/@earendil-works/pi-tui/dist/latex.js?v=55d56926",
|
| 50 |
+
"http://127.0.0.1:5173/node_modules/@earendil-works/pi-tui/dist/kill-ring.js?v=55d56926",
|
| 51 |
+
"http://127.0.0.1:5173/node_modules/@earendil-works/pi-tui/dist/undo-stack.js?v=55d56926",
|
| 52 |
+
"http://127.0.0.1:5173/node_modules/@earendil-works/pi-tui/dist/word-navigation.js?v=55d56926",
|
| 53 |
+
"http://127.0.0.1:5173/node_modules/@earendil-works/pi-tui/dist/components/select-list.js?v=55d56926",
|
| 54 |
+
"http://127.0.0.1:5173/@vite/client",
|
| 55 |
+
"http://127.0.0.1:5173/src/main.mjs?t=1789201506558",
|
| 56 |
+
"http://127.0.0.1:5173/node_modules/vite/dist/client/env.mjs",
|
| 57 |
+
"http://127.0.0.1:5173/src/style.css",
|
| 58 |
+
"http://127.0.0.1:5173/src/pi/terminal-ui.mjs?t=1789201506558",
|
| 59 |
+
"http://127.0.0.1:5173/node_modules/.vite/deps/@xterm_xterm.js?v=55d56926",
|
| 60 |
+
"http://127.0.0.1:5173/node_modules/.vite/deps/@xterm_addon-fit.js?v=55d56926",
|
| 61 |
+
"http://127.0.0.1:5173/node_modules/@xterm/xterm/css/xterm.css",
|
| 62 |
+
"http://127.0.0.1:5173/src/pi/entry.mjs",
|
| 63 |
+
"http://127.0.0.1:5173/vendor/pi-coding-agent/components/custom-editor.js",
|
| 64 |
+
"http://127.0.0.1:5173/vendor/pi-coding-agent/components/user-message.js",
|
| 65 |
+
"http://127.0.0.1:5173/vendor/pi-coding-agent/components/assistant-message.js",
|
| 66 |
+
"http://127.0.0.1:5173/src/pi/theme.mjs",
|
| 67 |
+
"http://127.0.0.1:5173/src/pi/xterm-terminal.mjs",
|
| 68 |
+
"http://127.0.0.1:5173/src/protocol.mjs",
|
| 69 |
+
"http://127.0.0.1:5173/node_modules/@earendil-works/pi-tui/dist/tui-alt-screen.js?v=55d56926",
|
| 70 |
+
"http://127.0.0.1:5173/node_modules/@earendil-works/pi-tui/dist/tui.js?v=55d56926",
|
| 71 |
+
"http://127.0.0.1:5173/node_modules/@earendil-works/pi-tui/dist/components/editor.js?v=55d56926",
|
| 72 |
+
"http://127.0.0.1:5173/node_modules/@earendil-works/pi-tui/dist/components/text.js?v=55d56926",
|
| 73 |
+
"http://127.0.0.1:5173/node_modules/@earendil-works/pi-tui/dist/components/truncated-text.js?v=55d56926",
|
| 74 |
+
"http://127.0.0.1:5173/node_modules/@earendil-works/pi-tui/dist/components/markdown.js?v=55d56926",
|
| 75 |
+
"http://127.0.0.1:5173/node_modules/@earendil-works/pi-tui/dist/components/box.js?v=55d56926",
|
| 76 |
+
"http://127.0.0.1:5173/node_modules/@earendil-works/pi-tui/dist/components/spacer.js?v=55d56926",
|
| 77 |
+
"http://127.0.0.1:5173/node_modules/@earendil-works/pi-tui/dist/components/mouse-region.js?v=55d56926",
|
| 78 |
+
"http://127.0.0.1:5173/node_modules/@earendil-works/pi-tui/dist/components/scroll-view.js?v=55d56926",
|
| 79 |
+
"http://127.0.0.1:5173/node_modules/@earendil-works/pi-tui/dist/components/v-stack.js?v=55d56926",
|
| 80 |
+
"http://127.0.0.1:5173/node_modules/@earendil-works/pi-tui/dist/keybindings.js?v=55d56926",
|
| 81 |
+
"http://127.0.0.1:5173/node_modules/@earendil-works/pi-tui/dist/keys.js?v=55d56926",
|
| 82 |
+
"http://127.0.0.1:5173/node_modules/@earendil-works/pi-tui/dist/stdin-buffer.js?v=55d56926",
|
| 83 |
+
"http://127.0.0.1:5173/node_modules/@earendil-works/pi-tui/dist/utils.js?v=55d56926",
|
| 84 |
+
"http://127.0.0.1:5173/src/pi/browser-host.mjs",
|
| 85 |
+
"http://127.0.0.1:5173/node_modules/@earendil-works/pi-tui/dist/alt-screen-search.js?v=55d56926",
|
| 86 |
+
"http://127.0.0.1:5173/node_modules/@earendil-works/pi-tui/dist/components/alt-screen-flash.js?v=55d56926",
|
| 87 |
+
"http://127.0.0.1:5173/node_modules/@earendil-works/pi-tui/dist/layout.js?v=55d56926",
|
| 88 |
+
"http://127.0.0.1:5173/node_modules/@earendil-works/pi-tui/dist/layout-node.js?v=55d56926",
|
| 89 |
+
"http://127.0.0.1:5173/src/pi/capabilities.mjs",
|
| 90 |
+
"http://127.0.0.1:5173/node_modules/@earendil-works/pi-tui/dist/terminal-colors.js?v=55d56926",
|
| 91 |
+
"http://127.0.0.1:5173/node_modules/@earendil-works/pi-tui/dist/kill-ring.js?v=55d56926",
|
| 92 |
+
"http://127.0.0.1:5173/node_modules/@earendil-works/pi-tui/dist/undo-stack.js?v=55d56926",
|
| 93 |
+
"http://127.0.0.1:5173/node_modules/@earendil-works/pi-tui/dist/word-navigation.js?v=55d56926",
|
| 94 |
+
"http://127.0.0.1:5173/node_modules/@earendil-works/pi-tui/dist/components/select-list.js?v=55d56926",
|
| 95 |
+
"http://127.0.0.1:5173/node_modules/marked/lib/marked.esm.js?v=55d56926",
|
| 96 |
+
"http://127.0.0.1:5173/node_modules/@earendil-works/pi-tui/dist/latex.js?v=55d56926",
|
| 97 |
+
"http://127.0.0.1:5173/vendor/pi-coding-agent/components/markdown-transform.js",
|
| 98 |
+
"http://127.0.0.1:5173/vendor/pi-coding-agent/theme/light.json?import",
|
| 99 |
+
"http://127.0.0.1:5173/node_modules/.vite/deps/@xmldom_xmldom.js?v=55d56926",
|
| 100 |
+
"http://127.0.0.1:5173/node_modules/@earendil-works/pi-tui/dist/components/stack.js?v=55d56926",
|
| 101 |
+
"http://127.0.0.1:5173/node_modules/.vite/deps/events.js?v=55d56926",
|
| 102 |
+
"http://127.0.0.1:5173/node_modules/.vite/deps/buffer.js?v=55d56926",
|
| 103 |
+
"http://127.0.0.1:5173/node_modules/@earendil-works/pi-tui/dist/components/input.js?v=55d56926",
|
| 104 |
+
"http://127.0.0.1:5173/node_modules/get-east-asian-width/index.js?v=55d56926",
|
| 105 |
+
"http://127.0.0.1:5173/node_modules/.vite/deps/rolldown-runtime-B-lAHAz2.js?v=55d56926",
|
| 106 |
+
"http://127.0.0.1:5173/node_modules/get-east-asian-width/lookup.js?v=55d56926",
|
| 107 |
+
"http://127.0.0.1:5173/node_modules/get-east-asian-width/lookup-data.js?v=55d56926",
|
| 108 |
+
"http://127.0.0.1:5173/node_modules/get-east-asian-width/utilities.js?v=55d56926",
|
| 109 |
+
"http://127.0.0.1:5173/src/agent.worker.mjs?worker_file&type=module",
|
| 110 |
+
"http://127.0.0.1:5173/node_modules/vite/dist/client/env.mjs",
|
| 111 |
+
"http://127.0.0.1:5173/node_modules/.vite/deps/@earendil-works_pi-agent-core.js?v=55d56926",
|
| 112 |
+
"http://127.0.0.1:5173/src/inference.mjs",
|
| 113 |
+
"http://127.0.0.1:5173/src/workspace.mjs",
|
| 114 |
+
"http://127.0.0.1:5173/src/storage.mjs",
|
| 115 |
+
"http://127.0.0.1:5173/node_modules/.vite/deps/@huggingface_transformers.js?v=55d56926",
|
| 116 |
+
"http://127.0.0.1:5173/node_modules/.vite/deps/@earendil-works_pi-ai_utils_event-stream.js?v=55d56926",
|
| 117 |
+
"http://127.0.0.1:5173/src/download.mjs",
|
| 118 |
+
"http://127.0.0.1:5173/src/protocol.mjs",
|
| 119 |
+
"http://127.0.0.1:5173/node_modules/.vite/deps/just-bash_browser.js?v=55d56926",
|
| 120 |
+
"http://127.0.0.1:5173/node_modules/.vite/deps/@earendil-works_pi-ai.js?v=55d56926",
|
| 121 |
+
"http://127.0.0.1:5173/favicon.svg",
|
| 122 |
+
"http://127.0.0.1:5173/node_modules/.vite/deps/rolldown-runtime-B-lAHAz2.js?v=55d56926",
|
| 123 |
+
"http://127.0.0.1:5173/node_modules/.vite/deps/dist-HiCO7I8Y.js?v=55d56926",
|
| 124 |
+
"http://127.0.0.1:5173/node_modules/.vite/deps/libesm-Ds0pOwVw.js?v=55d56926",
|
| 125 |
+
"http://127.0.0.1:5173/node_modules/.vite/deps/hash-wasm.js?v=55d56926",
|
| 126 |
+
"http://127.0.0.1:5173/src/model-manifest.json?import",
|
| 127 |
+
"http://127.0.0.1:5173/node_modules/.vite/deps/@xmldom_xmldom.js?v=55d56926",
|
| 128 |
+
"http://127.0.0.1:5173/@vite/client"
|
| 129 |
+
],
|
| 130 |
+
"checks": [
|
| 131 |
+
"Original Pi TUI rendered through xterm",
|
| 132 |
+
"Enter submits /help without loading a model",
|
| 133 |
+
"Pi fullscreen transcript search preserves the editor draft",
|
| 134 |
+
"Pi multiline editor: Shift+Enter, cursor, deletion, Ctrl+U/K",
|
| 135 |
+
"Pi prompt history",
|
| 136 |
+
"Pi autocomplete: /he β /help",
|
| 137 |
+
"xterm keyboard β Pi editor β real just-bash β workspace file",
|
| 138 |
+
"Shell escape sequences render as inert text",
|
| 139 |
+
"Large Pi paste markers expand before Send",
|
| 140 |
+
"Unicode multiline clipboard paste",
|
| 141 |
+
"Touch-accessible newline button",
|
| 142 |
+
"390px layout and live terminal resize preserve the draft"
|
| 143 |
+
],
|
| 144 |
+
"initial": " \n Ο Pi browser workspace \n \n Ask Pi to work with the files on the right. \n \n /load load MiniCPM \n /help show commands \n !ls run a workspace command \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n Files and chat are saved on this device. \nββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ\n \nββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ\n /workspace Β· MiniCPM5-2B Β· 4,096 context ",
|
| 145 |
+
"sizes": {
|
| 146 |
+
"desktop": {
|
| 147 |
+
"cols": 90,
|
| 148 |
+
"rows": 41
|
| 149 |
+
},
|
| 150 |
+
"mobile": {
|
| 151 |
+
"cols": 40,
|
| 152 |
+
"rows": 32
|
| 153 |
+
}
|
| 154 |
+
},
|
| 155 |
+
"mobileLimitation": "Desktop Chromium viewport simulation; physical mobile keyboards and GPUs not tested.",
|
| 156 |
+
"passed": true
|
| 157 |
+
}
|
validation/agent-terminal-model.json
ADDED
|
@@ -0,0 +1,1885 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"date": "2026-09-12T08:21:57.234Z",
|
| 3 |
+
"url": "http://127.0.0.1:4173/?source=local",
|
| 4 |
+
"cases": [
|
| 5 |
+
{
|
| 6 |
+
"name": "read-unseen-file",
|
| 7 |
+
"elapsedMs": 2233,
|
| 8 |
+
"version": 1,
|
| 9 |
+
"files": {
|
| 10 |
+
"/tmp/revenue.txt": "Notebook 12 5 60\nPencil 30 2 60\nFolder 8 3 24\nTOTAL: 144\n",
|
| 11 |
+
"/workspace/README.md": "# Browser workspace\n\nPi can read, write, edit, and run shell commands here.\nEverything stays in this browser. Try analyzing sales.csv.\n",
|
| 12 |
+
"/workspace/revenue.txt": "Notebook 12 5 -> 12 x 5 = 60\nPencil 30 2 -> 30 x 2 = 60\nFolder 8 3 -> 8 x 3 = 24\nTOTAL REVENUE: 144\n",
|
| 13 |
+
"/workspace/sales.csv": "product,units,price\nNotebook,12,5\nPencil,30,2\nFolder,8,3\n",
|
| 14 |
+
"/workspace/secret.txt": "LOCAL-fd008be1"
|
| 15 |
+
},
|
| 16 |
+
"entries": [
|
| 17 |
+
[
|
| 18 |
+
"/",
|
| 19 |
+
{
|
| 20 |
+
"type": "directory",
|
| 21 |
+
"mode": 493
|
| 22 |
+
}
|
| 23 |
+
],
|
| 24 |
+
[
|
| 25 |
+
"/tmp",
|
| 26 |
+
{
|
| 27 |
+
"type": "directory",
|
| 28 |
+
"mode": 493
|
| 29 |
+
}
|
| 30 |
+
],
|
| 31 |
+
[
|
| 32 |
+
"/tmp/revenue.txt",
|
| 33 |
+
{
|
| 34 |
+
"type": "file",
|
| 35 |
+
"base64": "Tm90ZWJvb2sgMTIgNSA2MApQZW5jaWwgMzAgMiA2MApGb2xkZXIgOCAzIDI0ClRPVEFMOiAxNDQK",
|
| 36 |
+
"mode": 420
|
| 37 |
+
}
|
| 38 |
+
],
|
| 39 |
+
[
|
| 40 |
+
"/usr",
|
| 41 |
+
{
|
| 42 |
+
"type": "directory",
|
| 43 |
+
"mode": 493
|
| 44 |
+
}
|
| 45 |
+
],
|
| 46 |
+
[
|
| 47 |
+
"/workspace",
|
| 48 |
+
{
|
| 49 |
+
"type": "directory",
|
| 50 |
+
"mode": 493
|
| 51 |
+
}
|
| 52 |
+
],
|
| 53 |
+
[
|
| 54 |
+
"/workspace/README.md",
|
| 55 |
+
{
|
| 56 |
+
"type": "file",
|
| 57 |
+
"base64": "IyBCcm93c2VyIHdvcmtzcGFjZQoKUGkgY2FuIHJlYWQsIHdyaXRlLCBlZGl0LCBhbmQgcnVuIHNoZWxsIGNvbW1hbmRzIGhlcmUuCkV2ZXJ5dGhpbmcgc3RheXMgaW4gdGhpcyBicm93c2VyLiBUcnkgYW5hbHl6aW5nIHNhbGVzLmNzdi4K",
|
| 58 |
+
"mode": 420
|
| 59 |
+
}
|
| 60 |
+
],
|
| 61 |
+
[
|
| 62 |
+
"/workspace/revenue.txt",
|
| 63 |
+
{
|
| 64 |
+
"type": "file",
|
| 65 |
+
"base64": "Tm90ZWJvb2sgMTIgNSAtPiAxMiB4IDUgPSA2MApQZW5jaWwgMzAgMiAtPiAzMCB4IDIgPSA2MApGb2xkZXIgOCAzIC0+IDggeCAzID0gMjQKVE9UQUwgUkVWRU5VRTogMTQ0Cg==",
|
| 66 |
+
"mode": 420
|
| 67 |
+
}
|
| 68 |
+
],
|
| 69 |
+
[
|
| 70 |
+
"/workspace/sales.csv",
|
| 71 |
+
{
|
| 72 |
+
"type": "file",
|
| 73 |
+
"base64": "cHJvZHVjdCx1bml0cyxwcmljZQpOb3RlYm9vaywxMiw1ClBlbmNpbCwzMCwyCkZvbGRlciw4LDMK",
|
| 74 |
+
"mode": 420
|
| 75 |
+
}
|
| 76 |
+
],
|
| 77 |
+
[
|
| 78 |
+
"/workspace/secret.txt",
|
| 79 |
+
{
|
| 80 |
+
"type": "file",
|
| 81 |
+
"base64": "TE9DQUwtZmQwMDhiZTE=",
|
| 82 |
+
"mode": 420
|
| 83 |
+
}
|
| 84 |
+
]
|
| 85 |
+
],
|
| 86 |
+
"messages": [
|
| 87 |
+
{
|
| 88 |
+
"role": "user",
|
| 89 |
+
"content": [
|
| 90 |
+
{
|
| 91 |
+
"type": "text",
|
| 92 |
+
"text": "Read secret.txt with the read tool and tell me its exact contents."
|
| 93 |
+
}
|
| 94 |
+
],
|
| 95 |
+
"timestamp": 1789201320594
|
| 96 |
+
},
|
| 97 |
+
{
|
| 98 |
+
"role": "assistant",
|
| 99 |
+
"api": "minicpm-webgpu",
|
| 100 |
+
"provider": "browser",
|
| 101 |
+
"model": "Mike0021/MiniCPM5-2B-ONNX",
|
| 102 |
+
"timestamp": 1789201320595,
|
| 103 |
+
"content": [
|
| 104 |
+
{
|
| 105 |
+
"type": "toolCall",
|
| 106 |
+
"id": "4e94e30c-5284-416d-aacb-c66b4d8bc051",
|
| 107 |
+
"name": "read",
|
| 108 |
+
"arguments": {
|
| 109 |
+
"path": "/workspace/secret.txt"
|
| 110 |
+
}
|
| 111 |
+
}
|
| 112 |
+
],
|
| 113 |
+
"stopReason": "toolUse",
|
| 114 |
+
"usage": {
|
| 115 |
+
"input": 717,
|
| 116 |
+
"output": 18,
|
| 117 |
+
"cacheRead": 0,
|
| 118 |
+
"cacheWrite": 0,
|
| 119 |
+
"totalTokens": 735,
|
| 120 |
+
"cost": {
|
| 121 |
+
"input": 0,
|
| 122 |
+
"output": 0,
|
| 123 |
+
"cacheRead": 0,
|
| 124 |
+
"cacheWrite": 0,
|
| 125 |
+
"total": 0
|
| 126 |
+
}
|
| 127 |
+
}
|
| 128 |
+
},
|
| 129 |
+
{
|
| 130 |
+
"role": "toolResult",
|
| 131 |
+
"toolCallId": "4e94e30c-5284-416d-aacb-c66b4d8bc051",
|
| 132 |
+
"toolName": "read",
|
| 133 |
+
"content": [
|
| 134 |
+
{
|
| 135 |
+
"type": "text",
|
| 136 |
+
"text": "LOCAL-fd008be1"
|
| 137 |
+
}
|
| 138 |
+
],
|
| 139 |
+
"details": {},
|
| 140 |
+
"isError": false,
|
| 141 |
+
"timestamp": 1789201321616
|
| 142 |
+
},
|
| 143 |
+
{
|
| 144 |
+
"role": "assistant",
|
| 145 |
+
"api": "minicpm-webgpu",
|
| 146 |
+
"provider": "browser",
|
| 147 |
+
"model": "Mike0021/MiniCPM5-2B-ONNX",
|
| 148 |
+
"timestamp": 1789201321616,
|
| 149 |
+
"content": [
|
| 150 |
+
{
|
| 151 |
+
"type": "text",
|
| 152 |
+
"text": "The read tool returned the contents of secret.txt as:\n\nLOCAL-fd008be1"
|
| 153 |
+
}
|
| 154 |
+
],
|
| 155 |
+
"stopReason": "stop",
|
| 156 |
+
"usage": {
|
| 157 |
+
"input": 771,
|
| 158 |
+
"output": 20,
|
| 159 |
+
"cacheRead": 0,
|
| 160 |
+
"cacheWrite": 0,
|
| 161 |
+
"totalTokens": 791,
|
| 162 |
+
"cost": {
|
| 163 |
+
"input": 0,
|
| 164 |
+
"output": 0,
|
| 165 |
+
"cacheRead": 0,
|
| 166 |
+
"cacheWrite": 0,
|
| 167 |
+
"total": 0
|
| 168 |
+
}
|
| 169 |
+
}
|
| 170 |
+
}
|
| 171 |
+
]
|
| 172 |
+
},
|
| 173 |
+
{
|
| 174 |
+
"name": "analyze-and-write",
|
| 175 |
+
"elapsedMs": 8251,
|
| 176 |
+
"version": 1,
|
| 177 |
+
"files": {
|
| 178 |
+
"/tmp/revenue.txt": "Notebook 12 5 60\nPencil 30 2 60\nFolder 8 3 24\nTOTAL: 144\n",
|
| 179 |
+
"/workspace/README.md": "# Browser workspace\n\nPi can read, write, edit, and run shell commands here.\nEverything stays in this browser. Try analyzing sales.csv.\n",
|
| 180 |
+
"/workspace/report.md": "# Sales Report\n\nTotal revenue from sales.csv is **$144**.\n\n- Notebook: 12 Γ $5 = $60\n- Pencil: 30 Γ $2 = $60\n- Folder: 8 Γ $3 = $24\n",
|
| 181 |
+
"/workspace/revenue.txt": "Notebook 12 5 -> 12 x 5 = 60\nPencil 30 2 -> 30 x 2 = 60\nFolder 8 3 -> 8 x 3 = 24\nTOTAL REVENUE: 144\n",
|
| 182 |
+
"/workspace/sales.csv": "product,units,price\nNotebook,12,5\nPencil,30,2\nFolder,8,3\n",
|
| 183 |
+
"/workspace/secret.txt": "LOCAL-fd008be1"
|
| 184 |
+
},
|
| 185 |
+
"entries": [
|
| 186 |
+
[
|
| 187 |
+
"/",
|
| 188 |
+
{
|
| 189 |
+
"type": "directory",
|
| 190 |
+
"mode": 493
|
| 191 |
+
}
|
| 192 |
+
],
|
| 193 |
+
[
|
| 194 |
+
"/tmp",
|
| 195 |
+
{
|
| 196 |
+
"type": "directory",
|
| 197 |
+
"mode": 493
|
| 198 |
+
}
|
| 199 |
+
],
|
| 200 |
+
[
|
| 201 |
+
"/tmp/revenue.txt",
|
| 202 |
+
{
|
| 203 |
+
"type": "file",
|
| 204 |
+
"base64": "Tm90ZWJvb2sgMTIgNSA2MApQZW5jaWwgMzAgMiA2MApGb2xkZXIgOCAzIDI0ClRPVEFMOiAxNDQK",
|
| 205 |
+
"mode": 420
|
| 206 |
+
}
|
| 207 |
+
],
|
| 208 |
+
[
|
| 209 |
+
"/usr",
|
| 210 |
+
{
|
| 211 |
+
"type": "directory",
|
| 212 |
+
"mode": 493
|
| 213 |
+
}
|
| 214 |
+
],
|
| 215 |
+
[
|
| 216 |
+
"/workspace",
|
| 217 |
+
{
|
| 218 |
+
"type": "directory",
|
| 219 |
+
"mode": 493
|
| 220 |
+
}
|
| 221 |
+
],
|
| 222 |
+
[
|
| 223 |
+
"/workspace/README.md",
|
| 224 |
+
{
|
| 225 |
+
"type": "file",
|
| 226 |
+
"base64": "IyBCcm93c2VyIHdvcmtzcGFjZQoKUGkgY2FuIHJlYWQsIHdyaXRlLCBlZGl0LCBhbmQgcnVuIHNoZWxsIGNvbW1hbmRzIGhlcmUuCkV2ZXJ5dGhpbmcgc3RheXMgaW4gdGhpcyBicm93c2VyLiBUcnkgYW5hbHl6aW5nIHNhbGVzLmNzdi4K",
|
| 227 |
+
"mode": 420
|
| 228 |
+
}
|
| 229 |
+
],
|
| 230 |
+
[
|
| 231 |
+
"/workspace/report.md",
|
| 232 |
+
{
|
| 233 |
+
"type": "file",
|
| 234 |
+
"base64": "IyBTYWxlcyBSZXBvcnQKClRvdGFsIHJldmVudWUgZnJvbSBzYWxlcy5jc3YgaXMgKiokMTQ0KiouCgotIE5vdGVib29rOiAxMiDDlyAkNSA9ICQ2MAotIFBlbmNpbDogMzAgw5cgJDIgPSAkNjAKLSBGb2xkZXI6IDggw5cgJDMgPSAkMjQK",
|
| 235 |
+
"mode": 420
|
| 236 |
+
}
|
| 237 |
+
],
|
| 238 |
+
[
|
| 239 |
+
"/workspace/revenue.txt",
|
| 240 |
+
{
|
| 241 |
+
"type": "file",
|
| 242 |
+
"base64": "Tm90ZWJvb2sgMTIgNSAtPiAxMiB4IDUgPSA2MApQZW5jaWwgMzAgMiAtPiAzMCB4IDIgPSA2MApGb2xkZXIgOCAzIC0+IDggeCAzID0gMjQKVE9UQUwgUkVWRU5VRTogMTQ0Cg==",
|
| 243 |
+
"mode": 420
|
| 244 |
+
}
|
| 245 |
+
],
|
| 246 |
+
[
|
| 247 |
+
"/workspace/sales.csv",
|
| 248 |
+
{
|
| 249 |
+
"type": "file",
|
| 250 |
+
"base64": "cHJvZHVjdCx1bml0cyxwcmljZQpOb3RlYm9vaywxMiw1ClBlbmNpbCwzMCwyCkZvbGRlciw4LDMK",
|
| 251 |
+
"mode": 420
|
| 252 |
+
}
|
| 253 |
+
],
|
| 254 |
+
[
|
| 255 |
+
"/workspace/secret.txt",
|
| 256 |
+
{
|
| 257 |
+
"type": "file",
|
| 258 |
+
"base64": "TE9DQUwtZmQwMDhiZTE=",
|
| 259 |
+
"mode": 420
|
| 260 |
+
}
|
| 261 |
+
]
|
| 262 |
+
],
|
| 263 |
+
"messages": [
|
| 264 |
+
{
|
| 265 |
+
"role": "user",
|
| 266 |
+
"content": [
|
| 267 |
+
{
|
| 268 |
+
"type": "text",
|
| 269 |
+
"text": "Read sales.csv, calculate total revenue using bash, and save a short report to report.md. Verify the file."
|
| 270 |
+
}
|
| 271 |
+
],
|
| 272 |
+
"timestamp": 1789201322878
|
| 273 |
+
},
|
| 274 |
+
{
|
| 275 |
+
"role": "assistant",
|
| 276 |
+
"api": "minicpm-webgpu",
|
| 277 |
+
"provider": "browser",
|
| 278 |
+
"model": "Mike0021/MiniCPM5-2B-ONNX",
|
| 279 |
+
"timestamp": 1789201322878,
|
| 280 |
+
"content": [
|
| 281 |
+
{
|
| 282 |
+
"type": "toolCall",
|
| 283 |
+
"id": "d609cb7d-ae48-45c5-8042-95e3aac1e0ca",
|
| 284 |
+
"name": "read",
|
| 285 |
+
"arguments": {
|
| 286 |
+
"path": "/workspace/sales.csv"
|
| 287 |
+
}
|
| 288 |
+
}
|
| 289 |
+
],
|
| 290 |
+
"stopReason": "toolUse",
|
| 291 |
+
"usage": {
|
| 292 |
+
"input": 726,
|
| 293 |
+
"output": 18,
|
| 294 |
+
"cacheRead": 0,
|
| 295 |
+
"cacheWrite": 0,
|
| 296 |
+
"totalTokens": 744,
|
| 297 |
+
"cost": {
|
| 298 |
+
"input": 0,
|
| 299 |
+
"output": 0,
|
| 300 |
+
"cacheRead": 0,
|
| 301 |
+
"cacheWrite": 0,
|
| 302 |
+
"total": 0
|
| 303 |
+
}
|
| 304 |
+
}
|
| 305 |
+
},
|
| 306 |
+
{
|
| 307 |
+
"role": "toolResult",
|
| 308 |
+
"toolCallId": "d609cb7d-ae48-45c5-8042-95e3aac1e0ca",
|
| 309 |
+
"toolName": "read",
|
| 310 |
+
"content": [
|
| 311 |
+
{
|
| 312 |
+
"type": "text",
|
| 313 |
+
"text": "product,units,price\nNotebook,12,5\nPencil,30,2\nFolder,8,3\n"
|
| 314 |
+
}
|
| 315 |
+
],
|
| 316 |
+
"details": {},
|
| 317 |
+
"isError": false,
|
| 318 |
+
"timestamp": 1789201323848
|
| 319 |
+
},
|
| 320 |
+
{
|
| 321 |
+
"role": "assistant",
|
| 322 |
+
"api": "minicpm-webgpu",
|
| 323 |
+
"provider": "browser",
|
| 324 |
+
"model": "Mike0021/MiniCPM5-2B-ONNX",
|
| 325 |
+
"timestamp": 1789201323848,
|
| 326 |
+
"content": [
|
| 327 |
+
{
|
| 328 |
+
"type": "toolCall",
|
| 329 |
+
"id": "24c2dfa0-6414-418a-8c15-c1f246eabd3e",
|
| 330 |
+
"name": "bash",
|
| 331 |
+
"arguments": {
|
| 332 |
+
"command": "awk -F',' 'NR>1 {rev=$2*$3; total+=rev; print $1,$2,$3,\"->\",$2,\"x\",$3,\"=\",rev} END {print \"TOTAL REVENUE:\", total}' sales.csv > revenue.txt && cat revenue.txt"
|
| 333 |
+
}
|
| 334 |
+
}
|
| 335 |
+
],
|
| 336 |
+
"stopReason": "toolUse",
|
| 337 |
+
"usage": {
|
| 338 |
+
"input": 798,
|
| 339 |
+
"output": 78,
|
| 340 |
+
"cacheRead": 0,
|
| 341 |
+
"cacheWrite": 0,
|
| 342 |
+
"totalTokens": 876,
|
| 343 |
+
"cost": {
|
| 344 |
+
"input": 0,
|
| 345 |
+
"output": 0,
|
| 346 |
+
"cacheRead": 0,
|
| 347 |
+
"cacheWrite": 0,
|
| 348 |
+
"total": 0
|
| 349 |
+
}
|
| 350 |
+
}
|
| 351 |
+
},
|
| 352 |
+
{
|
| 353 |
+
"role": "toolResult",
|
| 354 |
+
"toolCallId": "24c2dfa0-6414-418a-8c15-c1f246eabd3e",
|
| 355 |
+
"toolName": "bash",
|
| 356 |
+
"content": [
|
| 357 |
+
{
|
| 358 |
+
"type": "text",
|
| 359 |
+
"text": "Notebook 12 5 -> 12 x 5 = 60\nPencil 30 2 -> 30 x 2 = 60\nFolder 8 3 -> 8 x 3 = 24\nTOTAL REVENUE: 144\n\nExit code: 0"
|
| 360 |
+
}
|
| 361 |
+
],
|
| 362 |
+
"details": {
|
| 363 |
+
"exitCode": 0
|
| 364 |
+
},
|
| 365 |
+
"isError": false,
|
| 366 |
+
"timestamp": 1789201325902
|
| 367 |
+
},
|
| 368 |
+
{
|
| 369 |
+
"role": "assistant",
|
| 370 |
+
"api": "minicpm-webgpu",
|
| 371 |
+
"provider": "browser",
|
| 372 |
+
"model": "Mike0021/MiniCPM5-2B-ONNX",
|
| 373 |
+
"timestamp": 1789201325902,
|
| 374 |
+
"content": [
|
| 375 |
+
{
|
| 376 |
+
"type": "toolCall",
|
| 377 |
+
"id": "28f41bf2-1b3f-4d1d-85cc-d3e26f230be8",
|
| 378 |
+
"name": "write",
|
| 379 |
+
"arguments": {
|
| 380 |
+
"path": "report.md",
|
| 381 |
+
"content": "# Sales Report\n\nTotal revenue from sales.csv is **$144**.\n\n- Notebook: 12 Γ $5 = $60\n- Pencil: 30 Γ $2 = $60\n- Folder: 8 Γ $3 = $24\n"
|
| 382 |
+
}
|
| 383 |
+
}
|
| 384 |
+
],
|
| 385 |
+
"stopReason": "toolUse",
|
| 386 |
+
"usage": {
|
| 387 |
+
"input": 964,
|
| 388 |
+
"output": 79,
|
| 389 |
+
"cacheRead": 0,
|
| 390 |
+
"cacheWrite": 0,
|
| 391 |
+
"totalTokens": 1043,
|
| 392 |
+
"cost": {
|
| 393 |
+
"input": 0,
|
| 394 |
+
"output": 0,
|
| 395 |
+
"cacheRead": 0,
|
| 396 |
+
"cacheWrite": 0,
|
| 397 |
+
"total": 0
|
| 398 |
+
}
|
| 399 |
+
}
|
| 400 |
+
},
|
| 401 |
+
{
|
| 402 |
+
"role": "toolResult",
|
| 403 |
+
"toolCallId": "28f41bf2-1b3f-4d1d-85cc-d3e26f230be8",
|
| 404 |
+
"toolName": "write",
|
| 405 |
+
"content": [
|
| 406 |
+
{
|
| 407 |
+
"type": "text",
|
| 408 |
+
"text": "Wrote /workspace/report.md"
|
| 409 |
+
}
|
| 410 |
+
],
|
| 411 |
+
"details": {},
|
| 412 |
+
"isError": false,
|
| 413 |
+
"timestamp": 1789201328112
|
| 414 |
+
},
|
| 415 |
+
{
|
| 416 |
+
"role": "assistant",
|
| 417 |
+
"api": "minicpm-webgpu",
|
| 418 |
+
"provider": "browser",
|
| 419 |
+
"model": "Mike0021/MiniCPM5-2B-ONNX",
|
| 420 |
+
"timestamp": 1789201328112,
|
| 421 |
+
"content": [
|
| 422 |
+
{
|
| 423 |
+
"type": "toolCall",
|
| 424 |
+
"id": "92952bd2-6deb-45ef-b1d1-ee04a554d595",
|
| 425 |
+
"name": "read",
|
| 426 |
+
"arguments": {
|
| 427 |
+
"path": "/workspace/report.md"
|
| 428 |
+
}
|
| 429 |
+
}
|
| 430 |
+
],
|
| 431 |
+
"stopReason": "toolUse",
|
| 432 |
+
"usage": {
|
| 433 |
+
"input": 1079,
|
| 434 |
+
"output": 18,
|
| 435 |
+
"cacheRead": 0,
|
| 436 |
+
"cacheWrite": 0,
|
| 437 |
+
"totalTokens": 1097,
|
| 438 |
+
"cost": {
|
| 439 |
+
"input": 0,
|
| 440 |
+
"output": 0,
|
| 441 |
+
"cacheRead": 0,
|
| 442 |
+
"cacheWrite": 0,
|
| 443 |
+
"total": 0
|
| 444 |
+
}
|
| 445 |
+
}
|
| 446 |
+
},
|
| 447 |
+
{
|
| 448 |
+
"role": "toolResult",
|
| 449 |
+
"toolCallId": "92952bd2-6deb-45ef-b1d1-ee04a554d595",
|
| 450 |
+
"toolName": "read",
|
| 451 |
+
"content": [
|
| 452 |
+
{
|
| 453 |
+
"type": "text",
|
| 454 |
+
"text": "# Sales Report\n\nTotal revenue from sales.csv is **$144**.\n\n- Notebook: 12 Γ $5 = $60\n- Pencil: 30 Γ $2 = $60\n- Folder: 8 Γ $3 = $24\n"
|
| 455 |
+
}
|
| 456 |
+
],
|
| 457 |
+
"details": {},
|
| 458 |
+
"isError": false,
|
| 459 |
+
"timestamp": 1789201329332
|
| 460 |
+
},
|
| 461 |
+
{
|
| 462 |
+
"role": "assistant",
|
| 463 |
+
"api": "minicpm-webgpu",
|
| 464 |
+
"provider": "browser",
|
| 465 |
+
"model": "Mike0021/MiniCPM5-2B-ONNX",
|
| 466 |
+
"timestamp": 1789201329332,
|
| 467 |
+
"content": [
|
| 468 |
+
{
|
| 469 |
+
"type": "text",
|
| 470 |
+
"text": "Done. The report is saved at `/workspace/report.md` with total revenue of $144 (Notebook $60, Pencil $60, Folder $24)."
|
| 471 |
+
}
|
| 472 |
+
],
|
| 473 |
+
"stopReason": "stop",
|
| 474 |
+
"usage": {
|
| 475 |
+
"input": 1180,
|
| 476 |
+
"output": 35,
|
| 477 |
+
"cacheRead": 0,
|
| 478 |
+
"cacheWrite": 0,
|
| 479 |
+
"totalTokens": 1215,
|
| 480 |
+
"cost": {
|
| 481 |
+
"input": 0,
|
| 482 |
+
"output": 0,
|
| 483 |
+
"cacheRead": 0,
|
| 484 |
+
"cacheWrite": 0,
|
| 485 |
+
"total": 0
|
| 486 |
+
}
|
| 487 |
+
}
|
| 488 |
+
}
|
| 489 |
+
]
|
| 490 |
+
},
|
| 491 |
+
{
|
| 492 |
+
"name": "write-and-run-script",
|
| 493 |
+
"elapsedMs": 4368,
|
| 494 |
+
"version": 1,
|
| 495 |
+
"files": {
|
| 496 |
+
"/tmp/revenue.txt": "Notebook 12 5 60\nPencil 30 2 60\nFolder 8 3 24\nTOTAL: 144\n",
|
| 497 |
+
"/workspace/README.md": "# Browser workspace\n\nPi can read, write, edit, and run shell commands here.\nEverything stays in this browser. Try analyzing sales.csv.\n",
|
| 498 |
+
"/workspace/hello.sh": "#!/bin/bash\necho \"Hello from the browser\"\n",
|
| 499 |
+
"/workspace/report.md": "# Sales Report\n\nTotal revenue from sales.csv is **$144**.\n\n- Notebook: 12 Γ $5 = $60\n- Pencil: 30 Γ $2 = $60\n- Folder: 8 Γ $3 = $24\n",
|
| 500 |
+
"/workspace/revenue.txt": "Notebook 12 5 -> 12 x 5 = 60\nPencil 30 2 -> 30 x 2 = 60\nFolder 8 3 -> 8 x 3 = 24\nTOTAL REVENUE: 144\n",
|
| 501 |
+
"/workspace/sales.csv": "product,units,price\nNotebook,12,5\nPencil,30,2\nFolder,8,3\n",
|
| 502 |
+
"/workspace/secret.txt": "LOCAL-fd008be1"
|
| 503 |
+
},
|
| 504 |
+
"entries": [
|
| 505 |
+
[
|
| 506 |
+
"/",
|
| 507 |
+
{
|
| 508 |
+
"type": "directory",
|
| 509 |
+
"mode": 493
|
| 510 |
+
}
|
| 511 |
+
],
|
| 512 |
+
[
|
| 513 |
+
"/tmp",
|
| 514 |
+
{
|
| 515 |
+
"type": "directory",
|
| 516 |
+
"mode": 493
|
| 517 |
+
}
|
| 518 |
+
],
|
| 519 |
+
[
|
| 520 |
+
"/tmp/revenue.txt",
|
| 521 |
+
{
|
| 522 |
+
"type": "file",
|
| 523 |
+
"base64": "Tm90ZWJvb2sgMTIgNSA2MApQZW5jaWwgMzAgMiA2MApGb2xkZXIgOCAzIDI0ClRPVEFMOiAxNDQK",
|
| 524 |
+
"mode": 420
|
| 525 |
+
}
|
| 526 |
+
],
|
| 527 |
+
[
|
| 528 |
+
"/usr",
|
| 529 |
+
{
|
| 530 |
+
"type": "directory",
|
| 531 |
+
"mode": 493
|
| 532 |
+
}
|
| 533 |
+
],
|
| 534 |
+
[
|
| 535 |
+
"/workspace",
|
| 536 |
+
{
|
| 537 |
+
"type": "directory",
|
| 538 |
+
"mode": 493
|
| 539 |
+
}
|
| 540 |
+
],
|
| 541 |
+
[
|
| 542 |
+
"/workspace/README.md",
|
| 543 |
+
{
|
| 544 |
+
"type": "file",
|
| 545 |
+
"base64": "IyBCcm93c2VyIHdvcmtzcGFjZQoKUGkgY2FuIHJlYWQsIHdyaXRlLCBlZGl0LCBhbmQgcnVuIHNoZWxsIGNvbW1hbmRzIGhlcmUuCkV2ZXJ5dGhpbmcgc3RheXMgaW4gdGhpcyBicm93c2VyLiBUcnkgYW5hbHl6aW5nIHNhbGVzLmNzdi4K",
|
| 546 |
+
"mode": 420
|
| 547 |
+
}
|
| 548 |
+
],
|
| 549 |
+
[
|
| 550 |
+
"/workspace/hello.sh",
|
| 551 |
+
{
|
| 552 |
+
"type": "file",
|
| 553 |
+
"base64": "IyEvYmluL2Jhc2gKZWNobyAiSGVsbG8gZnJvbSB0aGUgYnJvd3NlciIK",
|
| 554 |
+
"mode": 493
|
| 555 |
+
}
|
| 556 |
+
],
|
| 557 |
+
[
|
| 558 |
+
"/workspace/report.md",
|
| 559 |
+
{
|
| 560 |
+
"type": "file",
|
| 561 |
+
"base64": "IyBTYWxlcyBSZXBvcnQKClRvdGFsIHJldmVudWUgZnJvbSBzYWxlcy5jc3YgaXMgKiokMTQ0KiouCgotIE5vdGVib29rOiAxMiDDlyAkNSA9ICQ2MAotIFBlbmNpbDogMzAgw5cgJDIgPSAkNjAKLSBGb2xkZXI6IDggw5cgJDMgPSAkMjQK",
|
| 562 |
+
"mode": 420
|
| 563 |
+
}
|
| 564 |
+
],
|
| 565 |
+
[
|
| 566 |
+
"/workspace/revenue.txt",
|
| 567 |
+
{
|
| 568 |
+
"type": "file",
|
| 569 |
+
"base64": "Tm90ZWJvb2sgMTIgNSAtPiAxMiB4IDUgPSA2MApQZW5jaWwgMzAgMiAtPiAzMCB4IDIgPSA2MApGb2xkZXIgOCAzIC0+IDggeCAzID0gMjQKVE9UQUwgUkVWRU5VRTogMTQ0Cg==",
|
| 570 |
+
"mode": 420
|
| 571 |
+
}
|
| 572 |
+
],
|
| 573 |
+
[
|
| 574 |
+
"/workspace/sales.csv",
|
| 575 |
+
{
|
| 576 |
+
"type": "file",
|
| 577 |
+
"base64": "cHJvZHVjdCx1bml0cyxwcmljZQpOb3RlYm9vaywxMiw1ClBlbmNpbCwzMCwyCkZvbGRlciw4LDMK",
|
| 578 |
+
"mode": 420
|
| 579 |
+
}
|
| 580 |
+
],
|
| 581 |
+
[
|
| 582 |
+
"/workspace/secret.txt",
|
| 583 |
+
{
|
| 584 |
+
"type": "file",
|
| 585 |
+
"base64": "TE9DQUwtZmQwMDhiZTE=",
|
| 586 |
+
"mode": 420
|
| 587 |
+
}
|
| 588 |
+
]
|
| 589 |
+
],
|
| 590 |
+
"messages": [
|
| 591 |
+
{
|
| 592 |
+
"role": "user",
|
| 593 |
+
"content": [
|
| 594 |
+
{
|
| 595 |
+
"type": "text",
|
| 596 |
+
"text": "Create a bash script called hello.sh that prints Hello from the browser, run it, and report the output."
|
| 597 |
+
}
|
| 598 |
+
],
|
| 599 |
+
"timestamp": 1789201331178
|
| 600 |
+
},
|
| 601 |
+
{
|
| 602 |
+
"role": "assistant",
|
| 603 |
+
"api": "minicpm-webgpu",
|
| 604 |
+
"provider": "browser",
|
| 605 |
+
"model": "Mike0021/MiniCPM5-2B-ONNX",
|
| 606 |
+
"timestamp": 1789201331178,
|
| 607 |
+
"content": [
|
| 608 |
+
{
|
| 609 |
+
"type": "toolCall",
|
| 610 |
+
"id": "b5138935-5cd3-40e5-8021-af9f2b79ca14",
|
| 611 |
+
"name": "write",
|
| 612 |
+
"arguments": {
|
| 613 |
+
"path": "/workspace/hello.sh",
|
| 614 |
+
"content": "#!/bin/bash\necho \"Hello from the browser\"\n"
|
| 615 |
+
}
|
| 616 |
+
}
|
| 617 |
+
],
|
| 618 |
+
"stopReason": "toolUse",
|
| 619 |
+
"usage": {
|
| 620 |
+
"input": 725,
|
| 621 |
+
"output": 43,
|
| 622 |
+
"cacheRead": 0,
|
| 623 |
+
"cacheWrite": 0,
|
| 624 |
+
"totalTokens": 768,
|
| 625 |
+
"cost": {
|
| 626 |
+
"input": 0,
|
| 627 |
+
"output": 0,
|
| 628 |
+
"cacheRead": 0,
|
| 629 |
+
"cacheWrite": 0,
|
| 630 |
+
"total": 0
|
| 631 |
+
}
|
| 632 |
+
}
|
| 633 |
+
},
|
| 634 |
+
{
|
| 635 |
+
"role": "toolResult",
|
| 636 |
+
"toolCallId": "b5138935-5cd3-40e5-8021-af9f2b79ca14",
|
| 637 |
+
"toolName": "write",
|
| 638 |
+
"content": [
|
| 639 |
+
{
|
| 640 |
+
"type": "text",
|
| 641 |
+
"text": "Wrote /workspace/hello.sh"
|
| 642 |
+
}
|
| 643 |
+
],
|
| 644 |
+
"details": {},
|
| 645 |
+
"isError": false,
|
| 646 |
+
"timestamp": 1789201332541
|
| 647 |
+
},
|
| 648 |
+
{
|
| 649 |
+
"role": "assistant",
|
| 650 |
+
"api": "minicpm-webgpu",
|
| 651 |
+
"provider": "browser",
|
| 652 |
+
"model": "Mike0021/MiniCPM5-2B-ONNX",
|
| 653 |
+
"timestamp": 1789201332541,
|
| 654 |
+
"content": [
|
| 655 |
+
{
|
| 656 |
+
"type": "toolCall",
|
| 657 |
+
"id": "26cda3ca-c25b-436b-bf85-33d3dd3eac83",
|
| 658 |
+
"name": "bash",
|
| 659 |
+
"arguments": {
|
| 660 |
+
"command": "chmod +x /workspace/hello.sh && /workspace/hello.sh"
|
| 661 |
+
}
|
| 662 |
+
}
|
| 663 |
+
],
|
| 664 |
+
"stopReason": "toolUse",
|
| 665 |
+
"usage": {
|
| 666 |
+
"input": 804,
|
| 667 |
+
"output": 34,
|
| 668 |
+
"cacheRead": 0,
|
| 669 |
+
"cacheWrite": 0,
|
| 670 |
+
"totalTokens": 838,
|
| 671 |
+
"cost": {
|
| 672 |
+
"input": 0,
|
| 673 |
+
"output": 0,
|
| 674 |
+
"cacheRead": 0,
|
| 675 |
+
"cacheWrite": 0,
|
| 676 |
+
"total": 0
|
| 677 |
+
}
|
| 678 |
+
}
|
| 679 |
+
},
|
| 680 |
+
{
|
| 681 |
+
"role": "toolResult",
|
| 682 |
+
"toolCallId": "26cda3ca-c25b-436b-bf85-33d3dd3eac83",
|
| 683 |
+
"toolName": "bash",
|
| 684 |
+
"content": [
|
| 685 |
+
{
|
| 686 |
+
"type": "text",
|
| 687 |
+
"text": "Hello from the browser\n\nExit code: 0"
|
| 688 |
+
}
|
| 689 |
+
],
|
| 690 |
+
"details": {
|
| 691 |
+
"exitCode": 0
|
| 692 |
+
},
|
| 693 |
+
"isError": false,
|
| 694 |
+
"timestamp": 1789201333991
|
| 695 |
+
},
|
| 696 |
+
{
|
| 697 |
+
"role": "assistant",
|
| 698 |
+
"api": "minicpm-webgpu",
|
| 699 |
+
"provider": "browser",
|
| 700 |
+
"model": "Mike0021/MiniCPM5-2B-ONNX",
|
| 701 |
+
"timestamp": 1789201333991,
|
| 702 |
+
"content": [
|
| 703 |
+
{
|
| 704 |
+
"type": "text",
|
| 705 |
+
"text": "The script `/workspace/hello.sh` prints \"Hello from the browser\". Running it outputs:\n\n```\nHello from the browser\n```\n\nwith exit code 0."
|
| 706 |
+
}
|
| 707 |
+
],
|
| 708 |
+
"stopReason": "stop",
|
| 709 |
+
"usage": {
|
| 710 |
+
"input": 878,
|
| 711 |
+
"output": 35,
|
| 712 |
+
"cacheRead": 0,
|
| 713 |
+
"cacheWrite": 0,
|
| 714 |
+
"totalTokens": 913,
|
| 715 |
+
"cost": {
|
| 716 |
+
"input": 0,
|
| 717 |
+
"output": 0,
|
| 718 |
+
"cacheRead": 0,
|
| 719 |
+
"cacheWrite": 0,
|
| 720 |
+
"total": 0
|
| 721 |
+
}
|
| 722 |
+
}
|
| 723 |
+
}
|
| 724 |
+
]
|
| 725 |
+
},
|
| 726 |
+
{
|
| 727 |
+
"name": "edit-and-verify",
|
| 728 |
+
"elapsedMs": 4833,
|
| 729 |
+
"version": 1,
|
| 730 |
+
"files": {
|
| 731 |
+
"/tmp/revenue.txt": "Notebook 12 5 60\nPencil 30 2 60\nFolder 8 3 24\nTOTAL: 144\n",
|
| 732 |
+
"/workspace/README.md": "# Browser workspace\n\nPi can read, write, edit, and run shell commands here.\nEverything stays in this browser. Try analyzing sales.csv.\n",
|
| 733 |
+
"/workspace/hello.sh": "#!/bin/bash\necho \"Hello from Pi\"\n",
|
| 734 |
+
"/workspace/report.md": "# Sales Report\n\nTotal revenue from sales.csv is **$144**.\n\n- Notebook: 12 Γ $5 = $60\n- Pencil: 30 Γ $2 = $60\n- Folder: 8 Γ $3 = $24\n",
|
| 735 |
+
"/workspace/revenue.txt": "Notebook 12 5 -> 12 x 5 = 60\nPencil 30 2 -> 30 x 2 = 60\nFolder 8 3 -> 8 x 3 = 24\nTOTAL REVENUE: 144\n",
|
| 736 |
+
"/workspace/sales.csv": "product,units,price\nNotebook,12,5\nPencil,30,2\nFolder,8,3\n",
|
| 737 |
+
"/workspace/secret.txt": "LOCAL-fd008be1"
|
| 738 |
+
},
|
| 739 |
+
"entries": [
|
| 740 |
+
[
|
| 741 |
+
"/",
|
| 742 |
+
{
|
| 743 |
+
"type": "directory",
|
| 744 |
+
"mode": 493
|
| 745 |
+
}
|
| 746 |
+
],
|
| 747 |
+
[
|
| 748 |
+
"/tmp",
|
| 749 |
+
{
|
| 750 |
+
"type": "directory",
|
| 751 |
+
"mode": 493
|
| 752 |
+
}
|
| 753 |
+
],
|
| 754 |
+
[
|
| 755 |
+
"/tmp/revenue.txt",
|
| 756 |
+
{
|
| 757 |
+
"type": "file",
|
| 758 |
+
"base64": "Tm90ZWJvb2sgMTIgNSA2MApQZW5jaWwgMzAgMiA2MApGb2xkZXIgOCAzIDI0ClRPVEFMOiAxNDQK",
|
| 759 |
+
"mode": 420
|
| 760 |
+
}
|
| 761 |
+
],
|
| 762 |
+
[
|
| 763 |
+
"/usr",
|
| 764 |
+
{
|
| 765 |
+
"type": "directory",
|
| 766 |
+
"mode": 493
|
| 767 |
+
}
|
| 768 |
+
],
|
| 769 |
+
[
|
| 770 |
+
"/workspace",
|
| 771 |
+
{
|
| 772 |
+
"type": "directory",
|
| 773 |
+
"mode": 493
|
| 774 |
+
}
|
| 775 |
+
],
|
| 776 |
+
[
|
| 777 |
+
"/workspace/README.md",
|
| 778 |
+
{
|
| 779 |
+
"type": "file",
|
| 780 |
+
"base64": "IyBCcm93c2VyIHdvcmtzcGFjZQoKUGkgY2FuIHJlYWQsIHdyaXRlLCBlZGl0LCBhbmQgcnVuIHNoZWxsIGNvbW1hbmRzIGhlcmUuCkV2ZXJ5dGhpbmcgc3RheXMgaW4gdGhpcyBicm93c2VyLiBUcnkgYW5hbHl6aW5nIHNhbGVzLmNzdi4K",
|
| 781 |
+
"mode": 420
|
| 782 |
+
}
|
| 783 |
+
],
|
| 784 |
+
[
|
| 785 |
+
"/workspace/hello.sh",
|
| 786 |
+
{
|
| 787 |
+
"type": "file",
|
| 788 |
+
"base64": "IyEvYmluL2Jhc2gKZWNobyAiSGVsbG8gZnJvbSBQaSIK",
|
| 789 |
+
"mode": 493
|
| 790 |
+
}
|
| 791 |
+
],
|
| 792 |
+
[
|
| 793 |
+
"/workspace/report.md",
|
| 794 |
+
{
|
| 795 |
+
"type": "file",
|
| 796 |
+
"base64": "IyBTYWxlcyBSZXBvcnQKClRvdGFsIHJldmVudWUgZnJvbSBzYWxlcy5jc3YgaXMgKiokMTQ0KiouCgotIE5vdGVib29rOiAxMiDDlyAkNSA9ICQ2MAotIFBlbmNpbDogMzAgw5cgJDIgPSAkNjAKLSBGb2xkZXI6IDggw5cgJDMgPSAkMjQK",
|
| 797 |
+
"mode": 420
|
| 798 |
+
}
|
| 799 |
+
],
|
| 800 |
+
[
|
| 801 |
+
"/workspace/revenue.txt",
|
| 802 |
+
{
|
| 803 |
+
"type": "file",
|
| 804 |
+
"base64": "Tm90ZWJvb2sgMTIgNSAtPiAxMiB4IDUgPSA2MApQZW5jaWwgMzAgMiAtPiAzMCB4IDIgPSA2MApGb2xkZXIgOCAzIC0+IDggeCAzID0gMjQKVE9UQUwgUkVWRU5VRTogMTQ0Cg==",
|
| 805 |
+
"mode": 420
|
| 806 |
+
}
|
| 807 |
+
],
|
| 808 |
+
[
|
| 809 |
+
"/workspace/sales.csv",
|
| 810 |
+
{
|
| 811 |
+
"type": "file",
|
| 812 |
+
"base64": "cHJvZHVjdCx1bml0cyxwcmljZQpOb3RlYm9vaywxMiw1ClBlbmNpbCwzMCwyCkZvbGRlciw4LDMK",
|
| 813 |
+
"mode": 420
|
| 814 |
+
}
|
| 815 |
+
],
|
| 816 |
+
[
|
| 817 |
+
"/workspace/secret.txt",
|
| 818 |
+
{
|
| 819 |
+
"type": "file",
|
| 820 |
+
"base64": "TE9DQUwtZmQwMDhiZTE=",
|
| 821 |
+
"mode": 420
|
| 822 |
+
}
|
| 823 |
+
]
|
| 824 |
+
],
|
| 825 |
+
"messages": [
|
| 826 |
+
{
|
| 827 |
+
"role": "user",
|
| 828 |
+
"content": [
|
| 829 |
+
{
|
| 830 |
+
"type": "text",
|
| 831 |
+
"text": "Read hello.sh, use the edit tool to replace Hello from the browser with Hello from Pi, then run the script and report the output."
|
| 832 |
+
}
|
| 833 |
+
],
|
| 834 |
+
"timestamp": 1789201335595
|
| 835 |
+
},
|
| 836 |
+
{
|
| 837 |
+
"role": "assistant",
|
| 838 |
+
"api": "minicpm-webgpu",
|
| 839 |
+
"provider": "browser",
|
| 840 |
+
"model": "Mike0021/MiniCPM5-2B-ONNX",
|
| 841 |
+
"timestamp": 1789201335596,
|
| 842 |
+
"content": [
|
| 843 |
+
{
|
| 844 |
+
"type": "toolCall",
|
| 845 |
+
"id": "4cf27702-1db6-4297-8dcd-1a6b28cba1cb",
|
| 846 |
+
"name": "read",
|
| 847 |
+
"arguments": {
|
| 848 |
+
"path": "hello.sh"
|
| 849 |
+
}
|
| 850 |
+
}
|
| 851 |
+
],
|
| 852 |
+
"stopReason": "toolUse",
|
| 853 |
+
"usage": {
|
| 854 |
+
"input": 731,
|
| 855 |
+
"output": 15,
|
| 856 |
+
"cacheRead": 0,
|
| 857 |
+
"cacheWrite": 0,
|
| 858 |
+
"totalTokens": 746,
|
| 859 |
+
"cost": {
|
| 860 |
+
"input": 0,
|
| 861 |
+
"output": 0,
|
| 862 |
+
"cacheRead": 0,
|
| 863 |
+
"cacheWrite": 0,
|
| 864 |
+
"total": 0
|
| 865 |
+
}
|
| 866 |
+
}
|
| 867 |
+
},
|
| 868 |
+
{
|
| 869 |
+
"role": "toolResult",
|
| 870 |
+
"toolCallId": "4cf27702-1db6-4297-8dcd-1a6b28cba1cb",
|
| 871 |
+
"toolName": "read",
|
| 872 |
+
"content": [
|
| 873 |
+
{
|
| 874 |
+
"type": "text",
|
| 875 |
+
"text": "#!/bin/bash\necho \"Hello from the browser\"\n"
|
| 876 |
+
}
|
| 877 |
+
],
|
| 878 |
+
"details": {},
|
| 879 |
+
"isError": false,
|
| 880 |
+
"timestamp": 1789201336493
|
| 881 |
+
},
|
| 882 |
+
{
|
| 883 |
+
"role": "assistant",
|
| 884 |
+
"api": "minicpm-webgpu",
|
| 885 |
+
"provider": "browser",
|
| 886 |
+
"model": "Mike0021/MiniCPM5-2B-ONNX",
|
| 887 |
+
"timestamp": 1789201336493,
|
| 888 |
+
"content": [
|
| 889 |
+
{
|
| 890 |
+
"type": "toolCall",
|
| 891 |
+
"id": "57259aeb-a574-4271-bebc-0693a9eac75f",
|
| 892 |
+
"name": "edit",
|
| 893 |
+
"arguments": {
|
| 894 |
+
"path": "hello.sh",
|
| 895 |
+
"oldText": "echo \"Hello from the browser\"",
|
| 896 |
+
"newText": "echo \"Hello from Pi\""
|
| 897 |
+
}
|
| 898 |
+
}
|
| 899 |
+
],
|
| 900 |
+
"stopReason": "toolUse",
|
| 901 |
+
"usage": {
|
| 902 |
+
"input": 790,
|
| 903 |
+
"output": 42,
|
| 904 |
+
"cacheRead": 0,
|
| 905 |
+
"cacheWrite": 0,
|
| 906 |
+
"totalTokens": 832,
|
| 907 |
+
"cost": {
|
| 908 |
+
"input": 0,
|
| 909 |
+
"output": 0,
|
| 910 |
+
"cacheRead": 0,
|
| 911 |
+
"cacheWrite": 0,
|
| 912 |
+
"total": 0
|
| 913 |
+
}
|
| 914 |
+
}
|
| 915 |
+
},
|
| 916 |
+
{
|
| 917 |
+
"role": "toolResult",
|
| 918 |
+
"toolCallId": "57259aeb-a574-4271-bebc-0693a9eac75f",
|
| 919 |
+
"toolName": "edit",
|
| 920 |
+
"content": [
|
| 921 |
+
{
|
| 922 |
+
"type": "text",
|
| 923 |
+
"text": "Edited /workspace/hello.sh"
|
| 924 |
+
}
|
| 925 |
+
],
|
| 926 |
+
"details": {},
|
| 927 |
+
"isError": false,
|
| 928 |
+
"timestamp": 1789201337880
|
| 929 |
+
},
|
| 930 |
+
{
|
| 931 |
+
"role": "assistant",
|
| 932 |
+
"api": "minicpm-webgpu",
|
| 933 |
+
"provider": "browser",
|
| 934 |
+
"model": "Mike0021/MiniCPM5-2B-ONNX",
|
| 935 |
+
"timestamp": 1789201337881,
|
| 936 |
+
"content": [
|
| 937 |
+
{
|
| 938 |
+
"type": "toolCall",
|
| 939 |
+
"id": "5ec18439-e1b8-4db2-bd9e-cde21bb97b91",
|
| 940 |
+
"name": "bash",
|
| 941 |
+
"arguments": {
|
| 942 |
+
"command": "chmod +x hello.sh && ./hello.sh"
|
| 943 |
+
}
|
| 944 |
+
}
|
| 945 |
+
],
|
| 946 |
+
"stopReason": "toolUse",
|
| 947 |
+
"usage": {
|
| 948 |
+
"input": 867,
|
| 949 |
+
"output": 29,
|
| 950 |
+
"cacheRead": 0,
|
| 951 |
+
"cacheWrite": 0,
|
| 952 |
+
"totalTokens": 896,
|
| 953 |
+
"cost": {
|
| 954 |
+
"input": 0,
|
| 955 |
+
"output": 0,
|
| 956 |
+
"cacheRead": 0,
|
| 957 |
+
"cacheWrite": 0,
|
| 958 |
+
"total": 0
|
| 959 |
+
}
|
| 960 |
+
}
|
| 961 |
+
},
|
| 962 |
+
{
|
| 963 |
+
"role": "toolResult",
|
| 964 |
+
"toolCallId": "5ec18439-e1b8-4db2-bd9e-cde21bb97b91",
|
| 965 |
+
"toolName": "bash",
|
| 966 |
+
"content": [
|
| 967 |
+
{
|
| 968 |
+
"type": "text",
|
| 969 |
+
"text": "Hello from Pi\n\nExit code: 0"
|
| 970 |
+
}
|
| 971 |
+
],
|
| 972 |
+
"details": {
|
| 973 |
+
"exitCode": 0
|
| 974 |
+
},
|
| 975 |
+
"isError": false,
|
| 976 |
+
"timestamp": 1789201339204
|
| 977 |
+
},
|
| 978 |
+
{
|
| 979 |
+
"role": "assistant",
|
| 980 |
+
"api": "minicpm-webgpu",
|
| 981 |
+
"provider": "browser",
|
| 982 |
+
"model": "Mike0021/MiniCPM5-2B-ONNX",
|
| 983 |
+
"timestamp": 1789201339204,
|
| 984 |
+
"content": [
|
| 985 |
+
{
|
| 986 |
+
"type": "text",
|
| 987 |
+
"text": "The script now outputs \"Hello from Pi\" when run."
|
| 988 |
+
}
|
| 989 |
+
],
|
| 990 |
+
"stopReason": "stop",
|
| 991 |
+
"usage": {
|
| 992 |
+
"input": 935,
|
| 993 |
+
"output": 13,
|
| 994 |
+
"cacheRead": 0,
|
| 995 |
+
"cacheWrite": 0,
|
| 996 |
+
"totalTokens": 948,
|
| 997 |
+
"cost": {
|
| 998 |
+
"input": 0,
|
| 999 |
+
"output": 0,
|
| 1000 |
+
"cacheRead": 0,
|
| 1001 |
+
"cacheWrite": 0,
|
| 1002 |
+
"total": 0
|
| 1003 |
+
}
|
| 1004 |
+
}
|
| 1005 |
+
}
|
| 1006 |
+
]
|
| 1007 |
+
},
|
| 1008 |
+
{
|
| 1009 |
+
"name": "recover-tool-error",
|
| 1010 |
+
"elapsedMs": 5084,
|
| 1011 |
+
"version": 1,
|
| 1012 |
+
"files": {
|
| 1013 |
+
"/tmp/revenue.txt": "Notebook 12 5 60\nPencil 30 2 60\nFolder 8 3 24\nTOTAL: 144\n",
|
| 1014 |
+
"/workspace/README.md": "# Browser workspace\n\nPi can read, write, edit, and run shell commands here.\nEverything stays in this browser. Try analyzing sales.csv.\n",
|
| 1015 |
+
"/workspace/hello.sh": "#!/bin/bash\necho \"Hello from Pi\"\n",
|
| 1016 |
+
"/workspace/missing.txt": "Recovered",
|
| 1017 |
+
"/workspace/report.md": "# Sales Report\n\nTotal revenue from sales.csv is **$144**.\n\n- Notebook: 12 Γ $5 = $60\n- Pencil: 30 Γ $2 = $60\n- Folder: 8 Γ $3 = $24\n",
|
| 1018 |
+
"/workspace/revenue.txt": "Notebook 12 5 -> 12 x 5 = 60\nPencil 30 2 -> 30 x 2 = 60\nFolder 8 3 -> 8 x 3 = 24\nTOTAL REVENUE: 144\n",
|
| 1019 |
+
"/workspace/sales.csv": "product,units,price\nNotebook,12,5\nPencil,30,2\nFolder,8,3\n",
|
| 1020 |
+
"/workspace/secret.txt": "LOCAL-fd008be1"
|
| 1021 |
+
},
|
| 1022 |
+
"entries": [
|
| 1023 |
+
[
|
| 1024 |
+
"/",
|
| 1025 |
+
{
|
| 1026 |
+
"type": "directory",
|
| 1027 |
+
"mode": 493
|
| 1028 |
+
}
|
| 1029 |
+
],
|
| 1030 |
+
[
|
| 1031 |
+
"/tmp",
|
| 1032 |
+
{
|
| 1033 |
+
"type": "directory",
|
| 1034 |
+
"mode": 493
|
| 1035 |
+
}
|
| 1036 |
+
],
|
| 1037 |
+
[
|
| 1038 |
+
"/tmp/revenue.txt",
|
| 1039 |
+
{
|
| 1040 |
+
"type": "file",
|
| 1041 |
+
"base64": "Tm90ZWJvb2sgMTIgNSA2MApQZW5jaWwgMzAgMiA2MApGb2xkZXIgOCAzIDI0ClRPVEFMOiAxNDQK",
|
| 1042 |
+
"mode": 420
|
| 1043 |
+
}
|
| 1044 |
+
],
|
| 1045 |
+
[
|
| 1046 |
+
"/usr",
|
| 1047 |
+
{
|
| 1048 |
+
"type": "directory",
|
| 1049 |
+
"mode": 493
|
| 1050 |
+
}
|
| 1051 |
+
],
|
| 1052 |
+
[
|
| 1053 |
+
"/workspace",
|
| 1054 |
+
{
|
| 1055 |
+
"type": "directory",
|
| 1056 |
+
"mode": 493
|
| 1057 |
+
}
|
| 1058 |
+
],
|
| 1059 |
+
[
|
| 1060 |
+
"/workspace/README.md",
|
| 1061 |
+
{
|
| 1062 |
+
"type": "file",
|
| 1063 |
+
"base64": "IyBCcm93c2VyIHdvcmtzcGFjZQoKUGkgY2FuIHJlYWQsIHdyaXRlLCBlZGl0LCBhbmQgcnVuIHNoZWxsIGNvbW1hbmRzIGhlcmUuCkV2ZXJ5dGhpbmcgc3RheXMgaW4gdGhpcyBicm93c2VyLiBUcnkgYW5hbHl6aW5nIHNhbGVzLmNzdi4K",
|
| 1064 |
+
"mode": 420
|
| 1065 |
+
}
|
| 1066 |
+
],
|
| 1067 |
+
[
|
| 1068 |
+
"/workspace/hello.sh",
|
| 1069 |
+
{
|
| 1070 |
+
"type": "file",
|
| 1071 |
+
"base64": "IyEvYmluL2Jhc2gKZWNobyAiSGVsbG8gZnJvbSBQaSIK",
|
| 1072 |
+
"mode": 493
|
| 1073 |
+
}
|
| 1074 |
+
],
|
| 1075 |
+
[
|
| 1076 |
+
"/workspace/missing.txt",
|
| 1077 |
+
{
|
| 1078 |
+
"type": "file",
|
| 1079 |
+
"base64": "UmVjb3ZlcmVk",
|
| 1080 |
+
"mode": 420
|
| 1081 |
+
}
|
| 1082 |
+
],
|
| 1083 |
+
[
|
| 1084 |
+
"/workspace/report.md",
|
| 1085 |
+
{
|
| 1086 |
+
"type": "file",
|
| 1087 |
+
"base64": "IyBTYWxlcyBSZXBvcnQKClRvdGFsIHJldmVudWUgZnJvbSBzYWxlcy5jc3YgaXMgKiokMTQ0KiouCgotIE5vdGVib29rOiAxMiDDlyAkNSA9ICQ2MAotIFBlbmNpbDogMzAgw5cgJDIgPSAkNjAKLSBGb2xkZXI6IDggw5cgJDMgPSAkMjQK",
|
| 1088 |
+
"mode": 420
|
| 1089 |
+
}
|
| 1090 |
+
],
|
| 1091 |
+
[
|
| 1092 |
+
"/workspace/revenue.txt",
|
| 1093 |
+
{
|
| 1094 |
+
"type": "file",
|
| 1095 |
+
"base64": "Tm90ZWJvb2sgMTIgNSAtPiAxMiB4IDUgPSA2MApQZW5jaWwgMzAgMiAtPiAzMCB4IDIgPSA2MApGb2xkZXIgOCAzIC0+IDggeCAzID0gMjQKVE9UQUwgUkVWRU5VRTogMTQ0Cg==",
|
| 1096 |
+
"mode": 420
|
| 1097 |
+
}
|
| 1098 |
+
],
|
| 1099 |
+
[
|
| 1100 |
+
"/workspace/sales.csv",
|
| 1101 |
+
{
|
| 1102 |
+
"type": "file",
|
| 1103 |
+
"base64": "cHJvZHVjdCx1bml0cyxwcmljZQpOb3RlYm9vaywxMiw1ClBlbmNpbCwzMCwyCkZvbGRlciw4LDMK",
|
| 1104 |
+
"mode": 420
|
| 1105 |
+
}
|
| 1106 |
+
],
|
| 1107 |
+
[
|
| 1108 |
+
"/workspace/secret.txt",
|
| 1109 |
+
{
|
| 1110 |
+
"type": "file",
|
| 1111 |
+
"base64": "TE9DQUwtZmQwMDhiZTE=",
|
| 1112 |
+
"mode": 420
|
| 1113 |
+
}
|
| 1114 |
+
]
|
| 1115 |
+
],
|
| 1116 |
+
"messages": [
|
| 1117 |
+
{
|
| 1118 |
+
"role": "user",
|
| 1119 |
+
"content": [
|
| 1120 |
+
{
|
| 1121 |
+
"type": "text",
|
| 1122 |
+
"text": "Read missing.txt using the read tool. If it does not exist, create it with the exact text Recovered and verify it by reading the file."
|
| 1123 |
+
}
|
| 1124 |
+
],
|
| 1125 |
+
"timestamp": 1789201340477
|
| 1126 |
+
},
|
| 1127 |
+
{
|
| 1128 |
+
"role": "assistant",
|
| 1129 |
+
"api": "minicpm-webgpu",
|
| 1130 |
+
"provider": "browser",
|
| 1131 |
+
"model": "Mike0021/MiniCPM5-2B-ONNX",
|
| 1132 |
+
"timestamp": 1789201340478,
|
| 1133 |
+
"content": [
|
| 1134 |
+
{
|
| 1135 |
+
"type": "toolCall",
|
| 1136 |
+
"id": "493508b4-a058-4d93-ae05-15a1329a5da9",
|
| 1137 |
+
"name": "read",
|
| 1138 |
+
"arguments": {
|
| 1139 |
+
"path": "/workspace/missing.txt"
|
| 1140 |
+
}
|
| 1141 |
+
}
|
| 1142 |
+
],
|
| 1143 |
+
"stopReason": "toolUse",
|
| 1144 |
+
"usage": {
|
| 1145 |
+
"input": 733,
|
| 1146 |
+
"output": 18,
|
| 1147 |
+
"cacheRead": 0,
|
| 1148 |
+
"cacheWrite": 0,
|
| 1149 |
+
"totalTokens": 751,
|
| 1150 |
+
"cost": {
|
| 1151 |
+
"input": 0,
|
| 1152 |
+
"output": 0,
|
| 1153 |
+
"cacheRead": 0,
|
| 1154 |
+
"cacheWrite": 0,
|
| 1155 |
+
"total": 0
|
| 1156 |
+
}
|
| 1157 |
+
}
|
| 1158 |
+
},
|
| 1159 |
+
{
|
| 1160 |
+
"role": "toolResult",
|
| 1161 |
+
"toolCallId": "493508b4-a058-4d93-ae05-15a1329a5da9",
|
| 1162 |
+
"toolName": "read",
|
| 1163 |
+
"content": [
|
| 1164 |
+
{
|
| 1165 |
+
"type": "text",
|
| 1166 |
+
"text": "ENOENT: no such file or directory, open '/workspace/missing.txt'"
|
| 1167 |
+
}
|
| 1168 |
+
],
|
| 1169 |
+
"details": {},
|
| 1170 |
+
"isError": true,
|
| 1171 |
+
"timestamp": 1789201341439
|
| 1172 |
+
},
|
| 1173 |
+
{
|
| 1174 |
+
"role": "assistant",
|
| 1175 |
+
"api": "minicpm-webgpu",
|
| 1176 |
+
"provider": "browser",
|
| 1177 |
+
"model": "Mike0021/MiniCPM5-2B-ONNX",
|
| 1178 |
+
"timestamp": 1789201341439,
|
| 1179 |
+
"content": [
|
| 1180 |
+
{
|
| 1181 |
+
"type": "toolCall",
|
| 1182 |
+
"id": "7d5678e4-fba5-43f1-bd2b-d770667f6bb1",
|
| 1183 |
+
"name": "write",
|
| 1184 |
+
"arguments": {
|
| 1185 |
+
"path": "/workspace/missing.txt",
|
| 1186 |
+
"content": "Recovered"
|
| 1187 |
+
}
|
| 1188 |
+
}
|
| 1189 |
+
],
|
| 1190 |
+
"stopReason": "toolUse",
|
| 1191 |
+
"usage": {
|
| 1192 |
+
"input": 796,
|
| 1193 |
+
"output": 32,
|
| 1194 |
+
"cacheRead": 0,
|
| 1195 |
+
"cacheWrite": 0,
|
| 1196 |
+
"totalTokens": 828,
|
| 1197 |
+
"cost": {
|
| 1198 |
+
"input": 0,
|
| 1199 |
+
"output": 0,
|
| 1200 |
+
"cacheRead": 0,
|
| 1201 |
+
"cacheWrite": 0,
|
| 1202 |
+
"total": 0
|
| 1203 |
+
}
|
| 1204 |
+
}
|
| 1205 |
+
},
|
| 1206 |
+
{
|
| 1207 |
+
"role": "toolResult",
|
| 1208 |
+
"toolCallId": "7d5678e4-fba5-43f1-bd2b-d770667f6bb1",
|
| 1209 |
+
"toolName": "write",
|
| 1210 |
+
"content": [
|
| 1211 |
+
{
|
| 1212 |
+
"type": "text",
|
| 1213 |
+
"text": "Wrote /workspace/missing.txt"
|
| 1214 |
+
}
|
| 1215 |
+
],
|
| 1216 |
+
"details": {},
|
| 1217 |
+
"isError": false,
|
| 1218 |
+
"timestamp": 1789201342688
|
| 1219 |
+
},
|
| 1220 |
+
{
|
| 1221 |
+
"role": "assistant",
|
| 1222 |
+
"api": "minicpm-webgpu",
|
| 1223 |
+
"provider": "browser",
|
| 1224 |
+
"model": "Mike0021/MiniCPM5-2B-ONNX",
|
| 1225 |
+
"timestamp": 1789201342688,
|
| 1226 |
+
"content": [
|
| 1227 |
+
{
|
| 1228 |
+
"type": "toolCall",
|
| 1229 |
+
"id": "3f6e9269-4018-408d-9055-7e1a8ed81ab7",
|
| 1230 |
+
"name": "read",
|
| 1231 |
+
"arguments": {
|
| 1232 |
+
"path": "/workspace/missing.txt"
|
| 1233 |
+
}
|
| 1234 |
+
}
|
| 1235 |
+
],
|
| 1236 |
+
"stopReason": "toolUse",
|
| 1237 |
+
"usage": {
|
| 1238 |
+
"input": 858,
|
| 1239 |
+
"output": 18,
|
| 1240 |
+
"cacheRead": 0,
|
| 1241 |
+
"cacheWrite": 0,
|
| 1242 |
+
"totalTokens": 876,
|
| 1243 |
+
"cost": {
|
| 1244 |
+
"input": 0,
|
| 1245 |
+
"output": 0,
|
| 1246 |
+
"cacheRead": 0,
|
| 1247 |
+
"cacheWrite": 0,
|
| 1248 |
+
"total": 0
|
| 1249 |
+
}
|
| 1250 |
+
}
|
| 1251 |
+
},
|
| 1252 |
+
{
|
| 1253 |
+
"role": "toolResult",
|
| 1254 |
+
"toolCallId": "3f6e9269-4018-408d-9055-7e1a8ed81ab7",
|
| 1255 |
+
"toolName": "read",
|
| 1256 |
+
"content": [
|
| 1257 |
+
{
|
| 1258 |
+
"type": "text",
|
| 1259 |
+
"text": "Recovered"
|
| 1260 |
+
}
|
| 1261 |
+
],
|
| 1262 |
+
"details": {},
|
| 1263 |
+
"isError": false,
|
| 1264 |
+
"timestamp": 1789201343745
|
| 1265 |
+
},
|
| 1266 |
+
{
|
| 1267 |
+
"role": "assistant",
|
| 1268 |
+
"api": "minicpm-webgpu",
|
| 1269 |
+
"provider": "browser",
|
| 1270 |
+
"model": "Mike0021/MiniCPM5-2B-ONNX",
|
| 1271 |
+
"timestamp": 1789201343745,
|
| 1272 |
+
"content": [
|
| 1273 |
+
{
|
| 1274 |
+
"type": "text",
|
| 1275 |
+
"text": "The file `/workspace/missing.txt` did not exist, so I created it with the exact text \"Recovered\" and verified it by reading the file back, which confirms the content is \"Recovered\"."
|
| 1276 |
+
}
|
| 1277 |
+
],
|
| 1278 |
+
"stopReason": "stop",
|
| 1279 |
+
"usage": {
|
| 1280 |
+
"input": 907,
|
| 1281 |
+
"output": 43,
|
| 1282 |
+
"cacheRead": 0,
|
| 1283 |
+
"cacheWrite": 0,
|
| 1284 |
+
"totalTokens": 950,
|
| 1285 |
+
"cost": {
|
| 1286 |
+
"input": 0,
|
| 1287 |
+
"output": 0,
|
| 1288 |
+
"cacheRead": 0,
|
| 1289 |
+
"cacheWrite": 0,
|
| 1290 |
+
"total": 0
|
| 1291 |
+
}
|
| 1292 |
+
}
|
| 1293 |
+
}
|
| 1294 |
+
]
|
| 1295 |
+
}
|
| 1296 |
+
],
|
| 1297 |
+
"errors": [],
|
| 1298 |
+
"requests": [
|
| 1299 |
+
{
|
| 1300 |
+
"method": "GET",
|
| 1301 |
+
"url": "http://127.0.0.1:4173/"
|
| 1302 |
+
},
|
| 1303 |
+
{
|
| 1304 |
+
"method": "GET",
|
| 1305 |
+
"url": "http://127.0.0.1:4173/assets/index-DOivj3sw.js"
|
| 1306 |
+
},
|
| 1307 |
+
{
|
| 1308 |
+
"method": "GET",
|
| 1309 |
+
"url": "http://127.0.0.1:4173/assets/index-DL-Ot0hN.css"
|
| 1310 |
+
},
|
| 1311 |
+
{
|
| 1312 |
+
"method": "GET",
|
| 1313 |
+
"url": "http://127.0.0.1:4173/assets/agent.worker-DqQzo9yL.js"
|
| 1314 |
+
},
|
| 1315 |
+
{
|
| 1316 |
+
"method": "GET",
|
| 1317 |
+
"url": "http://127.0.0.1:4173/runtime/ort-wasm-simd-threaded.asyncify.mjs"
|
| 1318 |
+
},
|
| 1319 |
+
{
|
| 1320 |
+
"method": "GET",
|
| 1321 |
+
"url": "http://127.0.0.1:4173/runtime/ort-wasm-simd-threaded.asyncify.wasm"
|
| 1322 |
+
},
|
| 1323 |
+
{
|
| 1324 |
+
"method": "GET",
|
| 1325 |
+
"url": "http://127.0.0.1:4173/"
|
| 1326 |
+
},
|
| 1327 |
+
{
|
| 1328 |
+
"method": "GET",
|
| 1329 |
+
"url": "http://127.0.0.1:4173/assets/index-DOivj3sw.js"
|
| 1330 |
+
},
|
| 1331 |
+
{
|
| 1332 |
+
"method": "GET",
|
| 1333 |
+
"url": "http://127.0.0.1:4173/assets/index-DL-Ot0hN.css"
|
| 1334 |
+
},
|
| 1335 |
+
{
|
| 1336 |
+
"method": "GET",
|
| 1337 |
+
"url": "http://127.0.0.1:4173/assets/agent.worker-DqQzo9yL.js"
|
| 1338 |
+
},
|
| 1339 |
+
{
|
| 1340 |
+
"method": "GET",
|
| 1341 |
+
"url": "http://127.0.0.1:4173/favicon.svg"
|
| 1342 |
+
},
|
| 1343 |
+
{
|
| 1344 |
+
"method": "GET",
|
| 1345 |
+
"url": "http://127.0.0.1:4173/runtime/ort-wasm-simd-threaded.asyncify.mjs"
|
| 1346 |
+
},
|
| 1347 |
+
{
|
| 1348 |
+
"method": "GET",
|
| 1349 |
+
"url": "http://127.0.0.1:4173/runtime/ort-wasm-simd-threaded.asyncify.wasm"
|
| 1350 |
+
}
|
| 1351 |
+
],
|
| 1352 |
+
"coldLoadMs": 2281,
|
| 1353 |
+
"device": {
|
| 1354 |
+
"vendor": "amd",
|
| 1355 |
+
"architecture": "rdna-3",
|
| 1356 |
+
"maxStorageBufferBindingSize": 4294967292,
|
| 1357 |
+
"features": [
|
| 1358 |
+
"depth32float-stencil8",
|
| 1359 |
+
"chromium-experimental-subgroup-matrix",
|
| 1360 |
+
"rg11b10ufloat-renderable",
|
| 1361 |
+
"bgra8unorm-storage",
|
| 1362 |
+
"texture-formats-tier1",
|
| 1363 |
+
"chromium-experimental-multi-draw-indirect",
|
| 1364 |
+
"texture-compression-bc",
|
| 1365 |
+
"dual-source-blending",
|
| 1366 |
+
"core-features-and-limits",
|
| 1367 |
+
"chromium-experimental-sampling-resource-table",
|
| 1368 |
+
"chromium-experimental-timestamp-query-inside-passes",
|
| 1369 |
+
"float32-filterable",
|
| 1370 |
+
"indirect-first-instance",
|
| 1371 |
+
"float32-blendable",
|
| 1372 |
+
"subgroup-size-control",
|
| 1373 |
+
"texture-compression-unaligned",
|
| 1374 |
+
"depth-clip-control",
|
| 1375 |
+
"texture-compression-bc-sliced-3d",
|
| 1376 |
+
"clip-distances",
|
| 1377 |
+
"shader-f16",
|
| 1378 |
+
"texture-formats-tier2",
|
| 1379 |
+
"timestamp-query",
|
| 1380 |
+
"primitive-index",
|
| 1381 |
+
"texture-component-swizzle",
|
| 1382 |
+
"subgroups"
|
| 1383 |
+
],
|
| 1384 |
+
"actualLimits": {
|
| 1385 |
+
"maxStorageBufferBindingSize": 4294967292,
|
| 1386 |
+
"maxBufferSize": 4294967292
|
| 1387 |
+
}
|
| 1388 |
+
},
|
| 1389 |
+
"cancellation": {
|
| 1390 |
+
"passed": true,
|
| 1391 |
+
"completedGenerations": 0
|
| 1392 |
+
},
|
| 1393 |
+
"diagnostics": {
|
| 1394 |
+
"events": [
|
| 1395 |
+
"agent_start",
|
| 1396 |
+
"turn_start",
|
| 1397 |
+
"message_start",
|
| 1398 |
+
"message_end",
|
| 1399 |
+
"message_start",
|
| 1400 |
+
"message_update",
|
| 1401 |
+
"message_update",
|
| 1402 |
+
"message_end",
|
| 1403 |
+
"tool_execution_start",
|
| 1404 |
+
"tool_execution_end",
|
| 1405 |
+
"message_start",
|
| 1406 |
+
"message_end",
|
| 1407 |
+
"turn_end",
|
| 1408 |
+
"turn_start",
|
| 1409 |
+
"message_start",
|
| 1410 |
+
"message_update",
|
| 1411 |
+
"message_update",
|
| 1412 |
+
"message_update",
|
| 1413 |
+
"message_update",
|
| 1414 |
+
"message_update",
|
| 1415 |
+
"message_update",
|
| 1416 |
+
"message_update",
|
| 1417 |
+
"message_update",
|
| 1418 |
+
"message_update",
|
| 1419 |
+
"message_update",
|
| 1420 |
+
"message_update",
|
| 1421 |
+
"message_update",
|
| 1422 |
+
"message_update",
|
| 1423 |
+
"message_end",
|
| 1424 |
+
"turn_end",
|
| 1425 |
+
"agent_end",
|
| 1426 |
+
"agent_start",
|
| 1427 |
+
"turn_start",
|
| 1428 |
+
"message_start",
|
| 1429 |
+
"message_end",
|
| 1430 |
+
"message_start",
|
| 1431 |
+
"message_update",
|
| 1432 |
+
"message_update",
|
| 1433 |
+
"message_end",
|
| 1434 |
+
"tool_execution_start",
|
| 1435 |
+
"tool_execution_end",
|
| 1436 |
+
"message_start",
|
| 1437 |
+
"message_end",
|
| 1438 |
+
"turn_end",
|
| 1439 |
+
"turn_start",
|
| 1440 |
+
"message_start",
|
| 1441 |
+
"message_update",
|
| 1442 |
+
"message_update",
|
| 1443 |
+
"message_end",
|
| 1444 |
+
"tool_execution_start",
|
| 1445 |
+
"tool_execution_end",
|
| 1446 |
+
"message_start",
|
| 1447 |
+
"message_end",
|
| 1448 |
+
"turn_end",
|
| 1449 |
+
"turn_start",
|
| 1450 |
+
"message_start",
|
| 1451 |
+
"message_update",
|
| 1452 |
+
"message_update",
|
| 1453 |
+
"message_end",
|
| 1454 |
+
"tool_execution_start",
|
| 1455 |
+
"tool_execution_end",
|
| 1456 |
+
"message_start",
|
| 1457 |
+
"message_end",
|
| 1458 |
+
"turn_end",
|
| 1459 |
+
"turn_start",
|
| 1460 |
+
"message_start",
|
| 1461 |
+
"message_update",
|
| 1462 |
+
"message_update",
|
| 1463 |
+
"message_end",
|
| 1464 |
+
"tool_execution_start",
|
| 1465 |
+
"tool_execution_end",
|
| 1466 |
+
"message_start",
|
| 1467 |
+
"message_end",
|
| 1468 |
+
"turn_end",
|
| 1469 |
+
"turn_start",
|
| 1470 |
+
"message_start",
|
| 1471 |
+
"message_update",
|
| 1472 |
+
"message_update",
|
| 1473 |
+
"message_update",
|
| 1474 |
+
"message_update",
|
| 1475 |
+
"message_update",
|
| 1476 |
+
"message_update",
|
| 1477 |
+
"message_update",
|
| 1478 |
+
"message_update",
|
| 1479 |
+
"message_update",
|
| 1480 |
+
"message_update",
|
| 1481 |
+
"message_update",
|
| 1482 |
+
"message_update",
|
| 1483 |
+
"message_update",
|
| 1484 |
+
"message_update",
|
| 1485 |
+
"message_update",
|
| 1486 |
+
"message_update",
|
| 1487 |
+
"message_update",
|
| 1488 |
+
"message_update",
|
| 1489 |
+
"message_update",
|
| 1490 |
+
"message_update",
|
| 1491 |
+
"message_end",
|
| 1492 |
+
"turn_end",
|
| 1493 |
+
"agent_end",
|
| 1494 |
+
"agent_start",
|
| 1495 |
+
"turn_start",
|
| 1496 |
+
"message_start",
|
| 1497 |
+
"message_end",
|
| 1498 |
+
"message_start",
|
| 1499 |
+
"message_update",
|
| 1500 |
+
"message_update",
|
| 1501 |
+
"message_end",
|
| 1502 |
+
"tool_execution_start",
|
| 1503 |
+
"tool_execution_end",
|
| 1504 |
+
"message_start",
|
| 1505 |
+
"message_end",
|
| 1506 |
+
"turn_end",
|
| 1507 |
+
"turn_start",
|
| 1508 |
+
"message_start",
|
| 1509 |
+
"message_update",
|
| 1510 |
+
"message_update",
|
| 1511 |
+
"message_end",
|
| 1512 |
+
"tool_execution_start",
|
| 1513 |
+
"tool_execution_end",
|
| 1514 |
+
"message_start",
|
| 1515 |
+
"message_end",
|
| 1516 |
+
"turn_end",
|
| 1517 |
+
"turn_start",
|
| 1518 |
+
"message_start",
|
| 1519 |
+
"message_update",
|
| 1520 |
+
"message_update",
|
| 1521 |
+
"message_update",
|
| 1522 |
+
"message_update",
|
| 1523 |
+
"message_update",
|
| 1524 |
+
"message_update",
|
| 1525 |
+
"message_update",
|
| 1526 |
+
"message_update",
|
| 1527 |
+
"message_update",
|
| 1528 |
+
"message_update",
|
| 1529 |
+
"message_update",
|
| 1530 |
+
"message_update",
|
| 1531 |
+
"message_update",
|
| 1532 |
+
"message_update",
|
| 1533 |
+
"message_update",
|
| 1534 |
+
"message_update",
|
| 1535 |
+
"message_update",
|
| 1536 |
+
"message_update",
|
| 1537 |
+
"message_update",
|
| 1538 |
+
"message_update",
|
| 1539 |
+
"message_update",
|
| 1540 |
+
"message_update",
|
| 1541 |
+
"message_update",
|
| 1542 |
+
"message_update",
|
| 1543 |
+
"message_update",
|
| 1544 |
+
"message_end",
|
| 1545 |
+
"turn_end",
|
| 1546 |
+
"agent_end",
|
| 1547 |
+
"agent_start",
|
| 1548 |
+
"turn_start",
|
| 1549 |
+
"message_start",
|
| 1550 |
+
"message_end",
|
| 1551 |
+
"message_start",
|
| 1552 |
+
"message_update",
|
| 1553 |
+
"message_update",
|
| 1554 |
+
"message_end",
|
| 1555 |
+
"tool_execution_start",
|
| 1556 |
+
"tool_execution_end",
|
| 1557 |
+
"message_start",
|
| 1558 |
+
"message_end",
|
| 1559 |
+
"turn_end",
|
| 1560 |
+
"turn_start",
|
| 1561 |
+
"message_start",
|
| 1562 |
+
"message_update",
|
| 1563 |
+
"message_update",
|
| 1564 |
+
"message_end",
|
| 1565 |
+
"tool_execution_start",
|
| 1566 |
+
"tool_execution_end",
|
| 1567 |
+
"message_start",
|
| 1568 |
+
"message_end",
|
| 1569 |
+
"turn_end",
|
| 1570 |
+
"turn_start",
|
| 1571 |
+
"message_start",
|
| 1572 |
+
"message_update",
|
| 1573 |
+
"message_update",
|
| 1574 |
+
"message_end",
|
| 1575 |
+
"tool_execution_start",
|
| 1576 |
+
"tool_execution_end",
|
| 1577 |
+
"message_start",
|
| 1578 |
+
"message_end",
|
| 1579 |
+
"turn_end",
|
| 1580 |
+
"turn_start",
|
| 1581 |
+
"message_start",
|
| 1582 |
+
"message_update",
|
| 1583 |
+
"message_update",
|
| 1584 |
+
"message_update",
|
| 1585 |
+
"message_update",
|
| 1586 |
+
"message_update",
|
| 1587 |
+
"message_update",
|
| 1588 |
+
"message_update",
|
| 1589 |
+
"message_update",
|
| 1590 |
+
"message_update",
|
| 1591 |
+
"message_update",
|
| 1592 |
+
"message_update",
|
| 1593 |
+
"message_end",
|
| 1594 |
+
"turn_end",
|
| 1595 |
+
"agent_end",
|
| 1596 |
+
"agent_start",
|
| 1597 |
+
"turn_start",
|
| 1598 |
+
"message_start",
|
| 1599 |
+
"message_end",
|
| 1600 |
+
"message_start",
|
| 1601 |
+
"message_update",
|
| 1602 |
+
"message_update",
|
| 1603 |
+
"message_end",
|
| 1604 |
+
"tool_execution_start",
|
| 1605 |
+
"tool_execution_end",
|
| 1606 |
+
"message_start",
|
| 1607 |
+
"message_end",
|
| 1608 |
+
"turn_end",
|
| 1609 |
+
"turn_start",
|
| 1610 |
+
"message_start",
|
| 1611 |
+
"message_update",
|
| 1612 |
+
"message_update",
|
| 1613 |
+
"message_end",
|
| 1614 |
+
"tool_execution_start",
|
| 1615 |
+
"tool_execution_end",
|
| 1616 |
+
"message_start",
|
| 1617 |
+
"message_end",
|
| 1618 |
+
"turn_end",
|
| 1619 |
+
"turn_start",
|
| 1620 |
+
"message_start",
|
| 1621 |
+
"message_update",
|
| 1622 |
+
"message_update",
|
| 1623 |
+
"message_end",
|
| 1624 |
+
"tool_execution_start",
|
| 1625 |
+
"tool_execution_end",
|
| 1626 |
+
"message_start",
|
| 1627 |
+
"message_end",
|
| 1628 |
+
"turn_end",
|
| 1629 |
+
"turn_start",
|
| 1630 |
+
"message_start",
|
| 1631 |
+
"message_update",
|
| 1632 |
+
"message_update",
|
| 1633 |
+
"message_update",
|
| 1634 |
+
"message_update",
|
| 1635 |
+
"message_update",
|
| 1636 |
+
"message_update",
|
| 1637 |
+
"message_update",
|
| 1638 |
+
"message_update",
|
| 1639 |
+
"message_update",
|
| 1640 |
+
"message_update",
|
| 1641 |
+
"message_update",
|
| 1642 |
+
"message_update",
|
| 1643 |
+
"message_update",
|
| 1644 |
+
"message_update",
|
| 1645 |
+
"message_update",
|
| 1646 |
+
"message_update",
|
| 1647 |
+
"message_update",
|
| 1648 |
+
"message_update",
|
| 1649 |
+
"message_update",
|
| 1650 |
+
"message_update",
|
| 1651 |
+
"message_update",
|
| 1652 |
+
"message_update",
|
| 1653 |
+
"message_update",
|
| 1654 |
+
"message_update",
|
| 1655 |
+
"message_update",
|
| 1656 |
+
"message_update",
|
| 1657 |
+
"message_update",
|
| 1658 |
+
"message_update",
|
| 1659 |
+
"message_update",
|
| 1660 |
+
"message_update",
|
| 1661 |
+
"message_update",
|
| 1662 |
+
"message_end",
|
| 1663 |
+
"turn_end",
|
| 1664 |
+
"agent_end",
|
| 1665 |
+
"agent_start",
|
| 1666 |
+
"turn_start",
|
| 1667 |
+
"message_start",
|
| 1668 |
+
"message_end",
|
| 1669 |
+
"message_start",
|
| 1670 |
+
"message_update",
|
| 1671 |
+
"message_update",
|
| 1672 |
+
"message_update",
|
| 1673 |
+
"message_update",
|
| 1674 |
+
"message_end",
|
| 1675 |
+
"turn_end",
|
| 1676 |
+
"agent_end"
|
| 1677 |
+
],
|
| 1678 |
+
"generations": [
|
| 1679 |
+
{
|
| 1680 |
+
"type": "generation",
|
| 1681 |
+
"raw": "<function name=\"read\"><param name=\"path\">/workspace/secret.txt</param></function><|im_end|>",
|
| 1682 |
+
"inputTokens": 717,
|
| 1683 |
+
"outputTokens": 18,
|
| 1684 |
+
"elapsedMs": 1002.8000000044703,
|
| 1685 |
+
"firstTokenMs": 666.3999999985099,
|
| 1686 |
+
"stopReason": "toolUse"
|
| 1687 |
+
},
|
| 1688 |
+
{
|
| 1689 |
+
"type": "generation",
|
| 1690 |
+
"raw": "The read tool returned the contents of secret.txt as:\n\nLOCAL-fd008be1<|im_end|>",
|
| 1691 |
+
"inputTokens": 771,
|
| 1692 |
+
"outputTokens": 20,
|
| 1693 |
+
"elapsedMs": 1041.2000000029802,
|
| 1694 |
+
"firstTokenMs": 677.9000000059605,
|
| 1695 |
+
"stopReason": "stop"
|
| 1696 |
+
},
|
| 1697 |
+
{
|
| 1698 |
+
"type": "generation",
|
| 1699 |
+
"raw": "<function name=\"read\"><param name=\"path\">/workspace/sales.csv</param></function><|im_end|>",
|
| 1700 |
+
"inputTokens": 726,
|
| 1701 |
+
"outputTokens": 18,
|
| 1702 |
+
"elapsedMs": 965.1000000014901,
|
| 1703 |
+
"firstTokenMs": 641.2000000029802,
|
| 1704 |
+
"stopReason": "toolUse"
|
| 1705 |
+
},
|
| 1706 |
+
{
|
| 1707 |
+
"type": "generation",
|
| 1708 |
+
"raw": "<function name=\"bash\"><param name=\"command\"><![CDATA[awk -F',' 'NR>1 {rev=$2*$3; total+=rev; print $1,$2,$3,\"->\",$2,\"x\",$3,\"=\",rev} END {print \"TOTAL REVENUE:\", total}' sales.csv > revenue.txt && cat revenue.txt]]></param></function><|im_end|>",
|
| 1709 |
+
"inputTokens": 798,
|
| 1710 |
+
"outputTokens": 78,
|
| 1711 |
+
"elapsedMs": 2038.1000000014901,
|
| 1712 |
+
"firstTokenMs": 690.6000000014901,
|
| 1713 |
+
"stopReason": "toolUse"
|
| 1714 |
+
},
|
| 1715 |
+
{
|
| 1716 |
+
"type": "generation",
|
| 1717 |
+
"raw": "<function name=\"write\"><param name=\"path\">report.md</param><param name=\"content\"><![CDATA[# Sales Report\n\nTotal revenue from sales.csv is **$144**.\n\n- Notebook: 12 Γ $5 = $60\n- Pencil: 30 Γ $2 = $60\n- Folder: 8 Γ $3 = $24\n]]></param></function><|im_end|>",
|
| 1718 |
+
"inputTokens": 964,
|
| 1719 |
+
"outputTokens": 79,
|
| 1720 |
+
"elapsedMs": 2203.0999999940395,
|
| 1721 |
+
"firstTokenMs": 839.0999999940395,
|
| 1722 |
+
"stopReason": "toolUse"
|
| 1723 |
+
},
|
| 1724 |
+
{
|
| 1725 |
+
"type": "generation",
|
| 1726 |
+
"raw": "<function name=\"read\"><param name=\"path\">/workspace/report.md</param></function><|im_end|>",
|
| 1727 |
+
"inputTokens": 1079,
|
| 1728 |
+
"outputTokens": 18,
|
| 1729 |
+
"elapsedMs": 1214,
|
| 1730 |
+
"firstTokenMs": 906.7000000029802,
|
| 1731 |
+
"stopReason": "toolUse"
|
| 1732 |
+
},
|
| 1733 |
+
{
|
| 1734 |
+
"type": "generation",
|
| 1735 |
+
"raw": "Done. The report is saved at `/workspace/report.md` with total revenue of $144 (Notebook $60, Pencil $60, Folder $24).<|im_end|>",
|
| 1736 |
+
"inputTokens": 1180,
|
| 1737 |
+
"outputTokens": 35,
|
| 1738 |
+
"elapsedMs": 1609.3999999985099,
|
| 1739 |
+
"firstTokenMs": 984.7999999970198,
|
| 1740 |
+
"stopReason": "stop"
|
| 1741 |
+
},
|
| 1742 |
+
{
|
| 1743 |
+
"type": "generation",
|
| 1744 |
+
"raw": "<function name=\"write\"><param name=\"path\">/workspace/hello.sh</param><param name=\"content\"><![CDATA[#!/bin/bash\necho \"Hello from the browser\"\n]]></param></function><|im_end|>",
|
| 1745 |
+
"inputTokens": 725,
|
| 1746 |
+
"outputTokens": 43,
|
| 1747 |
+
"elapsedMs": 1358.5,
|
| 1748 |
+
"firstTokenMs": 638.8999999985099,
|
| 1749 |
+
"stopReason": "toolUse"
|
| 1750 |
+
},
|
| 1751 |
+
{
|
| 1752 |
+
"type": "generation",
|
| 1753 |
+
"raw": "<function name=\"bash\"><param name=\"command\"><![CDATA[chmod +x /workspace/hello.sh && /workspace/hello.sh]]></param></function><|im_end|>",
|
| 1754 |
+
"inputTokens": 804,
|
| 1755 |
+
"outputTokens": 34,
|
| 1756 |
+
"elapsedMs": 1441.5,
|
| 1757 |
+
"firstTokenMs": 686.5,
|
| 1758 |
+
"stopReason": "toolUse"
|
| 1759 |
+
},
|
| 1760 |
+
{
|
| 1761 |
+
"type": "generation",
|
| 1762 |
+
"raw": "The script `/workspace/hello.sh` prints \"Hello from the browser\". Running it outputs:\n\n```\nHello from the browser\n```\n\nwith exit code 0.<|im_end|>",
|
| 1763 |
+
"inputTokens": 878,
|
| 1764 |
+
"outputTokens": 35,
|
| 1765 |
+
"elapsedMs": 1343.4000000059605,
|
| 1766 |
+
"firstTokenMs": 747.6000000014901,
|
| 1767 |
+
"stopReason": "stop"
|
| 1768 |
+
},
|
| 1769 |
+
{
|
| 1770 |
+
"type": "generation",
|
| 1771 |
+
"raw": "<function name=\"read\"><param name=\"path\">hello.sh</param></function><|im_end|>",
|
| 1772 |
+
"inputTokens": 731,
|
| 1773 |
+
"outputTokens": 15,
|
| 1774 |
+
"elapsedMs": 893.0999999940395,
|
| 1775 |
+
"firstTokenMs": 638.7999999970198,
|
| 1776 |
+
"stopReason": "toolUse"
|
| 1777 |
+
},
|
| 1778 |
+
{
|
| 1779 |
+
"type": "generation",
|
| 1780 |
+
"raw": "<function name=\"edit\"><param name=\"path\">hello.sh</param><param name=\"oldText\">echo \"Hello from the browser\"</param><param name=\"newText\">echo \"Hello from Pi\"</param></function><|im_end|>",
|
| 1781 |
+
"inputTokens": 790,
|
| 1782 |
+
"outputTokens": 42,
|
| 1783 |
+
"elapsedMs": 1381.2000000029802,
|
| 1784 |
+
"firstTokenMs": 679.3999999985099,
|
| 1785 |
+
"stopReason": "toolUse"
|
| 1786 |
+
},
|
| 1787 |
+
{
|
| 1788 |
+
"type": "generation",
|
| 1789 |
+
"raw": "<function name=\"bash\"><param name=\"command\"><![CDATA[chmod +x hello.sh && ./hello.sh]]></param></function><|im_end|>",
|
| 1790 |
+
"inputTokens": 867,
|
| 1791 |
+
"outputTokens": 29,
|
| 1792 |
+
"elapsedMs": 1316.6999999955297,
|
| 1793 |
+
"firstTokenMs": 738.7999999970198,
|
| 1794 |
+
"stopReason": "toolUse"
|
| 1795 |
+
},
|
| 1796 |
+
{
|
| 1797 |
+
"type": "generation",
|
| 1798 |
+
"raw": "The script now outputs \"Hello from Pi\" when run.<|im_end|>",
|
| 1799 |
+
"inputTokens": 935,
|
| 1800 |
+
"outputTokens": 13,
|
| 1801 |
+
"elapsedMs": 1007.1999999955297,
|
| 1802 |
+
"firstTokenMs": 796.8999999985099,
|
| 1803 |
+
"stopReason": "stop"
|
| 1804 |
+
},
|
| 1805 |
+
{
|
| 1806 |
+
"type": "generation",
|
| 1807 |
+
"raw": "<function name=\"read\"><param name=\"path\">/workspace/missing.txt</param></function><|im_end|>",
|
| 1808 |
+
"inputTokens": 733,
|
| 1809 |
+
"outputTokens": 18,
|
| 1810 |
+
"elapsedMs": 952.5,
|
| 1811 |
+
"firstTokenMs": 647.2999999970198,
|
| 1812 |
+
"stopReason": "toolUse"
|
| 1813 |
+
},
|
| 1814 |
+
{
|
| 1815 |
+
"type": "generation",
|
| 1816 |
+
"raw": "<function name=\"write\"><param name=\"path\">/workspace/missing.txt</param><param name=\"content\"><![CDATA[Recovered]]></param></function><|im_end|>",
|
| 1817 |
+
"inputTokens": 796,
|
| 1818 |
+
"outputTokens": 32,
|
| 1819 |
+
"elapsedMs": 1242.6000000014901,
|
| 1820 |
+
"firstTokenMs": 685.9000000059605,
|
| 1821 |
+
"stopReason": "toolUse"
|
| 1822 |
+
},
|
| 1823 |
+
{
|
| 1824 |
+
"type": "generation",
|
| 1825 |
+
"raw": "<function name=\"read\"><param name=\"path\">/workspace/missing.txt</param></function><|im_end|>",
|
| 1826 |
+
"inputTokens": 858,
|
| 1827 |
+
"outputTokens": 18,
|
| 1828 |
+
"elapsedMs": 1051.5,
|
| 1829 |
+
"firstTokenMs": 751.1000000014901,
|
| 1830 |
+
"stopReason": "toolUse"
|
| 1831 |
+
},
|
| 1832 |
+
{
|
| 1833 |
+
"type": "generation",
|
| 1834 |
+
"raw": "The file `/workspace/missing.txt` did not exist, so I created it with the exact text \"Recovered\" and verified it by reading the file back, which confirms the content is \"Recovered\".<|im_end|>",
|
| 1835 |
+
"inputTokens": 907,
|
| 1836 |
+
"outputTokens": 43,
|
| 1837 |
+
"elapsedMs": 1566.5,
|
| 1838 |
+
"firstTokenMs": 792.9000000059605,
|
| 1839 |
+
"stopReason": "stop"
|
| 1840 |
+
}
|
| 1841 |
+
],
|
| 1842 |
+
"device": {
|
| 1843 |
+
"vendor": "amd",
|
| 1844 |
+
"architecture": "rdna-3",
|
| 1845 |
+
"maxStorageBufferBindingSize": 4294967292,
|
| 1846 |
+
"features": [
|
| 1847 |
+
"depth32float-stencil8",
|
| 1848 |
+
"chromium-experimental-subgroup-matrix",
|
| 1849 |
+
"rg11b10ufloat-renderable",
|
| 1850 |
+
"bgra8unorm-storage",
|
| 1851 |
+
"texture-formats-tier1",
|
| 1852 |
+
"chromium-experimental-multi-draw-indirect",
|
| 1853 |
+
"texture-compression-bc",
|
| 1854 |
+
"dual-source-blending",
|
| 1855 |
+
"core-features-and-limits",
|
| 1856 |
+
"chromium-experimental-sampling-resource-table",
|
| 1857 |
+
"chromium-experimental-timestamp-query-inside-passes",
|
| 1858 |
+
"float32-filterable",
|
| 1859 |
+
"indirect-first-instance",
|
| 1860 |
+
"float32-blendable",
|
| 1861 |
+
"subgroup-size-control",
|
| 1862 |
+
"texture-compression-unaligned",
|
| 1863 |
+
"depth-clip-control",
|
| 1864 |
+
"texture-compression-bc-sliced-3d",
|
| 1865 |
+
"clip-distances",
|
| 1866 |
+
"shader-f16",
|
| 1867 |
+
"texture-formats-tier2",
|
| 1868 |
+
"timestamp-query",
|
| 1869 |
+
"primitive-index",
|
| 1870 |
+
"texture-component-swizzle",
|
| 1871 |
+
"subgroups"
|
| 1872 |
+
],
|
| 1873 |
+
"actualLimits": {
|
| 1874 |
+
"maxStorageBufferBindingSize": 4294967292,
|
| 1875 |
+
"maxBufferSize": 4294967292
|
| 1876 |
+
}
|
| 1877 |
+
}
|
| 1878 |
+
},
|
| 1879 |
+
"editorAndShell": true,
|
| 1880 |
+
"requestsDuringOfflineTasks": [],
|
| 1881 |
+
"warmLoadMs": 2122,
|
| 1882 |
+
"reloadWeightRequests": [],
|
| 1883 |
+
"mobileLayout": "390 Γ 844; desktop GPU, not a mobile hardware test",
|
| 1884 |
+
"passed": true
|
| 1885 |
+
}
|
validation/agent-terminal.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"date": "2026-09-12T08:26:57.014Z",
|
| 3 |
+
"url": "http://127.0.0.1:4173/",
|
| 4 |
+
"errors": [],
|
| 5 |
+
"requests": [
|
| 6 |
+
"http://127.0.0.1:4173/",
|
| 7 |
+
"http://127.0.0.1:4173/assets/index-BRKUEVCq.js",
|
| 8 |
+
"http://127.0.0.1:4173/assets/index-DL-Ot0hN.css",
|
| 9 |
+
"http://127.0.0.1:4173/assets/agent.worker-DqQzo9yL.js",
|
| 10 |
+
"http://127.0.0.1:4173/favicon.svg"
|
| 11 |
+
],
|
| 12 |
+
"checks": [
|
| 13 |
+
"Original Pi TUI rendered through xterm",
|
| 14 |
+
"Enter submits /help without loading a model",
|
| 15 |
+
"Pi fullscreen transcript search preserves the editor draft",
|
| 16 |
+
"Pi multiline editor: Shift+Enter, cursor, deletion, Ctrl+U/K",
|
| 17 |
+
"Pi prompt history",
|
| 18 |
+
"Pi autocomplete: /he β /help",
|
| 19 |
+
"xterm keyboard β Pi editor β real just-bash β workspace file",
|
| 20 |
+
"Shell escape sequences render as inert text",
|
| 21 |
+
"Large Pi paste markers expand before Send",
|
| 22 |
+
"Unicode multiline clipboard paste",
|
| 23 |
+
"Touch-accessible newline button",
|
| 24 |
+
"390px layout and live terminal resize preserve the draft"
|
| 25 |
+
],
|
| 26 |
+
"initial": " \n Ο Pi browser workspace \n \n Ask Pi to work with the files on the right. \n \n /load load MiniCPM \n /help show commands \n !ls run a workspace command \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n Files and chat are saved on this device. \nββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ\n \nββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ\n /workspace Β· MiniCPM5-2B Β· 4,096 context ",
|
| 27 |
+
"sizes": {
|
| 28 |
+
"desktop": {
|
| 29 |
+
"cols": 90,
|
| 30 |
+
"rows": 41
|
| 31 |
+
},
|
| 32 |
+
"mobile": {
|
| 33 |
+
"cols": 40,
|
| 34 |
+
"rows": 32
|
| 35 |
+
}
|
| 36 |
+
},
|
| 37 |
+
"mobileLimitation": "Desktop Chromium viewport simulation; physical mobile keyboards and GPUs not tested.",
|
| 38 |
+
"passed": true
|
| 39 |
+
}
|