Add product costs and inventory demo with a concise profit suggestion
Browse files- app/README.md +3 -0
- app/package.json +1 -0
- app/src/agent.worker.mjs +17 -3
- app/src/pi/empty-mascot.mjs +7 -3
- app/src/pi/terminal-ui.mjs +1 -1
- app/src/workspace.mjs +2 -1
- app/tests/mascot.mjs +3 -1
- app/tests/protocol.test.mjs +2 -0
- app/tests/starter-products.mjs +66 -0
- assets/{agent.worker-CG915rYx.js → agent.worker-L7ew_XyL.js} +0 -0
- assets/{index-DifYXNCD.js → index-8TcpvU1k.js} +3 -3
- deployment-manifest.json +37 -25
- index.html +1 -1
- scripts/prepare_space.py +1 -0
- validation/agent-products-mascot.json +136 -0
- validation/agent-starter-products.json +10 -0
app/README.md
CHANGED
|
@@ -20,6 +20,7 @@ For development, use `npm run dev`. To use the already converted weights on this
|
|
| 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 with Prism syntax colors for HTML/XML/SVG, CSS, JavaScript/TypeScript (including JSX/TSX), Python, Bash, JSON, Markdown, YAML, TOML, SQL, CSV, C/C++, C#, Java, Go, Rust, Dockerfiles, and diffs. Highlighting runs in a separate, lazily started worker and is debounced while typing. Native textarea editing, selection, undo, and scrolling are preserved. Unknown formats, files above 200,000 characters or 5,000 lines, excessive token output, and grammar work exceeding 1.5 seconds fall back to plain text. Text-file import, workspace export, and an interactive command entry are also included.
|
|
|
|
| 23 |
- Successful `read` tool calls open and highlight the resolved file in the workspace, including relative paths and symlinks. Following waits for newly created files, preserves unsaved edits, and keeps the conversation’s focus and page scroll position. Common file types use a local subset of 18 Seti icons from VS Code’s built-in theme, totaling about 12 KB of SVGs.
|
| 24 |
- The shell highlights Bash as you type, accepts multiline commands (Shift+Enter), recalls session history (↑/↓), and separates command/output blocks with colored exit states and errors. Clear or Ctrl+L clears the output. Its output area can be resized vertically.
|
| 25 |
- Model name, loading progress, and loaded status live in a rounded, inset top bar aligned with the workspace width. New chat sits at the bottom left of the terminal, opposite the send controls. Panels use background shading and rounded corners instead of separator borders.
|
|
@@ -76,6 +77,8 @@ npm run test:runtime-meter
|
|
| 76 |
npm run test:follow-read
|
| 77 |
# File highlighting, native editing, saving, scrolling and large-file fallback:
|
| 78 |
npm run test:editor
|
|
|
|
|
|
|
| 79 |
# Syntax-colored command input, actual shell execution, history and layouts:
|
| 80 |
npm run test:shell
|
| 81 |
# Actual token usage, streaming, cancellation, reload and input focus:
|
|
|
|
| 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 with Prism syntax colors for HTML/XML/SVG, CSS, JavaScript/TypeScript (including JSX/TSX), Python, Bash, JSON, Markdown, YAML, TOML, SQL, CSV, C/C++, C#, Java, Go, Rust, Dockerfiles, and diffs. Highlighting runs in a separate, lazily started worker and is debounced while typing. Native textarea editing, selection, undo, and scrolling are preserved. Unknown formats, files above 200,000 characters or 5,000 lines, excessive token output, and grammar work exceeding 1.5 seconds fall back to plain text. Text-file import, workspace export, and an interactive command entry are also included.
|
| 23 |
+
- The starter workspace pairs `sales.csv` with `products.csv` (matching product names, unit costs, and current stock), giving Pi data to discover and combine for profit or inventory questions. The cat suggests “What's our total sales profit?” without prescribing files or tools. Older saved sales workspaces receive the new file once; existing product files and later deletions are preserved.
|
| 24 |
- Successful `read` tool calls open and highlight the resolved file in the workspace, including relative paths and symlinks. Following waits for newly created files, preserves unsaved edits, and keeps the conversation’s focus and page scroll position. Common file types use a local subset of 18 Seti icons from VS Code’s built-in theme, totaling about 12 KB of SVGs.
|
| 25 |
- The shell highlights Bash as you type, accepts multiline commands (Shift+Enter), recalls session history (↑/↓), and separates command/output blocks with colored exit states and errors. Clear or Ctrl+L clears the output. Its output area can be resized vertically.
|
| 26 |
- Model name, loading progress, and loaded status live in a rounded, inset top bar aligned with the workspace width. New chat sits at the bottom left of the terminal, opposite the send controls. Panels use background shading and rounded corners instead of separator borders.
|
|
|
|
| 77 |
npm run test:follow-read
|
| 78 |
# File highlighting, native editing, saving, scrolling and large-file fallback:
|
| 79 |
npm run test:editor
|
| 80 |
+
# Companion product data and preserving existing workspaces during its addition:
|
| 81 |
+
npm run test:starter-products
|
| 82 |
# Syntax-colored command input, actual shell execution, history and layouts:
|
| 83 |
npm run test:shell
|
| 84 |
# Actual token usage, streaming, cancellation, reload and input focus:
|
app/package.json
CHANGED
|
@@ -18,6 +18,7 @@
|
|
| 18 |
"test:mascot": "node tests/mascot.mjs",
|
| 19 |
"test:follow-read": "node tests/follow-read.mjs",
|
| 20 |
"test:editor": "node tests/editor.mjs",
|
|
|
|
| 21 |
"test:shell": "node tests/shell.mjs",
|
| 22 |
"test:context": "node tests/context.mjs"
|
| 23 |
},
|
|
|
|
| 18 |
"test:mascot": "node tests/mascot.mjs",
|
| 19 |
"test:follow-read": "node tests/follow-read.mjs",
|
| 20 |
"test:editor": "node tests/editor.mjs",
|
| 21 |
+
"test:starter-products": "node tests/starter-products.mjs",
|
| 22 |
"test:shell": "node tests/shell.mjs",
|
| 23 |
"test:context": "node tests/context.mjs"
|
| 24 |
},
|
app/src/agent.worker.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
| 1 |
import { Agent } from '@earendil-works/pi-agent-core';
|
| 2 |
import { createInference, localModel } from './inference.mjs';
|
| 3 |
-
import { createWorkspace } from './workspace.mjs';
|
| 4 |
import { openStorage } from './storage.mjs';
|
| 5 |
import { isModelCached } from './download.mjs';
|
| 6 |
|
|
@@ -18,7 +18,7 @@ You can create and edit text files, including HTML, CSS, JavaScript, and shell s
|
|
| 18 |
Interpret short follow-ups using the conversation and inspect the relevant files when needed.
|
| 19 |
Inspect files before editing them. After changing files, verify the result with a tool. Finish with a short factual answer.`;
|
| 20 |
|
| 21 |
-
async function snapshot() { return { version: 1, files: await workspace.snapshot(), entries: await workspace.serialize(), messages: agent.state.messages }; }
|
| 22 |
async function persist() {
|
| 23 |
const state = await snapshot();
|
| 24 |
try { await storage?.save(state); } catch (e) { emit({ type: 'persistence_error', error: 'Changes are in memory, but browser storage failed: ' + e.message }); }
|
|
@@ -31,6 +31,15 @@ const initialized = (async () => {
|
|
| 31 |
catch (e) { emit({ type: 'persistence_error', error: 'Workspace saving is unavailable: ' + e.message }); }
|
| 32 |
workspace = createWorkspace(saved?.files, saved?.entries);
|
| 33 |
await workspace.ready;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 34 |
agent = new Agent({ initialState: { model: localModel, thinkingLevel: 'medium', systemPrompt, tools: workspace.tools, messages: saved?.messages ?? [] },
|
| 35 |
streamFn: inference.streamFn, toolExecution: 'sequential',
|
| 36 |
afterToolCall: ({ result, isError }) => ({ isError: isError || (result.details?.exitCode !== undefined && result.details.exitCode !== 0) }),
|
|
@@ -44,7 +53,12 @@ const initialized = (async () => {
|
|
| 44 |
emit({ type: 'agent_event', event });
|
| 45 |
if (event.type === 'tool_execution_end' || event.type === 'agent_end') await persist();
|
| 46 |
});
|
| 47 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 48 |
})();
|
| 49 |
|
| 50 |
onmessage = async ({ data }) => {
|
|
|
|
| 1 |
import { Agent } from '@earendil-works/pi-agent-core';
|
| 2 |
import { createInference, localModel } from './inference.mjs';
|
| 3 |
+
import { createWorkspace, initialFiles } from './workspace.mjs';
|
| 4 |
import { openStorage } from './storage.mjs';
|
| 5 |
import { isModelCached } from './download.mjs';
|
| 6 |
|
|
|
|
| 18 |
Interpret short follow-ups using the conversation and inspect the relevant files when needed.
|
| 19 |
Inspect files before editing them. After changing files, verify the result with a tool. Finish with a short factual answer.`;
|
| 20 |
|
| 21 |
+
async function snapshot() { return { version: 1, demoVersion: 1, files: await workspace.snapshot(), entries: await workspace.serialize(), messages: agent.state.messages }; }
|
| 22 |
async function persist() {
|
| 23 |
const state = await snapshot();
|
| 24 |
try { await storage?.save(state); } catch (e) { emit({ type: 'persistence_error', error: 'Changes are in memory, but browser storage failed: ' + e.message }); }
|
|
|
|
| 31 |
catch (e) { emit({ type: 'persistence_error', error: 'Workspace saving is unavailable: ' + e.message }); }
|
| 32 |
workspace = createWorkspace(saved?.files, saved?.entries);
|
| 33 |
await workspace.ready;
|
| 34 |
+
const upgradeDemo = saved && (saved.demoVersion ?? 0) < 1;
|
| 35 |
+
if (upgradeDemo) {
|
| 36 |
+
// Add this demo once to older sales workspaces. Existing files (including
|
| 37 |
+
// symlinks) win, and a later user deletion must survive subsequent reloads.
|
| 38 |
+
const paths = workspace.bash.fs.getAllPaths();
|
| 39 |
+
if (paths.includes('/workspace/sales.csv') && !paths.includes('/workspace/products.csv')) {
|
| 40 |
+
await workspace.write('/workspace/products.csv', initialFiles['/workspace/products.csv']);
|
| 41 |
+
}
|
| 42 |
+
}
|
| 43 |
agent = new Agent({ initialState: { model: localModel, thinkingLevel: 'medium', systemPrompt, tools: workspace.tools, messages: saved?.messages ?? [] },
|
| 44 |
streamFn: inference.streamFn, toolExecution: 'sequential',
|
| 45 |
afterToolCall: ({ result, isError }) => ({ isError: isError || (result.details?.exitCode !== undefined && result.details.exitCode !== 0) }),
|
|
|
|
| 53 |
emit({ type: 'agent_event', event });
|
| 54 |
if (event.type === 'tool_execution_end' || event.type === 'agent_end') await persist();
|
| 55 |
});
|
| 56 |
+
const state = await snapshot();
|
| 57 |
+
if (upgradeDemo) {
|
| 58 |
+
try { await storage?.save(state); }
|
| 59 |
+
catch (e) { emit({ type: 'persistence_error', error: 'Changes are in memory, but browser storage failed: ' + e.message }); }
|
| 60 |
+
}
|
| 61 |
+
emit({ type: 'initialized', ...state });
|
| 62 |
})();
|
| 63 |
|
| 64 |
onmessage = async ({ data }) => {
|
app/src/pi/empty-mascot.mjs
CHANGED
|
@@ -65,7 +65,7 @@ export function createEmptyMascot(element, onSelect) {
|
|
| 65 |
return {
|
| 66 |
hide,
|
| 67 |
get statusInset() { return mascot.hidden ? 0 : figure.offsetWidth + 20; },
|
| 68 |
-
update({ screen, rows, viewportRows, contentRows, draft, hasSales }) {
|
| 69 |
if (!screen || !rows) { hide(); return; }
|
| 70 |
const area = screen.getBoundingClientRect(), root = element.getBoundingClientRect();
|
| 71 |
const rowHeight = area.height / rows;
|
|
@@ -74,8 +74,12 @@ export function createEmptyMascot(element, onSelect) {
|
|
| 74 |
if (!cat.hasAttribute('src')) cat.src = new URL('../../public/empty-cat-v1.png', import.meta.url).href;
|
| 75 |
const withSuggestion = !draft.trim() && room >= 180;
|
| 76 |
suggestion.hidden = !withSuggestion;
|
| 77 |
-
suggestion.textContent = hasSales
|
| 78 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 79 |
suggestion.setAttribute('aria-label', `Use suggested prompt: ${prompt}`);
|
| 80 |
const size = Math.min(104, Math.floor(withSuggestion ? room - 76 : room));
|
| 81 |
figure.style.width = figure.style.height = `${size}px`;
|
|
|
|
| 65 |
return {
|
| 66 |
hide,
|
| 67 |
get statusInset() { return mascot.hidden ? 0 : figure.offsetWidth + 20; },
|
| 68 |
+
update({ screen, rows, viewportRows, contentRows, draft, hasSales, hasProducts }) {
|
| 69 |
if (!screen || !rows) { hide(); return; }
|
| 70 |
const area = screen.getBoundingClientRect(), root = element.getBoundingClientRect();
|
| 71 |
const rowHeight = area.height / rows;
|
|
|
|
| 74 |
if (!cat.hasAttribute('src')) cat.src = new URL('../../public/empty-cat-v1.png', import.meta.url).href;
|
| 75 |
const withSuggestion = !draft.trim() && room >= 180;
|
| 76 |
suggestion.hidden = !withSuggestion;
|
| 77 |
+
suggestion.textContent = hasSales
|
| 78 |
+
? (hasProducts ? 'Ask me to calculate our total profit.' : 'Ask me to calculate our total sales.')
|
| 79 |
+
: 'Ask me to explore your files.';
|
| 80 |
+
prompt = hasSales
|
| 81 |
+
? (hasProducts ? "What's our total sales profit?" : "What's our total sales revenue?")
|
| 82 |
+
: 'Explore the workspace files and summarize what is here.';
|
| 83 |
suggestion.setAttribute('aria-label', `Use suggested prompt: ${prompt}`);
|
| 84 |
const size = Math.min(104, Math.floor(withSuggestion ? room - 76 : room));
|
| 85 |
figure.style.width = figure.style.height = `${size}px`;
|
app/src/pi/terminal-ui.mjs
CHANGED
|
@@ -92,7 +92,7 @@ export function createTerminalUI(element, actions) {
|
|
| 92 |
if (!hydrated || messages.some(message => message.role === 'user') || state.busy || inputPending) mascot.hide();
|
| 93 |
else mascot.update({ screen, rows: xterm.rows,
|
| 94 |
viewportRows: scroll.viewportHeight, contentRows: transcript.render(xterm.cols).length,
|
| 95 |
-
draft: editor.getText(), hasSales: files.includes('sales.csv') });
|
| 96 |
// Keep the status text to the left of the cat's lower body.
|
| 97 |
const cellWidth = screen?.getBoundingClientRect().width / xterm.cols;
|
| 98 |
const reserved = cellWidth > 0 ? Math.ceil(mascot.statusInset / cellWidth) : 0;
|
|
|
|
| 92 |
if (!hydrated || messages.some(message => message.role === 'user') || state.busy || inputPending) mascot.hide();
|
| 93 |
else mascot.update({ screen, rows: xterm.rows,
|
| 94 |
viewportRows: scroll.viewportHeight, contentRows: transcript.render(xterm.cols).length,
|
| 95 |
+
draft: editor.getText(), hasSales: files.includes('sales.csv'), hasProducts: files.includes('products.csv') });
|
| 96 |
// Keep the status text to the left of the cat's lower body.
|
| 97 |
const cellWidth = screen?.getBoundingClientRect().width / xterm.cols;
|
| 98 |
const reserved = cellWidth > 0 ? Math.ceil(mascot.statusInset / cellWidth) : 0;
|
app/src/workspace.mjs
CHANGED
|
@@ -2,8 +2,9 @@ import { Bash } from 'just-bash/browser';
|
|
| 2 |
import { Type } from '@earendil-works/pi-ai';
|
| 3 |
|
| 4 |
export const initialFiles = {
|
| 5 |
-
'/workspace/README.md': '# Browser workspace\n\nPi can read, write, edit, and run shell commands here.\nEverything stays in this browser.
|
| 6 |
'/workspace/sales.csv': 'product,units,price\nNotebook,12,5\nPencil,30,2\nFolder,8,3\n',
|
|
|
|
| 7 |
};
|
| 8 |
const limitText = text => text.length > 6000 ? text.slice(0, 6000) + '\n[Output truncated at 6,000 characters; use head, grep, or a narrower query.]' : text;
|
| 9 |
const result = (text, details = {}) => ({ content: [{ type: 'text', text: limitText(text) }], details });
|
|
|
|
| 2 |
import { Type } from '@earendil-works/pi-ai';
|
| 3 |
|
| 4 |
export const initialFiles = {
|
| 5 |
+
'/workspace/README.md': '# Browser workspace\n\nPi can read, write, edit, and run shell commands here.\nEverything stays in this browser. Explore our sales, product costs, and stock levels.\n',
|
| 6 |
'/workspace/sales.csv': 'product,units,price\nNotebook,12,5\nPencil,30,2\nFolder,8,3\n',
|
| 7 |
+
'/workspace/products.csv': 'product,unit_cost,stock\nNotebook,2.50,24\nPencil,0.50,120\nFolder,1.20,18\nEraser,0.35,40\nStapler,2.75,9\nRuler,0.80,30\n',
|
| 8 |
};
|
| 9 |
const limitText = text => text.length > 6000 ? text.slice(0, 6000) + '\n[Output truncated at 6,000 characters; use head, grep, or a narrower query.]' : text;
|
| 10 |
const result = (text, details = {}) => ({ content: [{ type: 'text', text: limitText(text) }], details });
|
app/tests/mascot.mjs
CHANGED
|
@@ -36,7 +36,7 @@ try {
|
|
| 36 |
result.checks.push('Click and keyboard activation replay one hop without changing the prompt; reduced motion remains still');
|
| 37 |
await page.locator('.mascot-suggestion').click();
|
| 38 |
const prompt = await page.evaluate(() => window.browserPi.terminal.text);
|
| 39 |
-
assert.equal(prompt, "What's our total sales
|
| 40 |
assert(await page.locator('.xterm-helper-textarea').evaluate(el => el === document.activeElement));
|
| 41 |
assert.equal((await page.evaluate(() => window.browserPi.rpc('snapshot'))).messages.length, 0);
|
| 42 |
await page.waitForFunction(() => document.querySelector('.mascot-suggestion').hidden);
|
|
@@ -76,6 +76,8 @@ try {
|
|
| 76 |
result.checks.push('Seven viewports keep the cat on the input divider without covering the editor; short viewports prioritize the editor');
|
| 77 |
|
| 78 |
await page.setViewportSize({ width: 1440, height: 1000 }); await visible();
|
|
|
|
|
|
|
| 79 |
await page.evaluate(() => window.browserPi.rpc('shell', { command: 'rm /workspace/sales.csv' }));
|
| 80 |
await page.waitForFunction(() => document.querySelector('.mascot-suggestion').textContent.includes('explore'));
|
| 81 |
result.checks.push('Starter prompt follows actual workspace files');
|
|
|
|
| 36 |
result.checks.push('Click and keyboard activation replay one hop without changing the prompt; reduced motion remains still');
|
| 37 |
await page.locator('.mascot-suggestion').click();
|
| 38 |
const prompt = await page.evaluate(() => window.browserPi.terminal.text);
|
| 39 |
+
assert.equal(prompt, "What's our total sales profit?");
|
| 40 |
assert(await page.locator('.xterm-helper-textarea').evaluate(el => el === document.activeElement));
|
| 41 |
assert.equal((await page.evaluate(() => window.browserPi.rpc('snapshot'))).messages.length, 0);
|
| 42 |
await page.waitForFunction(() => document.querySelector('.mascot-suggestion').hidden);
|
|
|
|
| 76 |
result.checks.push('Seven viewports keep the cat on the input divider without covering the editor; short viewports prioritize the editor');
|
| 77 |
|
| 78 |
await page.setViewportSize({ width: 1440, height: 1000 }); await visible();
|
| 79 |
+
await page.evaluate(() => window.browserPi.rpc('shell', { command: 'rm /workspace/products.csv' }));
|
| 80 |
+
await page.waitForFunction(() => document.querySelector('.mascot-suggestion').textContent.includes('total sales'));
|
| 81 |
await page.evaluate(() => window.browserPi.rpc('shell', { command: 'rm /workspace/sales.csv' }));
|
| 82 |
await page.waitForFunction(() => document.querySelector('.mascot-suggestion').textContent.includes('explore'));
|
| 83 |
result.checks.push('Starter prompt follows actual workspace files');
|
app/tests/protocol.test.mjs
CHANGED
|
@@ -102,6 +102,8 @@ test('real just-bash executes pipelines and scripts; filesystem survives reload
|
|
| 102 |
const workspace = createWorkspace();
|
| 103 |
let result = await workspace.bash.exec("awk -F, 'NR>1 {sum += $2*$3} END {print sum}' sales.csv > total.txt; cat total.txt");
|
| 104 |
assert.equal(result.stdout.trim(), '144'); assert.equal(result.exitCode, 0);
|
|
|
|
|
|
|
| 105 |
await workspace.tools.find(t => t.name === 'write').execute('1', { path: 'hello.sh', content: 'printf "Hello from the browser\\n"' });
|
| 106 |
result = await workspace.bash.exec('bash hello.sh'); assert.equal(result.stdout.trim(), 'Hello from the browser');
|
| 107 |
await workspace.tools.find(t => t.name === 'edit').execute('2', { path: 'hello.sh', oldText: 'Hello', newText: '$& literal' });
|
|
|
|
| 102 |
const workspace = createWorkspace();
|
| 103 |
let result = await workspace.bash.exec("awk -F, 'NR>1 {sum += $2*$3} END {print sum}' sales.csv > total.txt; cat total.txt");
|
| 104 |
assert.equal(result.stdout.trim(), '144'); assert.equal(result.exitCode, 0);
|
| 105 |
+
result = await workspace.bash.exec("awk -F, 'NR==FNR {if (FNR>1) cost[$1]=$2; next} FNR>1 {if (!($1 in cost)) {exit 1}; profit += $2*($3-cost[$1])} END {printf \"%.2f\\n\", profit}' products.csv sales.csv");
|
| 106 |
+
assert.equal(result.stdout.trim(), '89.40'); assert.equal(result.exitCode, 0);
|
| 107 |
await workspace.tools.find(t => t.name === 'write').execute('1', { path: 'hello.sh', content: 'printf "Hello from the browser\\n"' });
|
| 108 |
result = await workspace.bash.exec('bash hello.sh'); assert.equal(result.stdout.trim(), 'Hello from the browser');
|
| 109 |
await workspace.tools.find(t => t.name === 'edit').execute('2', { path: 'hello.sh', oldText: 'Hello', newText: '$& literal' });
|
app/tests/starter-products.mjs
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { chromium } from 'playwright';
|
| 2 |
+
import assert from 'node:assert/strict';
|
| 3 |
+
import fs from 'node:fs/promises';
|
| 4 |
+
|
| 5 |
+
const url = process.argv[2] ?? 'http://127.0.0.1:4173/';
|
| 6 |
+
const label = process.argv[3] ?? 'agent-starter-products';
|
| 7 |
+
const browser = await chromium.launch({ executablePath: '/usr/bin/chromium', headless: true });
|
| 8 |
+
const page = await browser.newPage({ viewport: { width: 1440, height: 1000 } });
|
| 9 |
+
const result = { url, checks: [], errors: [] };
|
| 10 |
+
page.on('pageerror', error => result.errors.push(error.message));
|
| 11 |
+
const ready = async () => { await page.waitForFunction(() => window.browserPi); await page.evaluate(() => window.browserPi.ready); };
|
| 12 |
+
const rpc = (action, args) => page.evaluate(({ action, args }) => window.browserPi.rpc(action, args), { action, args });
|
| 13 |
+
async function restoreLegacy(state) {
|
| 14 |
+
delete state.demoVersion;
|
| 15 |
+
await page.evaluate(async state => {
|
| 16 |
+
const db = await new Promise((resolve, reject) => {
|
| 17 |
+
const request = indexedDB.open('minicpm5-pi-workspace', 1);
|
| 18 |
+
request.onsuccess = () => resolve(request.result); request.onerror = () => reject(request.error);
|
| 19 |
+
});
|
| 20 |
+
await new Promise((resolve, reject) => {
|
| 21 |
+
const tx = db.transaction('state', 'readwrite'); tx.objectStore('state').put(state, 'current');
|
| 22 |
+
tx.oncomplete = resolve; tx.onerror = () => reject(tx.error);
|
| 23 |
+
});
|
| 24 |
+
db.close();
|
| 25 |
+
}, state);
|
| 26 |
+
await page.reload(); await ready();
|
| 27 |
+
}
|
| 28 |
+
try {
|
| 29 |
+
await page.goto(url); await ready();
|
| 30 |
+
const fresh = await rpc('snapshot');
|
| 31 |
+
const products = fresh.files['/workspace/products.csv'];
|
| 32 |
+
assert.match(products, /^product,unit_cost,stock\n/);
|
| 33 |
+
assert.equal(products.trim().split('\n').length, 7);
|
| 34 |
+
await page.locator('.mascot-suggestion').waitFor({ state: 'visible' });
|
| 35 |
+
assert.equal(await page.locator('.mascot-suggestion').textContent(), 'Ask me to calculate our total profit.');
|
| 36 |
+
await page.getByRole('button', { name: 'products.csv', exact: true }).click();
|
| 37 |
+
assert.equal(await page.locator('#editor').inputValue(), products);
|
| 38 |
+
await page.screenshot({ path: `../validation/${label}.png`, fullPage: true });
|
| 39 |
+
result.checks.push('Fresh workspace has six products with unit costs and stock, and the same-length profit suggestion');
|
| 40 |
+
|
| 41 |
+
await rpc('shell', { command: "rm products.csv; printf 'Eraser,5,1.5\nStapler,12,4.99\nRuler,25,2.5\n' >> sales.csv; printf 'My notes\n' > notes.txt; mkdir empty; ln -s notes.txt link.txt" });
|
| 42 |
+
const legacy = await rpc('snapshot');
|
| 43 |
+
legacy.messages = [{ role: 'user', content: [{ type: 'text', text: 'Keep this conversation' }], timestamp: 1 }];
|
| 44 |
+
await restoreLegacy(legacy);
|
| 45 |
+
const upgraded = await rpc('snapshot');
|
| 46 |
+
assert.equal(upgraded.demoVersion, 1);
|
| 47 |
+
assert.deepEqual(upgraded.files, { ...legacy.files, '/workspace/products.csv': products });
|
| 48 |
+
assert.deepEqual(upgraded.messages, legacy.messages);
|
| 49 |
+
for (const entry of legacy.entries) assert.deepEqual(upgraded.entries.find(([path]) => path === entry[0]), entry);
|
| 50 |
+
const profit = await rpc('shell', { command: "awk -F, 'NR==FNR {if(FNR>1) cost[$1]=$2; next} FNR>1 {if(!($1 in cost)) exit 1; profit += $2*($3-cost[$1])} END {printf \"%.2f\\n\", profit}' products.csv sales.csv" });
|
| 51 |
+
assert.equal(profit.exitCode, 0); assert.equal(profit.stdout.trim(), '164.53');
|
| 52 |
+
result.checks.push('Older six-product sales data gains the companion file; existing files, entries, and chat remain intact; the join computes 164.53');
|
| 53 |
+
|
| 54 |
+
await rpc('shell', { command: 'rm products.csv' });
|
| 55 |
+
await page.reload(); await ready();
|
| 56 |
+
assert.equal((await rpc('snapshot')).files['/workspace/products.csv'], undefined);
|
| 57 |
+
await rpc('write', { path: '/workspace/products.csv', content: 'My own product data\n' });
|
| 58 |
+
await restoreLegacy(await rpc('snapshot'));
|
| 59 |
+
assert.equal((await rpc('snapshot')).files['/workspace/products.csv'], 'My own product data\n');
|
| 60 |
+
await restoreLegacy({ version: 1, files: { '/workspace/custom.txt': 'Custom workspace' }, messages: [] });
|
| 61 |
+
assert.deepEqual((await rpc('snapshot')).files, { '/workspace/custom.txt': 'Custom workspace' });
|
| 62 |
+
result.checks.push('The addition happens once: deleted files stay deleted, custom products are retained, and unrelated workspaces get no demo files');
|
| 63 |
+
assert.deepEqual(result.errors, []); result.passed = true;
|
| 64 |
+
console.log(JSON.stringify(result, null, 2));
|
| 65 |
+
} catch (error) { result.failure = error.stack; throw error; }
|
| 66 |
+
finally { await fs.writeFile(`../validation/${label}.json`, JSON.stringify(result, null, 2)); await browser.close(); }
|
assets/{agent.worker-CG915rYx.js → agent.worker-L7ew_XyL.js}
RENAMED
|
The diff for this file is too large to render.
See raw diff
|
|
|
assets/{index-DifYXNCD.js → index-8TcpvU1k.js}
RENAMED
|
@@ -130,8 +130,8 @@ Please report this to https://github.com/markedjs/marked.`,e){let e=`<p>An error
|
|
| 130 |
`).replace(/[\r\u0085\u2028\u2029]/g,`
|
| 131 |
`)}function h(e){if(e||={},e.locator===void 0&&(e.locator=!0),this.assign=e.assign||t.assign,this.domHandler=e.domHandler||g,this.onError=e.onError||e.errorHandler,e.errorHandler&&typeof e.errorHandler!=`function`)throw TypeError(`errorHandler object is no longer supported, switch to onError!`);e.errorHandler&&e.errorHandler(`warning`,"The `errorHandler` option has been deprecated, use `onError` instead!",this),this.normalizeLineEndings=e.normalizeLineEndings||m,this.locator=!!e.locator,this.xmlns=this.assign(Object.create(null),e.xmlns)}h.prototype.parseFromString=function(e,n){if(!l(n))throw TypeError(`DOMParser.parseFromString: the provided mimeType "`+n+`" is not valid.`);var r=this.assign(Object.create(null),this.xmlns),a=i.XML_ENTITIES,o=r[``]||null;s(n)?(a=i.HTML_ENTITIES,o=d.HTML):n===u.XML_SVG_IMAGE&&(o=d.SVG),r[``]=o,r.xml=r.xml||d.XML;var c=new this.domHandler({mimeType:n,defaultNamespace:o,onError:this.onError}),f=this.locator?{}:void 0;this.locator&&c.setDocumentLocator(f);var m=new p;return m.errorHandler=c,m.domBuilder=c,!t.isHTMLMimeType(n)&&typeof e!=`string`&&m.errorHandler.fatalError(`source is not a string`),m.parse(this.normalizeLineEndings(String(e)),r,a),c.doc.documentElement||m.errorHandler.fatalError(`missing root element`),c.doc};function g(e){var t=e||{};this.mimeType=t.mimeType||u.XML_APPLICATION,this.defaultNamespace=t.defaultNamespace||null,this.cdata=!1,this.currentElement=void 0,this.doc=void 0,this.locator=void 0,this.onError=t.onError}function _(e,t){t.lineNumber=e.lineNumber,t.columnNumber=e.columnNumber}g.prototype={startDocument:function(){var e=new o;this.doc=c(this.mimeType)?e.createHTMLDocument(!1):e.createDocument(this.defaultNamespace,``)},startElement:function(e,t,n,r){var i=this.doc,a=i.createElementNS(e,n||t),o=r.length;b(this,a),this.currentElement=a,this.locator&&_(this.locator,a);for(var s=0;s<o;s++){var e=r.getURI(s),c=r.getValue(s),n=r.getQName(s),l=i.createAttributeNS(e,n);this.locator&&_(r.getLocator(s),l),l.value=l.nodeValue=c,a.setAttributeNode(l)}},endElement:function(e,t,n){this.currentElement=this.currentElement.parentNode},startPrefixMapping:function(e,t){},endPrefixMapping:function(e){},processingInstruction:function(e,t){var n=this.doc.createProcessingInstruction(e,t);this.locator&&_(this.locator,n),b(this,n)},ignorableWhitespace:function(e,t,n){},characters:function(e,t,n){if(e=y.apply(this,arguments),e){if(this.cdata)var r=this.doc.createCDATASection(e);else var r=this.doc.createTextNode(e);this.currentElement?this.currentElement.appendChild(r):/^\s*$/.test(e)&&this.doc.appendChild(r),this.locator&&_(this.locator,r)}},skippedEntity:function(e){},endDocument:function(){this.doc.normalize()},setDocumentLocator:function(e){e&&(e.lineNumber=0),this.locator=e},comment:function(e,t,n){e=y.apply(this,arguments);var r=this.doc.createComment(e);this.locator&&_(this.locator,r),b(this,r)},startCDATA:function(){this.cdata=!0},endCDATA:function(){this.cdata=!1},startDTD:function(e,t,n,r){var i=this.doc.implementation;if(i&&i.createDocumentType){var a=i.createDocumentType(e,t,n,r);this.locator&&_(this.locator,a),b(this,a),this.doc.doctype=a}},reportError:function(e,t){if(typeof this.onError==`function`)try{this.onError(e,t,this)}catch(n){throw new f(`Reporting `+e+` "`+t+`" caused `+n,this.locator)}else console.error(`[xmldom `+e+`] `+t,v(this.locator))},warning:function(e){this.reportError(`warning`,e)},error:function(e){this.reportError(`error`,e)},fatalError:function(e,t){throw this.reportError(`fatalError`,e),new f(e,this.locator,t)}};function v(e){if(e)return`
|
| 132 |
@#[line:`+e.lineNumber+`,col:`+e.columnNumber+`]`}function y(e,t,n){return typeof e==`string`?e.substr(t,n):e.length>=t+n||t?new java.lang.String(e,t,n)+``:e}`endDTD,startEntity,endEntity,attributeDecl,elementDecl,externalEntityDecl,internalEntityDecl,resolveEntity,getExternalSubset,notationDecl,unparsedEntityDecl`.replace(/\w+/g,function(e){g.prototype[e]=function(){return null}});function b(e,t){e.currentElement?e.currentElement.appendChild(t):e.doc.appendChild(t)}function x(e){if(e===`error`)throw`onErrorStopParsing`}function S(){throw`onWarningStopParsing`}e.__DOMHandler=g,e.DOMParser=h,e.normalizeLineEndings=m,e.onErrorStopParsing=x,e.onWarningStopParsing=S}));o((e=>{var t=Cg();e.assign=t.assign,e.hasDefaultHTMLNamespace=t.hasDefaultHTMLNamespace,e.isHTMLMimeType=t.isHTMLMimeType,e.isValidMimeType=t.isValidMimeType,e.MIME_TYPE=t.MIME_TYPE,e.NAMESPACE=t.NAMESPACE;var n=wg();e.DOMException=n.DOMException,e.DOMExceptionName=n.DOMExceptionName,e.ExceptionCode=n.ExceptionCode,e.ParseError=n.ParseError;var r=Eg();e.Attr=r.Attr,e.CDATASection=r.CDATASection,e.CharacterData=r.CharacterData,e.Comment=r.Comment,e.Document=r.Document,e.DocumentFragment=r.DocumentFragment,e.DocumentType=r.DocumentType,e.DOMImplementation=r.DOMImplementation,e.Element=r.Element,e.Entity=r.Entity,e.EntityReference=r.EntityReference,e.LiveNodeList=r.LiveNodeList,e.NamedNodeMap=r.NamedNodeMap,e.Node=r.Node,e.NodeList=r.NodeList,e.Notation=r.Notation,e.ProcessingInstruction=r.ProcessingInstruction,e.Text=r.Text,e.XMLSerializer=r.XMLSerializer;var i=kg();e.DOMParser=i.DOMParser,e.normalizeLineEndings=i.normalizeLineEndings,e.onErrorStopParsing=i.onErrorStopParsing,e.onWarningStopParsing=i.onWarningStopParsing}))();var Ag=e=>typeof e==`string`?e:(e??[]).filter(e=>e.type===`text`).map(e=>e.text).join(`
|
| 133 |
-
`),jg=8192;function Mg(e){let t=e.findLast(e=>e.role===`assistant`&&e.usage?.input>0);return t?{inputTokens:t.usage.input,outputTokens:t.usage.output??0}:e.length?null:{inputTokens:0,outputTokens:0}}function Ng(e){if(!e)return`—/${jg.toLocaleString(`en-US`)} context`;let t=e.inputTokens+e.outputTokens,n=Math.max(0,jg-t);return`${t.toLocaleString(`en-US`)}/${jg.toLocaleString(`en-US`)} context · ${n.toLocaleString(`en-US`)} free`}function Pg(e,t){let n=document.createElement(`canvas`).getContext(`2d`),r=document.createElement(`span`);r.style.cssText=`display:inline-block;width:0;height:0;padding:0;border:0;vertical-align:baseline`;let i=document.createElement(`div`);i.className=`empty-mascot`,i.hidden=!0,i.style.visibility=`hidden`;let a=document.createElement(`button`);a.type=`button`,a.className=`mascot-suggestion`;let o=document.createElement(`button`);o.type=`button`,o.className=`mascot-cat`,o.setAttribute(`aria-label`,`Make the cat hop`);let s=document.createElement(`img`);s.width=s.height=104,s.alt=``,s.draggable=!1;let c=document.createElement(`span`);c.className=`mascot-eyelids`,c.setAttribute(`aria-hidden`,`true`),o.append(s,c),i.append(a,o),e.append(i);let l=``,u=!1,d=!1,f=0,p=0,m=new Set,h=matchMedia(`(prefers-reduced-motion: reduce)`);function g(e,t,n){for(let e of m)e.id===n.id&&(e.cancel(),m.delete(e));let r=e.animate(t,n);m.add(r),r.onfinish=()=>m.delete(r)}function _(){clearTimeout(f),f=0,!(i.hidden||document.hidden||h.matches||!u)&&(d=!0,g(o,[{transform:`translateY(0)`,easing:`ease-out`},{transform:`translateY(-4px)`,offset:.4,easing:`ease-in`},{transform:`translateY(0)`}],{id:`mascot-bump`,duration:420}))}o.onclick=_;function v(){clearTimeout(f),clearTimeout(p),f=p=0;for(let e of m)e.cancel();m.clear()}function y(){if(i.hidden||document.hidden||h.matches||!u){v();return}!d&&!f&&(f=setTimeout(_,1800)),p||=setTimeout(()=>{p=0,g(c,[{opacity:0,easing:`steps(1, end)`},{opacity:1,offset:.15},{opacity:1,offset:.8,easing:`steps(1, end)`},{opacity:0}],{id:`mascot-blink`,duration:180}),y()},5e3+Math.random()*4e3)}function b(){i.hidden=!0,v()}async function x(){try{await s.decode()}catch{return}i.isConnected&&(u=!0,i.style.visibility=``,y())}return s.addEventListener(`load`,x),document.addEventListener(`visibilitychange`,y),h.addEventListener(`change`,y),a.onclick=()=>t(l),{hide:b,get statusInset(){return i.hidden?0:o.offsetWidth+20},update({screen:t,rows:c,viewportRows:u,contentRows:d,draft:f,hasSales:p}){if(!t||!c){b();return}let
|
| 134 |
-
`).replace(/[\x00-\x08\x0b-\x1f\x7f-\x9f]/g,``),Ig=[{value:`/help`,label:`/help`,description:`Browser terminal commands`},{value:`/load`,label:`/load`,description:`Load MiniCPM on this device`},{value:`/new`,label:`/new`,description:`Start a new conversation; keep files`},{value:`/clear`,label:`/clear`,description:`Same as /new`}];function Lg(e,t){let n=new vs({fontFamily:`ui-monospace, SFMono-Regular, Menlo, Consolas, monospace`,fontSize:13,lineHeight:1.3,theme:pg,cursorBlink:!1,scrollback:0,screenReaderMode:!0,allowProposedApi:!0});n.loadAddon(new bl);let r=document.createElement(`div`);r.className=`pi-terminal-mount`,e.append(r);let i=new xs;n.loadAddon(i),n.open(r);let a=new Sg(n),o=new Qf(a,!0,void 0,{copyOnSelect:!1,copySelection:async e=>{try{return await navigator.clipboard.writeText(e),!0}catch{return!1}},openUrl:e=>{/^https?:\/\//i.test(e)&&window.open(e,`_blank`,`noopener,noreferrer`)},scrollToEndIndicator:()=>Q.fg(`accent`,` ↓ Latest `),searchMatchStyle:e=>Q.bg(`selectedBg`,Q.fg(`text`,e)),searchCurrentMatchStyle:e=>Q.bg(`accent`,Q.fg(`userMessageBg`,Q.bold(e)))}),s=new ru({...tu,"app.interrupt":{defaultKeys:[`escape`,`ctrl+c`]},"app.exit":{defaultKeys:`ctrl+d`},"app.tools.expand":{defaultKeys:`ctrl+o`},"tui.editor.historyPrevious":{defaultKeys:`ctrl+p`},"tui.editor.historyNext":{defaultKeys:`ctrl+n`}});au(s);let c=new lg(o,hg(),s,{paddingX:0}),l=new Pd,u=new af(l,{follow:`end`,primary:!0,scrollbar:`auto`,scrollbarTrackStyle:e=>Q.fg(`borderMuted`,e),scrollbarThumbStyle:e=>Q.fg(`muted`,e)}),d=new xp(``,0,0),f=0,p=d.render.bind(d);d.render=e=>{let t=Math.min(f,Math.max(0,e-1));return p(e-t).map(e=>e+` `.repeat(t))},o.setLayoutRoot(new Jh([{component:u,basis:0,grow:1,minSize:0},{component:new Jh([d,c,{invalidate(){},render(e){let t=`/workspace · MiniCPM5-2B`,n=Ng(m.contextUsage),r=`${t} · ${n}`;return(r.length<=e-2?[r]:[t,n]).flatMap(t=>new xp(Q.fg(`dim`,t),1,0).render(e))}}]),basis:`auto`,shrink:0}])),o.setFocus(c);let m={loaded:!1,busy:!1,activity:``,contextUsage:Mg([])},h=[],g=[],_=!1,v=!1,y=!1,b=Pg(e,e=>{m.busy||v||c.getText().trim()||(c.setText(e),n.focus(),o.requestRender())});function x(){let e=r.querySelector(`.xterm-screen`);!y||g.some(e=>e.role===`user`)||m.busy||v?b.hide():b.update({screen:e,rows:n.rows,viewportRows:u.viewportHeight,contentRows:l.render(n.cols).length,draft:c.getText(),hasSales:h.includes(`sales.csv`)});let t=e?.getBoundingClientRect().width/n.cols,i=t>0?Math.ceil(b.statusInset/t):0;i!==f&&(f=i,o.requestRender())}let S=n.onRender(x),C=new Map,w=[],T=new Set,E=new Pd;E.addChild(new bp(Q.fg(`accent`,Q.bold(`π Pi`))+Q.fg(`muted`,` browser workspace`),1,1)),E.addChild(new bp(`Ask Pi to work with the files on the right.
|
| 135 |
|
| 136 |
/load load MiniCPM
|
| 137 |
/help show commands
|
|
@@ -156,4 +156,4 @@ Node CLI extensions, npm, Python and native binaries are unavailable.`);else if(
|
|
| 156 |
`)),r.style.height=`auto`,r.style.height=Math.min(104,Math.max(32,r.scrollHeight))+`px`,i.scrollTop=r.scrollTop,i.scrollLeft=r.scrollLeft,v()}function b(e){r.value=e,y(),r.setSelectionRange(e.length,e.length)}function x(){m||(d.length=0,a.replaceChildren(l.cloneNode(!0)),v())}function S(e,t,n){if(!t)return;let r=document.createElement(`pre`);r.className=n,r.textContent=Hg(t),e.append(r)}o.onsubmit=async e=>{e.preventDefault();let n=r.value;if(m||h||!n.trim())return;m=!0,u.at(-1)!==n&&u.push(n),u.length>50&&u.shift(),f=u.length,p=``,b(``),r.focus(),d.length||a.replaceChildren();let i=document.createElement(`section`);i.className=`shell-entry`;let o=document.createElement(`div`);o.className=`shell-entry-heading`;let s=document.createElement(`span`);s.className=`shell-prompt`,s.textContent=`$`;let c=document.createElement(`pre`);c.className=`shell-code`,c.append(Ug(Hg(n)));let l=document.createElement(`span`);l.className=`shell-status`,l.textContent=`Running…`,o.append(s,c,l),i.append(o),a.append(i),d.push(i),_();try{let e=await t(n),r=g();S(i,e.stdout,`shell-stdout`),S(i,e.stderr,`shell-stderr`),l.classList.add(e.exitCode===0?`success`:`error`),l.textContent=`[exit ${e.exitCode}]`,r&&_()}catch(e){let t=g();S(i,e.message,`shell-stderr`),l.classList.add(`error`),l.textContent=`[error]`,r.value||b(n),t&&_()}finally{for(;d.length>1&&(d.length>40||a.textContent.length>48e3);)d.shift().remove();m=!1,v()}},r.addEventListener(`input`,()=>{f=u.length,p=r.value,y()}),r.addEventListener(`scroll`,()=>{i.scrollTop=r.scrollTop,i.scrollLeft=r.scrollLeft}),r.addEventListener(`keydown`,e=>{if(e.isComposing||e.keyCode===229)return;if(e.key===`Enter`&&!e.shiftKey){e.preventDefault(),o.requestSubmit();return}if(e.ctrlKey&&e.key.toLowerCase()===`l`){e.preventDefault(),x();return}if(e.ctrlKey||e.altKey||e.metaKey||e.shiftKey||r.selectionStart!==r.selectionEnd)return;let t=!r.value.slice(0,r.selectionStart).includes(`
|
| 157 |
`),n=!r.value.slice(r.selectionEnd).includes(`
|
| 158 |
`);e.key===`ArrowUp`&&t&&f>0?(e.preventDefault(),f===u.length&&(p=r.value),b(u[--f])):e.key===`ArrowDown`&&n&&f<u.length&&(e.preventDefault(),b(++f===u.length?p:u[f]))}),c.onclick=()=>{x(),r.focus()};let C=0;return new ResizeObserver(([e])=>{e.contentRect.width!==C&&(C=e.contentRect.width,y())}).observe(r.parentElement),y(),{setBusy(e){h=e,v()}}}function Gg(e){let t=e.querySelector(`.runtime-value`),n=e.querySelector(`.runtime-sparkline`),r=e.querySelector(`.runtime-bars`),i=e.querySelector(`[role="status"]`),a=e.querySelector(`.runtime-engine`),o=e.querySelector(`.runtime-content`);new ResizeObserver(([t])=>{e.style.width=`calc(${Math.ceil(t.contentRect.width)}px + 2 * var(--runtime-padding))`}).observe(o);let s=!1,c,l=[],u;function d(o,s,d){clearTimeout(u),o!==c&&(i.textContent=d??`${s} in this browser.`),c=o,a.textContent=c===`tool`?`JavaScript`:`WebGPU`,e.dataset.state=c,t.textContent=s,n.setAttribute(`hidden`,``),e.title=d??`${s} in this browser.`,c!==`generating`&&(l=[],r.replaceChildren())}function f(){d(`idle`,s?`Ready`:`Idle`,s?`Model ready. Inference and tools run in this browser.`:`Model loads when you send a message. Inference and tools run in this browser.`)}function p(e){if(!Number.isFinite(e)||e<0){d(`generating`,`Generating…`);return}d(`generating`,`${e<10?e.toFixed(1):Math.round(e)} tok/s`,`Live token generation speed, including thinking tokens. Prompt-processing time is excluded.`),l.push(e),l.length>12&&l.shift();let t=Math.max(40,...l),i=[...Array(12-l.length).fill(null),...l];r.replaceChildren(...i.map((e,n)=>{if(e===null||e===0){let t=document.createElementNS(`http://www.w3.org/2000/svg`,`circle`);return t.setAttribute(`cx`,String(n*4+1)),t.setAttribute(`cy`,`17`),t.setAttribute(`r`,`1`),t.setAttribute(`opacity`,e===null?`.35`:`.6`),t}let r=document.createElementNS(`http://www.w3.org/2000/svg`,`rect`),i=Math.max(2,Math.min(16,e/t*16));return r.setAttribute(`x`,String(n*4)),r.setAttribute(`y`,(18-i).toFixed(2)),r.setAttribute(`width`,`2`),r.setAttribute(`height`,i.toFixed(2)),r.setAttribute(`rx`,`1`),r})),n.removeAttribute(`hidden`),u=setTimeout(()=>{d(`generating`,`Generating…`),l=[],r.replaceChildren()},2e3)}return f(),{update(e){e.type===`loaded`?(s=!0,f()):e.type===`idle`||e.type===`initialized`?f():e.type===`fatal`?(s=!1,d(`error`,`Unavailable`)):e.type===`busy`?e.action===`load`?d(`loading`,`Loading…`,e.cachedOnly?`Loading model from this browser…`:void 0):e.action===`prompt`?d(`prefill`,`Reading prompt…`):e.action===`shell`?d(`tool`,`Running bash…`):e.action===`read`?d(`tool`,`Reading file…`):e.action===`write`?d(`tool`,`Writing file…`):d(`working`,`Working…`):e.type===`load_progress`?d(`loading`,e.phase===`download`&&e.loaded<e.total?`Downloading ${Math.round(e.loaded/e.total*100)}%`:e.phase===`warmup`?`Warming up…`:`Preparing…`):e.type===`inference_activity`?e.phase===`prefill`?d(`prefill`,`Reading prompt…`):e.phase===`decode`?p(e.rate):(c===`generating`||c===`prefill`)&&d(`working`,`Working…`):e.type===`agent_event`&&e.event.type===`tool_execution_start`&&d(`tool`,{bash:`Running bash…`,read:`Reading file…`,write:`Writing file…`,edit:`Editing file…`}[e.event.toolName]??`Running tool…`)}}}var Kg={default:`#b0b0bd`,markdown:`#519aba`,csv:`#8dc149`,json:`#cbcb41`,javascript:`#cbcb41`,typescript:`#519aba`,react:`#519aba`,html:`#e37933`,css:`#519aba`,python:`#519aba`,shell:`#8dc149`,config:`#b0b0bd`,image:`#a074c4`,svg:`#a074c4`,git:`#e37933`,pdf:`#cc3e44`,zip:`#b0b0bd`,db:`#cbcb41`},qg={md:`markdown`,markdown:`markdown`,mdx:`markdown`,csv:`csv`,tsv:`csv`,json:`json`,jsonc:`json`,jsonl:`json`,js:`javascript`,mjs:`javascript`,cjs:`javascript`,ts:`typescript`,mts:`typescript`,cts:`typescript`,jsx:`react`,tsx:`react`,html:`html`,htm:`html`,xml:`html`,css:`css`,scss:`css`,sass:`css`,less:`css`,py:`python`,pyi:`python`,sh:`shell`,bash:`shell`,zsh:`shell`,fish:`shell`,yaml:`config`,yml:`config`,toml:`config`,ini:`config`,conf:`config`,env:`config`,png:`image`,jpg:`image`,jpeg:`image`,gif:`image`,webp:`image`,avif:`image`,ico:`image`,svg:`svg`,pdf:`pdf`,zip:`zip`,gz:`zip`,tar:`zip`,sql:`db`,sqlite:`db`,db:`db`};function Jg(e){let t=e.split(`/`).at(-1).toLowerCase();return[`.gitignore`,`.gitattributes`,`.gitmodules`].includes(t)?`git`:t===`.env`||t.startsWith(`.env.`)||t===`.editorconfig`?`config`:qg[t.split(`.`).at(-1)]??`default`}function Yg(e){let t=Jg(e),n=document.createElement(`span`);return n.className=`file-icon`,n.dataset.kind=t,n.setAttribute(`aria-hidden`,`true`),n.style.setProperty(`--file-color`,Kg[t]),n.style.setProperty(`--file-icon`,`url("${new URL(`file-icons/${t}.svg`,document.baseURI).href}")`),n}var Xg={html:`markup`,htm:`markup`,xml:`markup`,svg:`markup`,vue:`markup`,css:`css`,js:`javascript`,mjs:`javascript`,cjs:`javascript`,jsx:`jsx`,ts:`typescript`,mts:`typescript`,cts:`typescript`,tsx:`tsx`,json:`json`,ipynb:`json`,md:`markdown`,markdown:`markdown`,py:`python`,pyw:`python`,sh:`bash`,bash:`bash`,zsh:`bash`,yml:`yaml`,yaml:`yaml`,toml:`toml`,sql:`sql`,csv:`csv`,c:`c`,h:`c`,cpp:`cpp`,cc:`cpp`,cxx:`cpp`,hpp:`cpp`,cs:`csharp`,java:`java`,go:`go`,rs:`rust`,diff:`diff`,patch:`diff`};function Zg(e=``){let t=e.split(`/`).at(-1).toLowerCase();return/^dockerfile(?:\.|$)/.test(t)?`docker`:/^\.(?:bashrc|bash_profile|zshrc|profile)$/.test(t)?`bash`:Xg[t.split(`.`).at(-1)]??null}function Qg(e){if(e.length>2e5)return!1;let t=1;for(let n=0;n<e.length;n++)if(e.charCodeAt(n)===10&&++t>5e3)return!1;return!0}function $g(e){let t=e.querySelector(`#editor`),n=e.querySelector(`#editor-highlight`),r=``,i=0,a,o,s,c=!1,l=!1,u;function d(){n.style.width=t.clientWidth+`px`,n.style.height=t.clientHeight+`px`,n.scrollTop=t.scrollTop,n.scrollLeft=t.scrollLeft}function f(){e.classList.remove(`is-highlighted`)}function p(){clearTimeout(o),s?.terminate(),s=void 0,c=!1}function m(){if(c||!u)return;let t=u;u=void 0;try{s||(s=new Worker(new URL(new URL(`editor-highlight.worker-oPpFVFqT.js`,import.meta.url).href,``+import.meta.url),{type:`module`}),s.onmessage=({data:t})=>{clearTimeout(o),c=!1,t.id===i&&t.html!==null&&(n.innerHTML=t.html+`
|
| 159 |
-
`,d(),e.classList.add(`is-highlighted`)),m()},s.onerror=()=>{p(),u=void 0}),c=!0,s.postMessage(t),o=setTimeout(()=>{p(),m()},1500)}catch{p()}}function h(n=0){i++,clearTimeout(a),u=void 0,f();let o=Zg(r),s=t.value;if(e.dataset.language=o??`plain`,l||!s||!o||!Qg(s))return;let c=i;a=setTimeout(()=>{u={id:c,text:s,language:o},m()},n)}t.addEventListener(`input`,()=>h(80)),t.addEventListener(`compositionstart`,()=>{l=!0,h()}),t.addEventListener(`compositionend`,()=>{l=!1,h(80)}),t.addEventListener(`scroll`,d,{passive:!0});let g=new ResizeObserver(d);return g.observe(t),{setFile(e,n){if(r===e&&t.value===n)return;let i=r!==e;r=e,t.value!==n&&(t.value=n),i&&(t.scrollTop=0,t.scrollLeft=0),h()},dispose(){clearTimeout(a),p(),g.disconnect()}}}var $=e=>document.querySelector(e),e_=new Worker(new URL(new URL(`agent.worker-
|
|
|
|
| 130 |
`).replace(/[\r\u0085\u2028\u2029]/g,`
|
| 131 |
`)}function h(e){if(e||={},e.locator===void 0&&(e.locator=!0),this.assign=e.assign||t.assign,this.domHandler=e.domHandler||g,this.onError=e.onError||e.errorHandler,e.errorHandler&&typeof e.errorHandler!=`function`)throw TypeError(`errorHandler object is no longer supported, switch to onError!`);e.errorHandler&&e.errorHandler(`warning`,"The `errorHandler` option has been deprecated, use `onError` instead!",this),this.normalizeLineEndings=e.normalizeLineEndings||m,this.locator=!!e.locator,this.xmlns=this.assign(Object.create(null),e.xmlns)}h.prototype.parseFromString=function(e,n){if(!l(n))throw TypeError(`DOMParser.parseFromString: the provided mimeType "`+n+`" is not valid.`);var r=this.assign(Object.create(null),this.xmlns),a=i.XML_ENTITIES,o=r[``]||null;s(n)?(a=i.HTML_ENTITIES,o=d.HTML):n===u.XML_SVG_IMAGE&&(o=d.SVG),r[``]=o,r.xml=r.xml||d.XML;var c=new this.domHandler({mimeType:n,defaultNamespace:o,onError:this.onError}),f=this.locator?{}:void 0;this.locator&&c.setDocumentLocator(f);var m=new p;return m.errorHandler=c,m.domBuilder=c,!t.isHTMLMimeType(n)&&typeof e!=`string`&&m.errorHandler.fatalError(`source is not a string`),m.parse(this.normalizeLineEndings(String(e)),r,a),c.doc.documentElement||m.errorHandler.fatalError(`missing root element`),c.doc};function g(e){var t=e||{};this.mimeType=t.mimeType||u.XML_APPLICATION,this.defaultNamespace=t.defaultNamespace||null,this.cdata=!1,this.currentElement=void 0,this.doc=void 0,this.locator=void 0,this.onError=t.onError}function _(e,t){t.lineNumber=e.lineNumber,t.columnNumber=e.columnNumber}g.prototype={startDocument:function(){var e=new o;this.doc=c(this.mimeType)?e.createHTMLDocument(!1):e.createDocument(this.defaultNamespace,``)},startElement:function(e,t,n,r){var i=this.doc,a=i.createElementNS(e,n||t),o=r.length;b(this,a),this.currentElement=a,this.locator&&_(this.locator,a);for(var s=0;s<o;s++){var e=r.getURI(s),c=r.getValue(s),n=r.getQName(s),l=i.createAttributeNS(e,n);this.locator&&_(r.getLocator(s),l),l.value=l.nodeValue=c,a.setAttributeNode(l)}},endElement:function(e,t,n){this.currentElement=this.currentElement.parentNode},startPrefixMapping:function(e,t){},endPrefixMapping:function(e){},processingInstruction:function(e,t){var n=this.doc.createProcessingInstruction(e,t);this.locator&&_(this.locator,n),b(this,n)},ignorableWhitespace:function(e,t,n){},characters:function(e,t,n){if(e=y.apply(this,arguments),e){if(this.cdata)var r=this.doc.createCDATASection(e);else var r=this.doc.createTextNode(e);this.currentElement?this.currentElement.appendChild(r):/^\s*$/.test(e)&&this.doc.appendChild(r),this.locator&&_(this.locator,r)}},skippedEntity:function(e){},endDocument:function(){this.doc.normalize()},setDocumentLocator:function(e){e&&(e.lineNumber=0),this.locator=e},comment:function(e,t,n){e=y.apply(this,arguments);var r=this.doc.createComment(e);this.locator&&_(this.locator,r),b(this,r)},startCDATA:function(){this.cdata=!0},endCDATA:function(){this.cdata=!1},startDTD:function(e,t,n,r){var i=this.doc.implementation;if(i&&i.createDocumentType){var a=i.createDocumentType(e,t,n,r);this.locator&&_(this.locator,a),b(this,a),this.doc.doctype=a}},reportError:function(e,t){if(typeof this.onError==`function`)try{this.onError(e,t,this)}catch(n){throw new f(`Reporting `+e+` "`+t+`" caused `+n,this.locator)}else console.error(`[xmldom `+e+`] `+t,v(this.locator))},warning:function(e){this.reportError(`warning`,e)},error:function(e){this.reportError(`error`,e)},fatalError:function(e,t){throw this.reportError(`fatalError`,e),new f(e,this.locator,t)}};function v(e){if(e)return`
|
| 132 |
@#[line:`+e.lineNumber+`,col:`+e.columnNumber+`]`}function y(e,t,n){return typeof e==`string`?e.substr(t,n):e.length>=t+n||t?new java.lang.String(e,t,n)+``:e}`endDTD,startEntity,endEntity,attributeDecl,elementDecl,externalEntityDecl,internalEntityDecl,resolveEntity,getExternalSubset,notationDecl,unparsedEntityDecl`.replace(/\w+/g,function(e){g.prototype[e]=function(){return null}});function b(e,t){e.currentElement?e.currentElement.appendChild(t):e.doc.appendChild(t)}function x(e){if(e===`error`)throw`onErrorStopParsing`}function S(){throw`onWarningStopParsing`}e.__DOMHandler=g,e.DOMParser=h,e.normalizeLineEndings=m,e.onErrorStopParsing=x,e.onWarningStopParsing=S}));o((e=>{var t=Cg();e.assign=t.assign,e.hasDefaultHTMLNamespace=t.hasDefaultHTMLNamespace,e.isHTMLMimeType=t.isHTMLMimeType,e.isValidMimeType=t.isValidMimeType,e.MIME_TYPE=t.MIME_TYPE,e.NAMESPACE=t.NAMESPACE;var n=wg();e.DOMException=n.DOMException,e.DOMExceptionName=n.DOMExceptionName,e.ExceptionCode=n.ExceptionCode,e.ParseError=n.ParseError;var r=Eg();e.Attr=r.Attr,e.CDATASection=r.CDATASection,e.CharacterData=r.CharacterData,e.Comment=r.Comment,e.Document=r.Document,e.DocumentFragment=r.DocumentFragment,e.DocumentType=r.DocumentType,e.DOMImplementation=r.DOMImplementation,e.Element=r.Element,e.Entity=r.Entity,e.EntityReference=r.EntityReference,e.LiveNodeList=r.LiveNodeList,e.NamedNodeMap=r.NamedNodeMap,e.Node=r.Node,e.NodeList=r.NodeList,e.Notation=r.Notation,e.ProcessingInstruction=r.ProcessingInstruction,e.Text=r.Text,e.XMLSerializer=r.XMLSerializer;var i=kg();e.DOMParser=i.DOMParser,e.normalizeLineEndings=i.normalizeLineEndings,e.onErrorStopParsing=i.onErrorStopParsing,e.onWarningStopParsing=i.onWarningStopParsing}))();var Ag=e=>typeof e==`string`?e:(e??[]).filter(e=>e.type===`text`).map(e=>e.text).join(`
|
| 133 |
+
`),jg=8192;function Mg(e){let t=e.findLast(e=>e.role===`assistant`&&e.usage?.input>0);return t?{inputTokens:t.usage.input,outputTokens:t.usage.output??0}:e.length?null:{inputTokens:0,outputTokens:0}}function Ng(e){if(!e)return`—/${jg.toLocaleString(`en-US`)} context`;let t=e.inputTokens+e.outputTokens,n=Math.max(0,jg-t);return`${t.toLocaleString(`en-US`)}/${jg.toLocaleString(`en-US`)} context · ${n.toLocaleString(`en-US`)} free`}function Pg(e,t){let n=document.createElement(`canvas`).getContext(`2d`),r=document.createElement(`span`);r.style.cssText=`display:inline-block;width:0;height:0;padding:0;border:0;vertical-align:baseline`;let i=document.createElement(`div`);i.className=`empty-mascot`,i.hidden=!0,i.style.visibility=`hidden`;let a=document.createElement(`button`);a.type=`button`,a.className=`mascot-suggestion`;let o=document.createElement(`button`);o.type=`button`,o.className=`mascot-cat`,o.setAttribute(`aria-label`,`Make the cat hop`);let s=document.createElement(`img`);s.width=s.height=104,s.alt=``,s.draggable=!1;let c=document.createElement(`span`);c.className=`mascot-eyelids`,c.setAttribute(`aria-hidden`,`true`),o.append(s,c),i.append(a,o),e.append(i);let l=``,u=!1,d=!1,f=0,p=0,m=new Set,h=matchMedia(`(prefers-reduced-motion: reduce)`);function g(e,t,n){for(let e of m)e.id===n.id&&(e.cancel(),m.delete(e));let r=e.animate(t,n);m.add(r),r.onfinish=()=>m.delete(r)}function _(){clearTimeout(f),f=0,!(i.hidden||document.hidden||h.matches||!u)&&(d=!0,g(o,[{transform:`translateY(0)`,easing:`ease-out`},{transform:`translateY(-4px)`,offset:.4,easing:`ease-in`},{transform:`translateY(0)`}],{id:`mascot-bump`,duration:420}))}o.onclick=_;function v(){clearTimeout(f),clearTimeout(p),f=p=0;for(let e of m)e.cancel();m.clear()}function y(){if(i.hidden||document.hidden||h.matches||!u){v();return}!d&&!f&&(f=setTimeout(_,1800)),p||=setTimeout(()=>{p=0,g(c,[{opacity:0,easing:`steps(1, end)`},{opacity:1,offset:.15},{opacity:1,offset:.8,easing:`steps(1, end)`},{opacity:0}],{id:`mascot-blink`,duration:180}),y()},5e3+Math.random()*4e3)}function b(){i.hidden=!0,v()}async function x(){try{await s.decode()}catch{return}i.isConnected&&(u=!0,i.style.visibility=``,y())}return s.addEventListener(`load`,x),document.addEventListener(`visibilitychange`,y),h.addEventListener(`change`,y),a.onclick=()=>t(l),{hide:b,get statusInset(){return i.hidden?0:o.offsetWidth+20},update({screen:t,rows:c,viewportRows:u,contentRows:d,draft:f,hasSales:p,hasProducts:m}){if(!t||!c){b();return}let h=t.getBoundingClientRect(),g=e.getBoundingClientRect(),_=h.height/c,v=(u-d)*_-16;if(v<48){b();return}s.hasAttribute(`src`)||(s.src=new URL(``+new URL(`../empty-cat-v1.png`,import.meta.url).href,``+import.meta.url).href);let x=!f.trim()&&v>=180;a.hidden=!x,a.textContent=p?m?`Ask me to calculate our total profit.`:`Ask me to calculate our total sales.`:`Ask me to explore your files.`,l=p?m?`What's our total sales profit?`:`What's our total sales revenue?`:`Explore the workspace files and summarize what is here.`,a.setAttribute(`aria-label`,`Use suggested prompt: ${l}`);let S=Math.min(104,Math.floor(x?v-76:v));o.style.width=o.style.height=`${S}px`,i.hidden=!1;let C=h.top+(u+1.5)*_,w=t.querySelector(`.xterm-rows`)?.children[u+1]?.firstElementChild,T=devicePixelRatio||1;if(w&&n){let e=getComputedStyle(w);w.parentElement.append(r);let t=r.getBoundingClientRect().top;r.remove(),n.font=`${e.fontWeight} ${parseFloat(e.fontSize)*T}px ${e.fontFamily}`;let i=n.measureText(`─`);C=t-(i.actualBoundingBoxAscent-i.actualBoundingBoxDescent)/2/T}C=Math.floor(C*T+1e-4)/T;let E=Math.round(S*1156/1250*T)/T;i.style.top=`${C-g.top+S-E-i.getBoundingClientRect().height}px`,y()},dispose(){b(),s.removeEventListener(`load`,x),document.removeEventListener(`visibilitychange`,y),h.removeEventListener(`change`,y),i.remove()}}}var Fg=e=>rd(String(e??``)).replace(/\r\n?/g,`
|
| 134 |
+
`).replace(/[\x00-\x08\x0b-\x1f\x7f-\x9f]/g,``),Ig=[{value:`/help`,label:`/help`,description:`Browser terminal commands`},{value:`/load`,label:`/load`,description:`Load MiniCPM on this device`},{value:`/new`,label:`/new`,description:`Start a new conversation; keep files`},{value:`/clear`,label:`/clear`,description:`Same as /new`}];function Lg(e,t){let n=new vs({fontFamily:`ui-monospace, SFMono-Regular, Menlo, Consolas, monospace`,fontSize:13,lineHeight:1.3,theme:pg,cursorBlink:!1,scrollback:0,screenReaderMode:!0,allowProposedApi:!0});n.loadAddon(new bl);let r=document.createElement(`div`);r.className=`pi-terminal-mount`,e.append(r);let i=new xs;n.loadAddon(i),n.open(r);let a=new Sg(n),o=new Qf(a,!0,void 0,{copyOnSelect:!1,copySelection:async e=>{try{return await navigator.clipboard.writeText(e),!0}catch{return!1}},openUrl:e=>{/^https?:\/\//i.test(e)&&window.open(e,`_blank`,`noopener,noreferrer`)},scrollToEndIndicator:()=>Q.fg(`accent`,` ↓ Latest `),searchMatchStyle:e=>Q.bg(`selectedBg`,Q.fg(`text`,e)),searchCurrentMatchStyle:e=>Q.bg(`accent`,Q.fg(`userMessageBg`,Q.bold(e)))}),s=new ru({...tu,"app.interrupt":{defaultKeys:[`escape`,`ctrl+c`]},"app.exit":{defaultKeys:`ctrl+d`},"app.tools.expand":{defaultKeys:`ctrl+o`},"tui.editor.historyPrevious":{defaultKeys:`ctrl+p`},"tui.editor.historyNext":{defaultKeys:`ctrl+n`}});au(s);let c=new lg(o,hg(),s,{paddingX:0}),l=new Pd,u=new af(l,{follow:`end`,primary:!0,scrollbar:`auto`,scrollbarTrackStyle:e=>Q.fg(`borderMuted`,e),scrollbarThumbStyle:e=>Q.fg(`muted`,e)}),d=new xp(``,0,0),f=0,p=d.render.bind(d);d.render=e=>{let t=Math.min(f,Math.max(0,e-1));return p(e-t).map(e=>e+` `.repeat(t))},o.setLayoutRoot(new Jh([{component:u,basis:0,grow:1,minSize:0},{component:new Jh([d,c,{invalidate(){},render(e){let t=`/workspace · MiniCPM5-2B`,n=Ng(m.contextUsage),r=`${t} · ${n}`;return(r.length<=e-2?[r]:[t,n]).flatMap(t=>new xp(Q.fg(`dim`,t),1,0).render(e))}}]),basis:`auto`,shrink:0}])),o.setFocus(c);let m={loaded:!1,busy:!1,activity:``,contextUsage:Mg([])},h=[],g=[],_=!1,v=!1,y=!1,b=Pg(e,e=>{m.busy||v||c.getText().trim()||(c.setText(e),n.focus(),o.requestRender())});function x(){let e=r.querySelector(`.xterm-screen`);!y||g.some(e=>e.role===`user`)||m.busy||v?b.hide():b.update({screen:e,rows:n.rows,viewportRows:u.viewportHeight,contentRows:l.render(n.cols).length,draft:c.getText(),hasSales:h.includes(`sales.csv`),hasProducts:h.includes(`products.csv`)});let t=e?.getBoundingClientRect().width/n.cols,i=t>0?Math.ceil(b.statusInset/t):0;i!==f&&(f=i,o.requestRender())}let S=n.onRender(x),C=new Map,w=[],T=new Set,E=new Pd;E.addChild(new bp(Q.fg(`accent`,Q.bold(`π Pi`))+Q.fg(`muted`,` browser workspace`),1,1)),E.addChild(new bp(`Ask Pi to work with the files on the right.
|
| 135 |
|
| 136 |
/load load MiniCPM
|
| 137 |
/help show commands
|
|
|
|
| 156 |
`)),r.style.height=`auto`,r.style.height=Math.min(104,Math.max(32,r.scrollHeight))+`px`,i.scrollTop=r.scrollTop,i.scrollLeft=r.scrollLeft,v()}function b(e){r.value=e,y(),r.setSelectionRange(e.length,e.length)}function x(){m||(d.length=0,a.replaceChildren(l.cloneNode(!0)),v())}function S(e,t,n){if(!t)return;let r=document.createElement(`pre`);r.className=n,r.textContent=Hg(t),e.append(r)}o.onsubmit=async e=>{e.preventDefault();let n=r.value;if(m||h||!n.trim())return;m=!0,u.at(-1)!==n&&u.push(n),u.length>50&&u.shift(),f=u.length,p=``,b(``),r.focus(),d.length||a.replaceChildren();let i=document.createElement(`section`);i.className=`shell-entry`;let o=document.createElement(`div`);o.className=`shell-entry-heading`;let s=document.createElement(`span`);s.className=`shell-prompt`,s.textContent=`$`;let c=document.createElement(`pre`);c.className=`shell-code`,c.append(Ug(Hg(n)));let l=document.createElement(`span`);l.className=`shell-status`,l.textContent=`Running…`,o.append(s,c,l),i.append(o),a.append(i),d.push(i),_();try{let e=await t(n),r=g();S(i,e.stdout,`shell-stdout`),S(i,e.stderr,`shell-stderr`),l.classList.add(e.exitCode===0?`success`:`error`),l.textContent=`[exit ${e.exitCode}]`,r&&_()}catch(e){let t=g();S(i,e.message,`shell-stderr`),l.classList.add(`error`),l.textContent=`[error]`,r.value||b(n),t&&_()}finally{for(;d.length>1&&(d.length>40||a.textContent.length>48e3);)d.shift().remove();m=!1,v()}},r.addEventListener(`input`,()=>{f=u.length,p=r.value,y()}),r.addEventListener(`scroll`,()=>{i.scrollTop=r.scrollTop,i.scrollLeft=r.scrollLeft}),r.addEventListener(`keydown`,e=>{if(e.isComposing||e.keyCode===229)return;if(e.key===`Enter`&&!e.shiftKey){e.preventDefault(),o.requestSubmit();return}if(e.ctrlKey&&e.key.toLowerCase()===`l`){e.preventDefault(),x();return}if(e.ctrlKey||e.altKey||e.metaKey||e.shiftKey||r.selectionStart!==r.selectionEnd)return;let t=!r.value.slice(0,r.selectionStart).includes(`
|
| 157 |
`),n=!r.value.slice(r.selectionEnd).includes(`
|
| 158 |
`);e.key===`ArrowUp`&&t&&f>0?(e.preventDefault(),f===u.length&&(p=r.value),b(u[--f])):e.key===`ArrowDown`&&n&&f<u.length&&(e.preventDefault(),b(++f===u.length?p:u[f]))}),c.onclick=()=>{x(),r.focus()};let C=0;return new ResizeObserver(([e])=>{e.contentRect.width!==C&&(C=e.contentRect.width,y())}).observe(r.parentElement),y(),{setBusy(e){h=e,v()}}}function Gg(e){let t=e.querySelector(`.runtime-value`),n=e.querySelector(`.runtime-sparkline`),r=e.querySelector(`.runtime-bars`),i=e.querySelector(`[role="status"]`),a=e.querySelector(`.runtime-engine`),o=e.querySelector(`.runtime-content`);new ResizeObserver(([t])=>{e.style.width=`calc(${Math.ceil(t.contentRect.width)}px + 2 * var(--runtime-padding))`}).observe(o);let s=!1,c,l=[],u;function d(o,s,d){clearTimeout(u),o!==c&&(i.textContent=d??`${s} in this browser.`),c=o,a.textContent=c===`tool`?`JavaScript`:`WebGPU`,e.dataset.state=c,t.textContent=s,n.setAttribute(`hidden`,``),e.title=d??`${s} in this browser.`,c!==`generating`&&(l=[],r.replaceChildren())}function f(){d(`idle`,s?`Ready`:`Idle`,s?`Model ready. Inference and tools run in this browser.`:`Model loads when you send a message. Inference and tools run in this browser.`)}function p(e){if(!Number.isFinite(e)||e<0){d(`generating`,`Generating…`);return}d(`generating`,`${e<10?e.toFixed(1):Math.round(e)} tok/s`,`Live token generation speed, including thinking tokens. Prompt-processing time is excluded.`),l.push(e),l.length>12&&l.shift();let t=Math.max(40,...l),i=[...Array(12-l.length).fill(null),...l];r.replaceChildren(...i.map((e,n)=>{if(e===null||e===0){let t=document.createElementNS(`http://www.w3.org/2000/svg`,`circle`);return t.setAttribute(`cx`,String(n*4+1)),t.setAttribute(`cy`,`17`),t.setAttribute(`r`,`1`),t.setAttribute(`opacity`,e===null?`.35`:`.6`),t}let r=document.createElementNS(`http://www.w3.org/2000/svg`,`rect`),i=Math.max(2,Math.min(16,e/t*16));return r.setAttribute(`x`,String(n*4)),r.setAttribute(`y`,(18-i).toFixed(2)),r.setAttribute(`width`,`2`),r.setAttribute(`height`,i.toFixed(2)),r.setAttribute(`rx`,`1`),r})),n.removeAttribute(`hidden`),u=setTimeout(()=>{d(`generating`,`Generating…`),l=[],r.replaceChildren()},2e3)}return f(),{update(e){e.type===`loaded`?(s=!0,f()):e.type===`idle`||e.type===`initialized`?f():e.type===`fatal`?(s=!1,d(`error`,`Unavailable`)):e.type===`busy`?e.action===`load`?d(`loading`,`Loading…`,e.cachedOnly?`Loading model from this browser…`:void 0):e.action===`prompt`?d(`prefill`,`Reading prompt…`):e.action===`shell`?d(`tool`,`Running bash…`):e.action===`read`?d(`tool`,`Reading file…`):e.action===`write`?d(`tool`,`Writing file…`):d(`working`,`Working…`):e.type===`load_progress`?d(`loading`,e.phase===`download`&&e.loaded<e.total?`Downloading ${Math.round(e.loaded/e.total*100)}%`:e.phase===`warmup`?`Warming up…`:`Preparing…`):e.type===`inference_activity`?e.phase===`prefill`?d(`prefill`,`Reading prompt…`):e.phase===`decode`?p(e.rate):(c===`generating`||c===`prefill`)&&d(`working`,`Working…`):e.type===`agent_event`&&e.event.type===`tool_execution_start`&&d(`tool`,{bash:`Running bash…`,read:`Reading file…`,write:`Writing file…`,edit:`Editing file…`}[e.event.toolName]??`Running tool…`)}}}var Kg={default:`#b0b0bd`,markdown:`#519aba`,csv:`#8dc149`,json:`#cbcb41`,javascript:`#cbcb41`,typescript:`#519aba`,react:`#519aba`,html:`#e37933`,css:`#519aba`,python:`#519aba`,shell:`#8dc149`,config:`#b0b0bd`,image:`#a074c4`,svg:`#a074c4`,git:`#e37933`,pdf:`#cc3e44`,zip:`#b0b0bd`,db:`#cbcb41`},qg={md:`markdown`,markdown:`markdown`,mdx:`markdown`,csv:`csv`,tsv:`csv`,json:`json`,jsonc:`json`,jsonl:`json`,js:`javascript`,mjs:`javascript`,cjs:`javascript`,ts:`typescript`,mts:`typescript`,cts:`typescript`,jsx:`react`,tsx:`react`,html:`html`,htm:`html`,xml:`html`,css:`css`,scss:`css`,sass:`css`,less:`css`,py:`python`,pyi:`python`,sh:`shell`,bash:`shell`,zsh:`shell`,fish:`shell`,yaml:`config`,yml:`config`,toml:`config`,ini:`config`,conf:`config`,env:`config`,png:`image`,jpg:`image`,jpeg:`image`,gif:`image`,webp:`image`,avif:`image`,ico:`image`,svg:`svg`,pdf:`pdf`,zip:`zip`,gz:`zip`,tar:`zip`,sql:`db`,sqlite:`db`,db:`db`};function Jg(e){let t=e.split(`/`).at(-1).toLowerCase();return[`.gitignore`,`.gitattributes`,`.gitmodules`].includes(t)?`git`:t===`.env`||t.startsWith(`.env.`)||t===`.editorconfig`?`config`:qg[t.split(`.`).at(-1)]??`default`}function Yg(e){let t=Jg(e),n=document.createElement(`span`);return n.className=`file-icon`,n.dataset.kind=t,n.setAttribute(`aria-hidden`,`true`),n.style.setProperty(`--file-color`,Kg[t]),n.style.setProperty(`--file-icon`,`url("${new URL(`file-icons/${t}.svg`,document.baseURI).href}")`),n}var Xg={html:`markup`,htm:`markup`,xml:`markup`,svg:`markup`,vue:`markup`,css:`css`,js:`javascript`,mjs:`javascript`,cjs:`javascript`,jsx:`jsx`,ts:`typescript`,mts:`typescript`,cts:`typescript`,tsx:`tsx`,json:`json`,ipynb:`json`,md:`markdown`,markdown:`markdown`,py:`python`,pyw:`python`,sh:`bash`,bash:`bash`,zsh:`bash`,yml:`yaml`,yaml:`yaml`,toml:`toml`,sql:`sql`,csv:`csv`,c:`c`,h:`c`,cpp:`cpp`,cc:`cpp`,cxx:`cpp`,hpp:`cpp`,cs:`csharp`,java:`java`,go:`go`,rs:`rust`,diff:`diff`,patch:`diff`};function Zg(e=``){let t=e.split(`/`).at(-1).toLowerCase();return/^dockerfile(?:\.|$)/.test(t)?`docker`:/^\.(?:bashrc|bash_profile|zshrc|profile)$/.test(t)?`bash`:Xg[t.split(`.`).at(-1)]??null}function Qg(e){if(e.length>2e5)return!1;let t=1;for(let n=0;n<e.length;n++)if(e.charCodeAt(n)===10&&++t>5e3)return!1;return!0}function $g(e){let t=e.querySelector(`#editor`),n=e.querySelector(`#editor-highlight`),r=``,i=0,a,o,s,c=!1,l=!1,u;function d(){n.style.width=t.clientWidth+`px`,n.style.height=t.clientHeight+`px`,n.scrollTop=t.scrollTop,n.scrollLeft=t.scrollLeft}function f(){e.classList.remove(`is-highlighted`)}function p(){clearTimeout(o),s?.terminate(),s=void 0,c=!1}function m(){if(c||!u)return;let t=u;u=void 0;try{s||(s=new Worker(new URL(new URL(`editor-highlight.worker-oPpFVFqT.js`,import.meta.url).href,``+import.meta.url),{type:`module`}),s.onmessage=({data:t})=>{clearTimeout(o),c=!1,t.id===i&&t.html!==null&&(n.innerHTML=t.html+`
|
| 159 |
+
`,d(),e.classList.add(`is-highlighted`)),m()},s.onerror=()=>{p(),u=void 0}),c=!0,s.postMessage(t),o=setTimeout(()=>{p(),m()},1500)}catch{p()}}function h(n=0){i++,clearTimeout(a),u=void 0,f();let o=Zg(r),s=t.value;if(e.dataset.language=o??`plain`,l||!s||!o||!Qg(s))return;let c=i;a=setTimeout(()=>{u={id:c,text:s,language:o},m()},n)}t.addEventListener(`input`,()=>h(80)),t.addEventListener(`compositionstart`,()=>{l=!0,h()}),t.addEventListener(`compositionend`,()=>{l=!1,h(80)}),t.addEventListener(`scroll`,d,{passive:!0});let g=new ResizeObserver(d);return g.observe(t),{setFile(e,n){if(r===e&&t.value===n)return;let i=r!==e;r=e,t.value!==n&&(t.value=n),i&&(t.scrollTop=0,t.scrollLeft=0),h()},dispose(){clearTimeout(a),p(),g.disconnect()}}}var $=e=>document.querySelector(e),e_=new Worker(new URL(new URL(`agent.worker-L7ew_XyL.js`,import.meta.url).href,``+import.meta.url),{type:`module`}),t_=new Map,n_=1,r_=!1,i_=!1,a_,o_=!1,s_={},c_=[],l_,u_,d_=new Promise(e=>{l_=e}),f_={events:[],generations:[],runtimeSamples:[],device:null,contextUsage:Mg([])},p_=Gg($(`#runtime-meter`)),m_=$g($(`.editor-input`)),h_=Lg($(`#pi-terminal`),{load:()=>d_.then(O_),prompt:e=>d_.then(()=>k_(e)),newChat:()=>d_.then(A_),stop:()=>v_(`stop`),shell:e=>{if(o_)throw Error(`Save your file edits before running commands.`);return v_(`shell`,{command:e})}}),g_=Rg($(`#model-dialog`),{load:O_,stop:()=>v_(`stop`),onClose:()=>h_.focus()}),__=Wg($(`.workspace > .terminal`),{run:async e=>{if(await d_,o_)throw Error(`Save your file edits before running commands.`);if(r_)throw Error(`Wait for Pi to finish or stop the current task first.`);return v_(`shell`,{command:e})}});function v_(e,t){return new Promise((n,r)=>{let i=n_++;t_.set(i,{resolve:n,reject:r}),e_.postMessage({id:i,action:e,args:t})})}function y_(e){$(`#notice`).textContent=e,$(`#notice`).hidden=!e}function b_(e){return window.self===window.top||!/denied|storage directory access/i.test(e)?e:($(`#open-tab`).href=location.href,$(`#open-tab`).hidden=!1,`Your browser blocked storage in this embedded app. Open the workspace in a new tab to load the model and save files.`)}function x_(){$(`#load`).disabled=r_||i_,$(`#load`).classList.toggle(`is-loaded`,i_),i_?$(`#load`).textContent=`Loaded`:r_||($(`#load`).textContent=`Load model`),$(`#load`).title=$(`#load-status`).textContent+` `+$(`#load-detail`).textContent,$(`#send`).disabled=r_,$(`#newline`).disabled=r_,h_.setState({busy:r_,loaded:i_,activity:$(`#activity`).textContent}),$(`#stop`).hidden=!r_;for(let e of[`#new-chat`,`#add-file`,`#upload`])$(e).disabled=r_;__.setBusy(r_),$(`#save`).disabled=r_||!a_||!o_,$(`#editor`).disabled=r_||!a_}function S_(){h_.setFiles(s_),$(`#files`).replaceChildren();for(let e of Object.keys(s_).sort()){let t=document.createElement(`button`),n=document.createElement(`span`);n.className=`file-name`,n.textContent=e.replace(/^\/workspace\//,``),t.append(Yg(e),n),t.title=e,t.classList.toggle(`selected`,e===a_),t.onclick=()=>C_(e),$(`#files`).append(t)}a_&&!o_&&m_.setFile(a_,s_[a_]??``)}function C_(e){if(u_=void 0,o_){y_(`Save the open file before selecting another file.`);return}a_=e,$(`#filename`).textContent=e,$(`#filetype`).replaceChildren(Yg(e)),S_(),x_()}function w_(){if(o_){u_=void 0;return}if(!u_||!Object.hasOwn(s_,u_))return;let e=u_,t=e!==a_;C_(e),t&&($(`#editor`).scrollTop=0,$(`#editor`).scrollLeft=0);let n=[...$(`#files`).children].find(t=>t.title===e);if(n){let e=n.getBoundingClientRect(),t=$(`#files`).getBoundingClientRect();e.top<t.top?$(`#files`).scrollTop+=e.top-t.top:e.bottom>t.bottom&&($(`#files`).scrollTop+=e.bottom-t.bottom)}}function T_(){h_.setMessages(c_)}function E_(e){f_.contextUsage=e,h_.setState({contextUsage:e})}e_.onmessage=({data:e})=>{if(p_.update(e),e.type===`inference_activity`&&(f_.runtimeSamples.push(e),f_.runtimeSamples.length>160&&f_.runtimeSamples.shift()),e.type===`reply`){let t=t_.get(e.id);t_.delete(e.id),e.error?t?.reject(Object.assign(Error(e.error),{name:e.errorName||`Error`,code:e.errorCode})):t?.resolve(e.value)}else if(e.type===`initialized`)s_=e.files,c_=e.messages,S_(),T_(),E_(Mg(c_)),s_[`/workspace/README.md`]&&C_(`/workspace/README.md`),document.activeElement===document.body&&h_.focus(),l_();else if(e.type===`busy`)e.action===`load`&&($(`#load`).textContent=`Loading…`),r_=!0,$(`#activity`).textContent=e.action===`prompt`?`Pi is working…`:e.action===`load`?e.cachedOnly?`Loading model from this browser…`:`Loading model…`:`Working…`,x_();else if(e.type===`idle`)r_=!1,$(`#activity`).textContent=i_?`Ready · running on your device`:`Files and chat are saved on this device.`,x_();else if(e.type===`workspace`)s_=e.files,S_(),w_();else if(e.type===`device`)f_.device=e.device;else if(e.type===`load_progress`)g_.update(e),e.phase===`download`?($(`#progress`).hidden=!1,$(`#progress`).value=e.loaded/e.total*100,$(`#load-status`).textContent=e.loaded===e.total?`Download verified. Preparing model…`:`Downloading and verifying model…`,$(`#load-detail`).textContent=`${(e.loaded/1e9).toFixed(2)} / ${(e.total/1e9).toFixed(2)} GB · ${Math.round(e.loaded/e.total*100)}%${e.cached?` · ${(e.cached/1e9).toFixed(2)} GB cached`:``}`,$(`#load`).textContent=`Loading ${Math.round(e.loaded/e.total*100)}%`):($(`#load-status`).textContent=e.phase===`compile`?`Preparing WebGPU session…`:`Warming up GPU shaders…`,$(`#progress`).removeAttribute(`value`),$(`#load`).textContent=e.phase===`compile`?`Preparing…`:`Almost ready…`),$(`#load`).title=$(`#load-status`).textContent+` `+$(`#load-detail`).textContent;else if(e.type===`loaded`)i_=!0,f_.device=e.device,$(`#progress`).hidden=!0,$(`#load`).textContent=`Loaded`,$(`#load-status`).textContent=`Ready. Model inference and tools run entirely in this browser.`,$(`#load-detail`).textContent=e.cachedBytes?`Verified model restored from browser storage.`:`SHA-256 verified · model cached for next time.`,x_();else if(e.type===`agent_event`){let t=e.event;if(f_.events.push(t.type),f_.events.length>500&&f_.events.shift(),t.message){let e=c_.findIndex(e=>e.timestamp===t.message.timestamp&&e.role===t.message.role&&e.toolCallId===t.message.toolCallId);e<0?c_.push(t.message):c_[e]=t.message,T_()}t.type===`tool_execution_start`&&($(`#activity`).textContent=`Running ${t.toolName}…`),t.type===`tool_execution_end`&&t.toolName===`read`&&!t.isError&&typeof t.result?.details?.path==`string`&&(u_=t.result.details.path,w_()),t.type===`turn_start`&&($(`#activity`).textContent=`Pi is thinking…`),h_.setState({activity:$(`#activity`).textContent})}else e.type===`context_usage`?E_({inputTokens:e.inputTokens,outputTokens:e.outputTokens}):e.type===`generation`?(f_.generations.push(e),f_.generations.length>100&&f_.generations.shift()):e.type===`context_trim`?y_(`Older turns were left out of the model context. Your full chat remains saved.`):e.type===`notice`?y_(e.text):e.type===`persistence_error`?y_(b_(e.error)):e.type===`fatal`&&(i_=!1,y_(e.error),x_())},e_.onerror=e=>{p_.update({type:`fatal`});let t=Error(e.message||`The worker stopped. Reload to restore your saved workspace.`);y_(t.message);for(let e of t_.values())e.reject(t);t_.clear(),r_=!1,i_=!1,x_()};var D_=e=>async t=>{t?.preventDefault();try{await d_,await e(t)}catch(e){y_(e.message)}};async function O_({cachedOnly:e=!1}={}){y_(``);let t=new URLSearchParams(location.search);try{return await v_(`load`,{runtimeURL:new URL(`./runtime/`,location.href).href,local:t.get(`source`)===`local`,origin:location.origin,limit128:t.has(`limit128`),cachedOnly:e})}catch(e){throw e.message=b_(e.message),$(`#load-status`).textContent=e.message,$(`#progress`).hidden=!0,e}}async function k_(e){if(!e.trim())return;if(o_)throw Error(`Save your file edits before asking Pi to work on them.`);if(!i_){let t;if(await v_(`cache_status`))try{await O_({cachedOnly:!0})}catch(e){if(e.name===`AbortError`)throw e;e.code!==`MODEL_CACHE_MISS`&&(t=e.message)}if(!i_&&!await g_.request(e,{errorMessage:t}))throw new DOMException(`Message kept for later.`,`AbortError`)}y_(``);let t=await v_(`prompt`,{text:e});return c_=t.messages,T_(),t}$(`#load`).onclick=D_(O_),$(`#send`).onclick=()=>{h_.submit(),h_.focus()},$(`#newline`).onclick=()=>h_.newline(),$(`#stop`).onclick=D_(()=>v_(`stop`)),$(`.terminal-panel`).addEventListener(`click`,e=>{!e.defaultPrevented&&e.button===0&&e.target instanceof Element&&(e.target.closest(`#pi-terminal, button, a, input, textarea, select, label, summary, [role="button"], [contenteditable]`)||window.getSelection()?.isCollapsed!==!1&&h_.focus())});async function A_(){u_=void 0,c_=(await v_(`new_chat`)).messages,E_(Mg(c_)),h_.setMessages(c_,{reset:!0}),y_(``)}$(`#new-chat`).onclick=D_(A_),$(`#editor`).oninput=()=>{o_=!0,x_()},$(`#save`).onclick=D_(async()=>{await v_(`write`,{path:a_,content:$(`#editor`).value}),o_=!1,S_(),x_(),y_(``)}),$(`#add-file`).onclick=D_(async()=>{if(o_)throw Error(`Save the open file first.`);let e=window.prompt(`File path, relative to /workspace:`);if(!e?.trim())return;let t=e.startsWith(`/`)?e:`/workspace/`+e;if(Object.hasOwn(s_,t))throw Error(`That file already exists.`);await v_(`write`,{path:t,content:``}),C_(t)}),$(`#upload`).onchange=D_(async()=>{if(o_)throw Error(`Save the open file first.`);for(let e of $(`#upload`).files){if(e.size>1024**2)throw Error(`Import text files of 1 MiB or less.`);let t=await e.text();if(t.includes(`\0`))throw Error(`Import supports text files only.`);let n=`/workspace/`+e.name;if(Object.hasOwn(s_,n))throw Error(e.name+` already exists. Rename it before importing.`);await v_(`write`,{path:n,content:t}),C_(n)}$(`#upload`).value=``}),$(`#export`).onclick=D_(async()=>{let e=await v_(`snapshot`),t=URL.createObjectURL(new Blob([JSON.stringify(e,null,2)],{type:`application/json`})),n=document.createElement(`a`);n.href=t,n.download=`pi-workspace.json`,n.click(),setTimeout(()=>URL.revokeObjectURL(t),1e3)}),window.browserPi={ready:d_,load:O_,prompt:k_,rpc:v_,diagnostics:f_,terminal:h_,runtimeMeter:p_,get loaded(){return i_},get busy(){return r_}},x_();
|
deployment-manifest.json
CHANGED
|
@@ -21,8 +21,8 @@
|
|
| 21 |
"sha256": "196d96e03432dd5a222e63d9757817a65589d27e6562f7f7c748c9eee40a7694"
|
| 22 |
},
|
| 23 |
"app/README.md": {
|
| 24 |
-
"bytes":
|
| 25 |
-
"sha256": "
|
| 26 |
},
|
| 27 |
"app/index.html": {
|
| 28 |
"bytes": 8758,
|
|
@@ -33,8 +33,8 @@
|
|
| 33 |
"sha256": "b8a7cb766e4d87879e27ebd656b2e2a7b1bfb2ffd50c52598736fd878b35fe84"
|
| 34 |
},
|
| 35 |
"app/package.json": {
|
| 36 |
-
"bytes":
|
| 37 |
-
"sha256": "
|
| 38 |
},
|
| 39 |
"app/public/empty-cat-v1.png": {
|
| 40 |
"bytes": 668737,
|
|
@@ -165,8 +165,8 @@
|
|
| 165 |
"sha256": "f92958fc5397fff319591bbf7ea751d96719e9f07a46544036a26a91de90e5d8"
|
| 166 |
},
|
| 167 |
"app/src/agent.worker.mjs": {
|
| 168 |
-
"bytes":
|
| 169 |
-
"sha256": "
|
| 170 |
},
|
| 171 |
"app/src/context-usage.mjs": {
|
| 172 |
"bytes": 870,
|
|
@@ -221,16 +221,16 @@
|
|
| 221 |
"sha256": "62f09aa13c695980717c7bc7e25ad6aba66254faa994bf81f39d2a912a89ff83"
|
| 222 |
},
|
| 223 |
"app/src/pi/empty-mascot.mjs": {
|
| 224 |
-
"bytes":
|
| 225 |
-
"sha256": "
|
| 226 |
},
|
| 227 |
"app/src/pi/entry.mjs": {
|
| 228 |
"bytes": 1351,
|
| 229 |
"sha256": "f7e6ae0ba428b78126c6873f9784dbf7d9220421cd6510ad893819492ed5ff9b"
|
| 230 |
},
|
| 231 |
"app/src/pi/terminal-ui.mjs": {
|
| 232 |
-
"bytes":
|
| 233 |
-
"sha256": "
|
| 234 |
},
|
| 235 |
"app/src/pi/theme.mjs": {
|
| 236 |
"bytes": 2453,
|
|
@@ -273,8 +273,8 @@
|
|
| 273 |
"sha256": "90e4f781f4f27f1e7d4ad38ea6c591bd80f1d366f9816717e5469f3ad838cb9f"
|
| 274 |
},
|
| 275 |
"app/src/workspace.mjs": {
|
| 276 |
-
"bytes":
|
| 277 |
-
"sha256": "
|
| 278 |
},
|
| 279 |
"app/tests/browser.mjs": {
|
| 280 |
"bytes": 9598,
|
|
@@ -309,8 +309,8 @@
|
|
| 309 |
"sha256": "7092f2cdfc729f5d761d257a73038e4fbccbc2cf5f1e62fd3a10f84301e5a0b2"
|
| 310 |
},
|
| 311 |
"app/tests/mascot.mjs": {
|
| 312 |
-
"bytes":
|
| 313 |
-
"sha256": "
|
| 314 |
},
|
| 315 |
"app/tests/model-probe.mjs": {
|
| 316 |
"bytes": 2726,
|
|
@@ -321,8 +321,8 @@
|
|
| 321 |
"sha256": "8331c6664f04d2a5858af36a25d53b33d6283229b7a105696e7040ca1695a881"
|
| 322 |
},
|
| 323 |
"app/tests/protocol.test.mjs": {
|
| 324 |
-
"bytes":
|
| 325 |
-
"sha256": "
|
| 326 |
},
|
| 327 |
"app/tests/render.mjs": {
|
| 328 |
"bytes": 12139,
|
|
@@ -352,6 +352,10 @@
|
|
| 352 |
"bytes": 2589,
|
| 353 |
"sha256": "717b65ec8cb5109ac349bbf79286d258a542af7e31a791b2f8f0cf2e7f2430d7"
|
| 354 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
| 355 |
"app/tests/terminal.mjs": {
|
| 356 |
"bytes": 7329,
|
| 357 |
"sha256": "d19a8df4e4559f75a6c8f5397dde82b42e522da49e637fb5a7ed5496603b8013"
|
|
@@ -400,9 +404,9 @@
|
|
| 400 |
"bytes": 1679,
|
| 401 |
"sha256": "eab9d66533efd95e346d22f3f455c4c77a85c1386e82e14f3e72c4fdbb8d9c16"
|
| 402 |
},
|
| 403 |
-
"assets/agent.worker-
|
| 404 |
-
"bytes":
|
| 405 |
-
"sha256": "
|
| 406 |
},
|
| 407 |
"assets/editor-highlight.worker-oPpFVFqT.js": {
|
| 408 |
"bytes": 61457,
|
|
@@ -412,9 +416,9 @@
|
|
| 412 |
"bytes": 24522,
|
| 413 |
"sha256": "7b79ed5d5fdd77b61ef795853f6898910ed934b437cad82082ed07f67d252b26"
|
| 414 |
},
|
| 415 |
-
"assets/index-
|
| 416 |
-
"bytes":
|
| 417 |
-
"sha256": "
|
| 418 |
},
|
| 419 |
"assets/ort-wasm-simd-threaded.asyncify-DMmc6YqF.wasm": {
|
| 420 |
"bytes": 23567050,
|
|
@@ -502,7 +506,7 @@
|
|
| 502 |
},
|
| 503 |
"index.html": {
|
| 504 |
"bytes": 8855,
|
| 505 |
-
"sha256": "
|
| 506 |
},
|
| 507 |
"licenses/dependencies.txt": {
|
| 508 |
"bytes": 1386543,
|
|
@@ -705,8 +709,8 @@
|
|
| 705 |
"sha256": "b9bb39d5bd7bceb260d5eafa633f5af2e47a35d6132c8453d908ea6a4ad8a0f9"
|
| 706 |
},
|
| 707 |
"scripts/prepare_space.py": {
|
| 708 |
-
"bytes":
|
| 709 |
-
"sha256": "
|
| 710 |
},
|
| 711 |
"scripts/reference.py": {
|
| 712 |
"bytes": 2652,
|
|
@@ -836,6 +840,10 @@
|
|
| 836 |
"bytes": 1766,
|
| 837 |
"sha256": "aca0d8cacb7c5d4a6a9205db5a60c54a619817b83c5f549572f892f26c5a0dfe"
|
| 838 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
| 839 |
"validation/agent-rounded-header-render.json": {
|
| 840 |
"bytes": 7718,
|
| 841 |
"sha256": "1ca5acd2d5f62f340416d575d3750dd865b6bd86ee08e5eaa35c84d99fad6693"
|
|
@@ -900,6 +908,10 @@
|
|
| 900 |
"bytes": 2244,
|
| 901 |
"sha256": "64f5f41f482beb1886a922d4f5872a8f82ad61b701ff45c9e21fb7980acd4ff7"
|
| 902 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
| 903 |
"validation/agent-terminal-dark-conversation.json": {
|
| 904 |
"bytes": 73336,
|
| 905 |
"sha256": "981c8ac9c0f48f696ed6b96c86e147da5d628e3d0ffa5493b40af13e5f7168fe"
|
|
|
|
| 21 |
"sha256": "196d96e03432dd5a222e63d9757817a65589d27e6562f7f7c748c9eee40a7694"
|
| 22 |
},
|
| 23 |
"app/README.md": {
|
| 24 |
+
"bytes": 15515,
|
| 25 |
+
"sha256": "75e1e03b258556edc889b48480358f5ec8f2950224a92ea5be64688f1821a1d8"
|
| 26 |
},
|
| 27 |
"app/index.html": {
|
| 28 |
"bytes": 8758,
|
|
|
|
| 33 |
"sha256": "b8a7cb766e4d87879e27ebd656b2e2a7b1bfb2ffd50c52598736fd878b35fe84"
|
| 34 |
},
|
| 35 |
"app/package.json": {
|
| 36 |
+
"bytes": 1494,
|
| 37 |
+
"sha256": "a1742e6c628d57602e9223d46c8288ac36fcf78e8b45d114bd29a57125c0bc52"
|
| 38 |
},
|
| 39 |
"app/public/empty-cat-v1.png": {
|
| 40 |
"bytes": 668737,
|
|
|
|
| 165 |
"sha256": "f92958fc5397fff319591bbf7ea751d96719e9f07a46544036a26a91de90e5d8"
|
| 166 |
},
|
| 167 |
"app/src/agent.worker.mjs": {
|
| 168 |
+
"bytes": 5788,
|
| 169 |
+
"sha256": "532e0ef751d545ec42891db30addfd5f7af769e3cd1b9f0f27dd8b7e9874b7cc"
|
| 170 |
},
|
| 171 |
"app/src/context-usage.mjs": {
|
| 172 |
"bytes": 870,
|
|
|
|
| 221 |
"sha256": "62f09aa13c695980717c7bc7e25ad6aba66254faa994bf81f39d2a912a89ff83"
|
| 222 |
},
|
| 223 |
"app/src/pi/empty-mascot.mjs": {
|
| 224 |
+
"bytes": 6088,
|
| 225 |
+
"sha256": "4bebf8a86ba8710edfebce450684f4f55f161f8215009122ee1327cd808c471d"
|
| 226 |
},
|
| 227 |
"app/src/pi/entry.mjs": {
|
| 228 |
"bytes": 1351,
|
| 229 |
"sha256": "f7e6ae0ba428b78126c6873f9784dbf7d9220421cd6510ad893819492ed5ff9b"
|
| 230 |
},
|
| 231 |
"app/src/pi/terminal-ui.mjs": {
|
| 232 |
+
"bytes": 17727,
|
| 233 |
+
"sha256": "2d8ce6a8a3f04271c8bcac2ef7104457b3ad54425ef7c3d7f5f86ab7f5506e38"
|
| 234 |
},
|
| 235 |
"app/src/pi/theme.mjs": {
|
| 236 |
"bytes": 2453,
|
|
|
|
| 273 |
"sha256": "90e4f781f4f27f1e7d4ad38ea6c591bd80f1d366f9816717e5469f3ad838cb9f"
|
| 274 |
},
|
| 275 |
"app/src/workspace.mjs": {
|
| 276 |
+
"bytes": 5488,
|
| 277 |
+
"sha256": "c9be9b7a25ea04b40cdb30432c0cfcf4025d4e8454dd5a11a9c81404952f19e3"
|
| 278 |
},
|
| 279 |
"app/tests/browser.mjs": {
|
| 280 |
"bytes": 9598,
|
|
|
|
| 309 |
"sha256": "7092f2cdfc729f5d761d257a73038e4fbccbc2cf5f1e62fd3a10f84301e5a0b2"
|
| 310 |
},
|
| 311 |
"app/tests/mascot.mjs": {
|
| 312 |
+
"bytes": 8510,
|
| 313 |
+
"sha256": "1f84f41125c21c102b29cf7118fb5d92c15dfc3912e58ffe2e0f5dfd49871de6"
|
| 314 |
},
|
| 315 |
"app/tests/model-probe.mjs": {
|
| 316 |
"bytes": 2726,
|
|
|
|
| 321 |
"sha256": "8331c6664f04d2a5858af36a25d53b33d6283229b7a105696e7040ca1695a881"
|
| 322 |
},
|
| 323 |
"app/tests/protocol.test.mjs": {
|
| 324 |
+
"bytes": 8727,
|
| 325 |
+
"sha256": "67df25dfc319437f9613fa0399a264a6c10c091ed3711bec3721080d117ee39f"
|
| 326 |
},
|
| 327 |
"app/tests/render.mjs": {
|
| 328 |
"bytes": 12139,
|
|
|
|
| 352 |
"bytes": 2589,
|
| 353 |
"sha256": "717b65ec8cb5109ac349bbf79286d258a542af7e31a791b2f8f0cf2e7f2430d7"
|
| 354 |
},
|
| 355 |
+
"app/tests/starter-products.mjs": {
|
| 356 |
+
"bytes": 4460,
|
| 357 |
+
"sha256": "58455efc7e7e09eb4f309b59928d443db1318ebfd5e9a581189e166eda4bbf8d"
|
| 358 |
+
},
|
| 359 |
"app/tests/terminal.mjs": {
|
| 360 |
"bytes": 7329,
|
| 361 |
"sha256": "d19a8df4e4559f75a6c8f5397dde82b42e522da49e637fb5a7ed5496603b8013"
|
|
|
|
| 404 |
"bytes": 1679,
|
| 405 |
"sha256": "eab9d66533efd95e346d22f3f455c4c77a85c1386e82e14f3e72c4fdbb8d9c16"
|
| 406 |
},
|
| 407 |
+
"assets/agent.worker-L7ew_XyL.js": {
|
| 408 |
+
"bytes": 2098081,
|
| 409 |
+
"sha256": "e08312772a7af531dc7c54ab362eada50b016a4500b2e4b4f02f006778de56bf"
|
| 410 |
},
|
| 411 |
"assets/editor-highlight.worker-oPpFVFqT.js": {
|
| 412 |
"bytes": 61457,
|
|
|
|
| 416 |
"bytes": 24522,
|
| 417 |
"sha256": "7b79ed5d5fdd77b61ef795853f6898910ed934b437cad82082ed07f67d252b26"
|
| 418 |
},
|
| 419 |
+
"assets/index-8TcpvU1k.js": {
|
| 420 |
+
"bytes": 757876,
|
| 421 |
+
"sha256": "32ce41a1d226c6e2c82e1fecebf9ed84e4c4ce14f12540db1372c0f8ad825b8a"
|
| 422 |
},
|
| 423 |
"assets/ort-wasm-simd-threaded.asyncify-DMmc6YqF.wasm": {
|
| 424 |
"bytes": 23567050,
|
|
|
|
| 506 |
},
|
| 507 |
"index.html": {
|
| 508 |
"bytes": 8855,
|
| 509 |
+
"sha256": "a1138ffe838db29939fa05f77bc18fa1b01aa1ca33fb8c9eb33161103e2a88cd"
|
| 510 |
},
|
| 511 |
"licenses/dependencies.txt": {
|
| 512 |
"bytes": 1386543,
|
|
|
|
| 709 |
"sha256": "b9bb39d5bd7bceb260d5eafa633f5af2e47a35d6132c8453d908ea6a4ad8a0f9"
|
| 710 |
},
|
| 711 |
"scripts/prepare_space.py": {
|
| 712 |
+
"bytes": 6085,
|
| 713 |
+
"sha256": "833e771a7e836b8da16d9dd5e550c59415f85032e7fe1e1afc5ae4f605734695"
|
| 714 |
},
|
| 715 |
"scripts/reference.py": {
|
| 716 |
"bytes": 2652,
|
|
|
|
| 840 |
"bytes": 1766,
|
| 841 |
"sha256": "aca0d8cacb7c5d4a6a9205db5a60c54a619817b83c5f549572f892f26c5a0dfe"
|
| 842 |
},
|
| 843 |
+
"validation/agent-products-mascot.json": {
|
| 844 |
+
"bytes": 3395,
|
| 845 |
+
"sha256": "4e5844c3aba351b0d13711e42325b9ecbe07dd4287af675095e852846f8f7111"
|
| 846 |
+
},
|
| 847 |
"validation/agent-rounded-header-render.json": {
|
| 848 |
"bytes": 7718,
|
| 849 |
"sha256": "1ca5acd2d5f62f340416d575d3750dd865b6bd86ee08e5eaa35c84d99fad6693"
|
|
|
|
| 908 |
"bytes": 2244,
|
| 909 |
"sha256": "64f5f41f482beb1886a922d4f5872a8f82ad61b701ff45c9e21fb7980acd4ff7"
|
| 910 |
},
|
| 911 |
+
"validation/agent-starter-products.json": {
|
| 912 |
+
"bytes": 465,
|
| 913 |
+
"sha256": "1f2d1839c7e98fb3e9ec26799248761619c952a62be52c41630e50608caf33ce"
|
| 914 |
+
},
|
| 915 |
"validation/agent-terminal-dark-conversation.json": {
|
| 916 |
"bytes": 73336,
|
| 917 |
"sha256": "981c8ac9c0f48f696ed6b96c86e147da5d628e3d0ffa5493b40af13e5f7168fe"
|
index.html
CHANGED
|
@@ -20,7 +20,7 @@
|
|
| 20 |
<meta name="twitter:image" content="https://huggingface.co/spaces/Mike0021/MiniCPM5-2B-WebGPU-Pi/resolve/main/social-thumbnail-v1.png">
|
| 21 |
<meta name="twitter:image:alt" content="Pi inside a browser window with the Chrome logo, in monochrome Macintosh style.">
|
| 22 |
<title>Pi · Coding agent in your browser</title>
|
| 23 |
-
<script type="module" crossorigin src="./assets/index-
|
| 24 |
<link rel="stylesheet" crossorigin href="./assets/index-7LHZzhK2.css">
|
| 25 |
</head>
|
| 26 |
<body>
|
|
|
|
| 20 |
<meta name="twitter:image" content="https://huggingface.co/spaces/Mike0021/MiniCPM5-2B-WebGPU-Pi/resolve/main/social-thumbnail-v1.png">
|
| 21 |
<meta name="twitter:image:alt" content="Pi inside a browser window with the Chrome logo, in monochrome Macintosh style.">
|
| 22 |
<title>Pi · Coding agent in your browser</title>
|
| 23 |
+
<script type="module" crossorigin src="./assets/index-8TcpvU1k.js"></script>
|
| 24 |
<link rel="stylesheet" crossorigin href="./assets/index-7LHZzhK2.css">
|
| 25 |
</head>
|
| 26 |
<body>
|
scripts/prepare_space.py
CHANGED
|
@@ -66,6 +66,7 @@ for name in [
|
|
| 66 |
'agent-mascot-motion.json', 'agent-mascot-motion-layout.json',
|
| 67 |
'agent-editor-focus.json',
|
| 68 |
'agent-editor.json',
|
|
|
|
| 69 |
'agent-follow-read.json', 'agent-workspace-runtime.json',
|
| 70 |
]:
|
| 71 |
source = root / 'validation' / name
|
|
|
|
| 66 |
'agent-mascot-motion.json', 'agent-mascot-motion-layout.json',
|
| 67 |
'agent-editor-focus.json',
|
| 68 |
'agent-editor.json',
|
| 69 |
+
'agent-starter-products.json', 'agent-products-mascot.json',
|
| 70 |
'agent-follow-read.json', 'agent-workspace-runtime.json',
|
| 71 |
]:
|
| 72 |
source = root / 'validation' / name
|
validation/agent-products-mascot.json
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"url": "http://127.0.0.1:4173/",
|
| 3 |
+
"date": "2026-09-14T12:16:50.132Z",
|
| 4 |
+
"checks": [
|
| 5 |
+
"Bubble and cat stay hidden during a delayed image download and appear together when ready",
|
| 6 |
+
"Click and keyboard activation replay one hop without changing the prompt; reduced motion remains still",
|
| 7 |
+
"Suggestion fills and focuses Pi without sending; first-message modal and Cancel preserve the draft and zero-user state",
|
| 8 |
+
"Seven viewports keep the cat on the input divider without covering the editor; short viewports prioritize the editor",
|
| 9 |
+
"Starter prompt follows actual workspace files",
|
| 10 |
+
"Restored user history hides the mascot without fetching its image; New chat brings it back",
|
| 11 |
+
"Multiline drafts and keyboard-sized viewports stay unobstructed"
|
| 12 |
+
],
|
| 13 |
+
"layouts": [
|
| 14 |
+
{
|
| 15 |
+
"width": 1440,
|
| 16 |
+
"height": 1000,
|
| 17 |
+
"visible": true,
|
| 18 |
+
"top": 689.21875,
|
| 19 |
+
"bottom": 858,
|
| 20 |
+
"left": 555.875,
|
| 21 |
+
"right": 751.875,
|
| 22 |
+
"contentBottom": 233,
|
| 23 |
+
"statusTop": 822,
|
| 24 |
+
"pawBottom": 850.1792,
|
| 25 |
+
"dividerCenter": 850.5,
|
| 26 |
+
"inputTop": 860,
|
| 27 |
+
"screenLeft": 23,
|
| 28 |
+
"screenRight": 759,
|
| 29 |
+
"overflow": false
|
| 30 |
+
},
|
| 31 |
+
{
|
| 32 |
+
"width": 1100,
|
| 33 |
+
"height": 800,
|
| 34 |
+
"visible": true,
|
| 35 |
+
"top": 499.21875,
|
| 36 |
+
"bottom": 668,
|
| 37 |
+
"left": 370.078125,
|
| 38 |
+
"right": 566.078125,
|
| 39 |
+
"contentBottom": 233,
|
| 40 |
+
"statusTop": 632,
|
| 41 |
+
"pawBottom": 660.1792,
|
| 42 |
+
"dividerCenter": 660.5,
|
| 43 |
+
"inputTop": 670,
|
| 44 |
+
"screenLeft": 23,
|
| 45 |
+
"screenRight": 571,
|
| 46 |
+
"overflow": false
|
| 47 |
+
},
|
| 48 |
+
{
|
| 49 |
+
"width": 851,
|
| 50 |
+
"height": 640,
|
| 51 |
+
"visible": true,
|
| 52 |
+
"top": 374,
|
| 53 |
+
"bottom": 478,
|
| 54 |
+
"left": 234.015625,
|
| 55 |
+
"right": 430.015625,
|
| 56 |
+
"contentBottom": 233,
|
| 57 |
+
"statusTop": 442,
|
| 58 |
+
"pawBottom": 470.1792,
|
| 59 |
+
"dividerCenter": 470.5,
|
| 60 |
+
"inputTop": 480,
|
| 61 |
+
"screenLeft": 23,
|
| 62 |
+
"screenRight": 438,
|
| 63 |
+
"overflow": false
|
| 64 |
+
},
|
| 65 |
+
{
|
| 66 |
+
"width": 850,
|
| 67 |
+
"height": 640,
|
| 68 |
+
"visible": true,
|
| 69 |
+
"top": 391,
|
| 70 |
+
"bottom": 495,
|
| 71 |
+
"left": 623,
|
| 72 |
+
"right": 819,
|
| 73 |
+
"contentBottom": 269,
|
| 74 |
+
"statusTop": 459,
|
| 75 |
+
"pawBottom": 487.1792,
|
| 76 |
+
"dividerCenter": 487.5,
|
| 77 |
+
"inputTop": 497,
|
| 78 |
+
"screenLeft": 19,
|
| 79 |
+
"screenRight": 825,
|
| 80 |
+
"overflow": false
|
| 81 |
+
},
|
| 82 |
+
{
|
| 83 |
+
"width": 390,
|
| 84 |
+
"height": 844,
|
| 85 |
+
"visible": true,
|
| 86 |
+
"top": 516.21875,
|
| 87 |
+
"bottom": 685,
|
| 88 |
+
"left": 163,
|
| 89 |
+
"right": 359,
|
| 90 |
+
"contentBottom": 288,
|
| 91 |
+
"statusTop": 649,
|
| 92 |
+
"pawBottom": 677.1792,
|
| 93 |
+
"dividerCenter": 677.5,
|
| 94 |
+
"inputTop": 687,
|
| 95 |
+
"screenLeft": 19,
|
| 96 |
+
"screenRight": 363,
|
| 97 |
+
"overflow": false
|
| 98 |
+
},
|
| 99 |
+
{
|
| 100 |
+
"width": 320,
|
| 101 |
+
"height": 568,
|
| 102 |
+
"visible": false,
|
| 103 |
+
"top": 0,
|
| 104 |
+
"bottom": 0,
|
| 105 |
+
"left": 0,
|
| 106 |
+
"right": 0,
|
| 107 |
+
"contentBottom": 288,
|
| 108 |
+
"statusTop": 364,
|
| 109 |
+
"pawBottom": 0,
|
| 110 |
+
"dividerCenter": 392.5,
|
| 111 |
+
"inputTop": 402,
|
| 112 |
+
"screenLeft": 19,
|
| 113 |
+
"screenRight": 301,
|
| 114 |
+
"overflow": false
|
| 115 |
+
},
|
| 116 |
+
{
|
| 117 |
+
"width": 390,
|
| 118 |
+
"height": 420,
|
| 119 |
+
"visible": false,
|
| 120 |
+
"top": 0,
|
| 121 |
+
"bottom": 0,
|
| 122 |
+
"left": 0,
|
| 123 |
+
"right": 0,
|
| 124 |
+
"contentBottom": 212,
|
| 125 |
+
"statusTop": 231,
|
| 126 |
+
"pawBottom": 0,
|
| 127 |
+
"dividerCenter": 259.5,
|
| 128 |
+
"inputTop": 269,
|
| 129 |
+
"screenLeft": 19,
|
| 130 |
+
"screenRight": 363,
|
| 131 |
+
"overflow": false
|
| 132 |
+
}
|
| 133 |
+
],
|
| 134 |
+
"errors": [],
|
| 135 |
+
"passed": true
|
| 136 |
+
}
|
validation/agent-starter-products.json
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"url": "http://127.0.0.1:4173/",
|
| 3 |
+
"checks": [
|
| 4 |
+
"Fresh workspace has six products with unit costs and stock, and the same-length profit suggestion",
|
| 5 |
+
"Older six-product sales data gains the companion file; existing files, entries, and chat remain intact; the join computes 164.53",
|
| 6 |
+
"The addition happens once: deleted files stay deleted, custom products are retained, and unrelated workspaces get no demo files"
|
| 7 |
+
],
|
| 8 |
+
"errors": [],
|
| 9 |
+
"passed": true
|
| 10 |
+
}
|