agentbox-app 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +674 -0
- package/README.md +176 -0
- package/bin/agentbox.mjs +103 -0
- package/briefs/adhd-mode.md +5 -0
- package/briefs/message-rules.md +95 -0
- package/briefs/worker.md +230 -0
- package/briefs/writing-rules.md +336 -0
- package/main/account-discovery.mjs +145 -0
- package/main/account-tooling.mjs +261 -0
- package/main/agent-activity.mjs +56 -0
- package/main/agent-files.mjs +422 -0
- package/main/agent-schedule.mjs +254 -0
- package/main/agent-sessions.mjs +435 -0
- package/main/agents.mjs +1095 -0
- package/main/analytics.mjs +206 -0
- package/main/answer-modes.mjs +206 -0
- package/main/approval-prompt-server.mjs +160 -0
- package/main/approvals.mjs +487 -0
- package/main/artifact-path.mjs +144 -0
- package/main/claude-bin.mjs +311 -0
- package/main/claude-input.mjs +117 -0
- package/main/claude-models.mjs +244 -0
- package/main/claude-plan.mjs +148 -0
- package/main/claude-usage.mjs +149 -0
- package/main/code-change.mjs +383 -0
- package/main/code-file.mjs +116 -0
- package/main/codex-account.mjs +146 -0
- package/main/codex-app-server.mjs +796 -0
- package/main/codex-approvals.mjs +712 -0
- package/main/codex-bin.mjs +261 -0
- package/main/codex-compaction.mjs +49 -0
- package/main/codex-models.mjs +194 -0
- package/main/codex-session.mjs +797 -0
- package/main/codex-threads.mjs +268 -0
- package/main/codex-usage-file.mjs +192 -0
- package/main/codex-usage.mjs +149 -0
- package/main/codex-watch.mjs +35 -0
- package/main/codex.mjs +757 -0
- package/main/config.mjs +345 -0
- package/main/copy-selection.mjs +55 -0
- package/main/crash-report.mjs +158 -0
- package/main/demo.mjs +254 -0
- package/main/dev-window.mjs +103 -0
- package/main/doc-file.mjs +99 -0
- package/main/doc-scheme.mjs +126 -0
- package/main/first-run.mjs +190 -0
- package/main/folders.mjs +91 -0
- package/main/fresh-user.mjs +120 -0
- package/main/git-change.mjs +323 -0
- package/main/img-scheme.mjs +90 -0
- package/main/instruction-settings.mjs +205 -0
- package/main/ipc.mjs +1558 -0
- package/main/live-replies.mjs +77 -0
- package/main/machine.mjs +113 -0
- package/main/main.mjs +972 -0
- package/main/menu.mjs +177 -0
- package/main/notify.mjs +155 -0
- package/main/open-source-file.mjs +8 -0
- package/main/project-identity.mjs +149 -0
- package/main/provider-commands.mjs +32 -0
- package/main/rail-note.mjs +74 -0
- package/main/repeats.mjs +261 -0
- package/main/row-label.mjs +185 -0
- package/main/screen-detail.mjs +44 -0
- package/main/serve.mjs +262 -0
- package/main/settings.mjs +912 -0
- package/main/staleness.mjs +76 -0
- package/main/store/chats.mjs +188 -0
- package/main/store/home.mjs +383 -0
- package/main/store/migrate.mjs +40 -0
- package/main/store/paths.mjs +82 -0
- package/main/store/pending-writes.mjs +122 -0
- package/main/store/project-lock.mjs +200 -0
- package/main/store/project.mjs +523 -0
- package/main/store/queues.mjs +31 -0
- package/main/store/work-items.mjs +299 -0
- package/main/store/write-queue.mjs +14 -0
- package/main/store-modules.mjs +23 -0
- package/main/store.mjs +1269 -0
- package/main/supervisor.mjs +6875 -0
- package/main/task-commands.mjs +161 -0
- package/main/task-folders-offthread.mjs +84 -0
- package/main/task-folders-thread.mjs +13 -0
- package/main/task-folders.mjs +409 -0
- package/main/task-remote-control.mjs +65 -0
- package/main/task-terminals.mjs +169 -0
- package/main/terminal-access.mjs +6 -0
- package/main/updater.mjs +240 -0
- package/main/zoom-keys.mjs +54 -0
- package/main/zoom-readout.mjs +20 -0
- package/mcp/agentbox-mcp.sh +34 -0
- package/mcp/core/account.mjs +77 -0
- package/mcp/core/chat.mjs +61 -0
- package/mcp/core/documents.mjs +238 -0
- package/mcp/core/page.mjs +152 -0
- package/mcp/core/products.mjs +110 -0
- package/mcp/core/skills.mjs +126 -0
- package/mcp/core/work.mjs +377 -0
- package/mcp/stdio.mjs +70 -0
- package/mcp/tools.mjs +257 -0
- package/package.json +186 -0
- package/renderer/dist/assets/Geist-Variable-Bj2R_7yk.woff2 +0 -0
- package/renderer/dist/assets/GeistMono-Variable-Dispecij.woff2 +0 -0
- package/renderer/dist/assets/addon-fit-YJmn1quW.js +12 -0
- package/renderer/dist/assets/addon-serialize-CsFQTvP_.js +13 -0
- package/renderer/dist/assets/cel-dusk-CS5I7kJS.webp +0 -0
- package/renderer/dist/assets/cel-dusk-DUslqS6B.webp +0 -0
- package/renderer/dist/assets/cel-dusk-Jt_xVGxp.webp +0 -0
- package/renderer/dist/assets/cel-dusk-sm-BBkV-tWT.webp +0 -0
- package/renderer/dist/assets/frost-haze-CxPSOBEg.webp +0 -0
- package/renderer/dist/assets/frost-haze-sm-C_w21qgd.webp +0 -0
- package/renderer/dist/assets/gouache-valley-ChSu14-t.webp +0 -0
- package/renderer/dist/assets/gouache-valley-d9qasI6k.webp +0 -0
- package/renderer/dist/assets/gouache-valley-sm-CdLjT5Ec.webp +0 -0
- package/renderer/dist/assets/ice-BC8luINc.webp +0 -0
- package/renderer/dist/assets/ice-CP9PrKHo.webp +0 -0
- package/renderer/dist/assets/ice-river-D6ziOhLY.webp +0 -0
- package/renderer/dist/assets/ice-river-UVM2-JY_.webp +0 -0
- package/renderer/dist/assets/ice-river-bErJwYWW.webp +0 -0
- package/renderer/dist/assets/ice-river-sm-Bg1U-SIj.webp +0 -0
- package/renderer/dist/assets/ice-sm-CbVcg3_D.webp +0 -0
- package/renderer/dist/assets/index-CwiDv5bL.css +32 -0
- package/renderer/dist/assets/index-D9hCAVPl.js +684 -0
- package/renderer/dist/assets/lake-C0WaW-6E.webp +0 -0
- package/renderer/dist/assets/lake-CSKjLC5K.webp +0 -0
- package/renderer/dist/assets/lake-ETGcOb25.webp +0 -0
- package/renderer/dist/assets/lake-harbour-BCQRbrHb.webp +0 -0
- package/renderer/dist/assets/lake-harbour-B_wfXTmE.webp +0 -0
- package/renderer/dist/assets/lake-harbour-Te1pEvBh.webp +0 -0
- package/renderer/dist/assets/lake-harbour-sm-Cme_ShEg.webp +0 -0
- package/renderer/dist/assets/lake-sm-CbG72Hbv.webp +0 -0
- package/renderer/dist/assets/lino-coast-CUZHcucI.webp +0 -0
- package/renderer/dist/assets/lino-coast-DP4J7cgd.webp +0 -0
- package/renderer/dist/assets/lino-coast-kOIAffr_.webp +0 -0
- package/renderer/dist/assets/lino-coast-sm-C1Mdbgit.webp +0 -0
- package/renderer/dist/assets/moon-pines-BskgnnXR.webp +0 -0
- package/renderer/dist/assets/moon-pines-CFSIIwt0.webp +0 -0
- package/renderer/dist/assets/moon-pines-PNhdOYww.webp +0 -0
- package/renderer/dist/assets/moon-pines-sm-C6K1NomK.webp +0 -0
- package/renderer/dist/assets/orbit-rings-2XV36Ch3.webp +0 -0
- package/renderer/dist/assets/orbit-rings-C4NrcYGQ.webp +0 -0
- package/renderer/dist/assets/orbit-rings-DGLk07dT.webp +0 -0
- package/renderer/dist/assets/orbit-rings-sm-Dky3YdvL.webp +0 -0
- package/renderer/dist/assets/peach-haze-2-C6f7xnWw.webp +0 -0
- package/renderer/dist/assets/peach-haze-2-sm-Dj_Ez_Sb.webp +0 -0
- package/renderer/dist/assets/pixel-harbour-DQjqMROg.webp +0 -0
- package/renderer/dist/assets/pixel-harbour-Dw71H_bc.webp +0 -0
- package/renderer/dist/assets/pixel-harbour-kOZfm-GM.webp +0 -0
- package/renderer/dist/assets/pixel-harbour-sm-Dturjuky.webp +0 -0
- package/renderer/dist/assets/pixel-orbit-C296JVMO.webp +0 -0
- package/renderer/dist/assets/pixel-orbit-C4oMRDSx.webp +0 -0
- package/renderer/dist/assets/pixel-orbit-CqVmQ4eh.webp +0 -0
- package/renderer/dist/assets/pixel-orbit-sm-CHagC_nO.webp +0 -0
- package/renderer/dist/assets/pixel-ridge-C6LYu6uE.webp +0 -0
- package/renderer/dist/assets/pixel-ridge-CIUCEESs.webp +0 -0
- package/renderer/dist/assets/pixel-ridge-sm-BnE0F_w-.webp +0 -0
- package/renderer/dist/assets/riso-hills-7heKVtry.webp +0 -0
- package/renderer/dist/assets/riso-hills-CIjAL3J6.webp +0 -0
- package/renderer/dist/assets/riso-hills-sm-DHsnbCfP.webp +0 -0
- package/renderer/dist/assets/slate-haze-BPWdkNsw.webp +0 -0
- package/renderer/dist/assets/slate-haze-sm-B0Cb5a2W.webp +0 -0
- package/renderer/dist/assets/source-sans-3-cyrillic-ext-wght-normal-DzyfIafT.woff2 +0 -0
- package/renderer/dist/assets/source-sans-3-cyrillic-wght-normal-BMDVbyM7.woff2 +0 -0
- package/renderer/dist/assets/source-sans-3-greek-ext-wght-normal-BWSLJLk6.woff2 +0 -0
- package/renderer/dist/assets/source-sans-3-greek-wght-normal-C9H9m1vD.woff2 +0 -0
- package/renderer/dist/assets/source-sans-3-latin-ext-wght-normal-C8iNium2.woff2 +0 -0
- package/renderer/dist/assets/source-sans-3-latin-wght-normal-BqRLTx4X.woff2 +0 -0
- package/renderer/dist/assets/source-sans-3-vietnamese-wght-normal-C1uRvKPU.woff2 +0 -0
- package/renderer/dist/assets/valley-haze-BDHPqoJy.webp +0 -0
- package/renderer/dist/assets/valley-haze-sm-CTyjmPAG.webp +0 -0
- package/renderer/dist/assets/vector-dunes-DzzWLdKo.webp +0 -0
- package/renderer/dist/assets/vector-dunes-Yr9NBQUy.webp +0 -0
- package/renderer/dist/assets/vector-dunes-sm-D8MYY6ms.webp +0 -0
- package/renderer/dist/assets/watercolour-mist-BCGKN4u4.webp +0 -0
- package/renderer/dist/assets/watercolour-mist-BF-jk5MC.webp +0 -0
- package/renderer/dist/assets/watercolour-mist-sm-Ds1xzTw9.webp +0 -0
- package/renderer/dist/assets/woodblock-sea-B1um67Z9.webp +0 -0
- package/renderer/dist/assets/woodblock-sea-DKQdYs32.webp +0 -0
- package/renderer/dist/assets/woodblock-sea-sm-BJzNq8xh.webp +0 -0
- package/renderer/dist/assets/xterm-BT7tBf8A.js +27 -0
- package/renderer/dist/emulator/audio-rules.mjs +74 -0
- package/renderer/dist/emulator/data/compression/README.md +11 -0
- package/renderer/dist/emulator/data/compression/extract7z.js +1 -0
- package/renderer/dist/emulator/data/compression/extractzip.js +1 -0
- package/renderer/dist/emulator/data/compression/libunrar.js +519 -0
- package/renderer/dist/emulator/data/compression/libunrar.wasm +0 -0
- package/renderer/dist/emulator/data/cores/README.md +22 -0
- package/renderer/dist/emulator/data/cores/mgba-legacy-wasm.data +0 -0
- package/renderer/dist/emulator/data/cores/mgba-thread-legacy-wasm.data +0 -0
- package/renderer/dist/emulator/data/cores/mgba-thread-wasm.data +0 -0
- package/renderer/dist/emulator/data/cores/mgba-wasm.data +0 -0
- package/renderer/dist/emulator/data/cores/package.json +65 -0
- package/renderer/dist/emulator/data/cores/reports/mgba.json +1 -0
- package/renderer/dist/emulator/data/emulator.css +1614 -0
- package/renderer/dist/emulator/data/loader.js +170 -0
- package/renderer/dist/emulator/data/localization/README.md +63 -0
- package/renderer/dist/emulator/data/localization/af-FR.json +339 -0
- package/renderer/dist/emulator/data/localization/ar-AR.json +301 -0
- package/renderer/dist/emulator/data/localization/ben-BEN.json +301 -0
- package/renderer/dist/emulator/data/localization/de-GER.json +301 -0
- package/renderer/dist/emulator/data/localization/el-GR.json +301 -0
- package/renderer/dist/emulator/data/localization/en-US.json +339 -0
- package/renderer/dist/emulator/data/localization/es-ES.json +310 -0
- package/renderer/dist/emulator/data/localization/fa-AF.json +310 -0
- package/renderer/dist/emulator/data/localization/hi-HI.json +301 -0
- package/renderer/dist/emulator/data/localization/it-IT.json +302 -0
- package/renderer/dist/emulator/data/localization/ja-JA.json +301 -0
- package/renderer/dist/emulator/data/localization/jv-JV.json +301 -0
- package/renderer/dist/emulator/data/localization/ko-KO.json +336 -0
- package/renderer/dist/emulator/data/localization/pt-BR.json +361 -0
- package/renderer/dist/emulator/data/localization/retroarch.json +617 -0
- package/renderer/dist/emulator/data/localization/ro-RO.json +310 -0
- package/renderer/dist/emulator/data/localization/ru-RU.json +301 -0
- package/renderer/dist/emulator/data/localization/tr-TR.json +310 -0
- package/renderer/dist/emulator/data/localization/vi-VN.json +302 -0
- package/renderer/dist/emulator/data/localization/zh-CN.json +351 -0
- package/renderer/dist/emulator/data/src/GameManager.js +464 -0
- package/renderer/dist/emulator/data/src/compression.js +142 -0
- package/renderer/dist/emulator/data/src/emulator.js +6158 -0
- package/renderer/dist/emulator/data/src/gamepad.js +167 -0
- package/renderer/dist/emulator/data/src/nipplejs.js +1 -0
- package/renderer/dist/emulator/data/src/shaders.js +236 -0
- package/renderer/dist/emulator/data/src/socket.io.min.js +7 -0
- package/renderer/dist/emulator/data/src/storage.js +125 -0
- package/renderer/dist/emulator/data/version.json +5 -0
- package/renderer/dist/emulator/host.js +299 -0
- package/renderer/dist/emulator/index.html +28 -0
- package/renderer/dist/emulator/provenance.json +6 -0
- package/renderer/dist/fixtures/onboard/designs/w-n13/the-first-row.html +1 -0
- package/renderer/dist/index.html +13 -0
- package/scripts/after-install.mjs +59 -0
- package/scripts/approval-server.sh +43 -0
- package/scripts/before-publish.mjs +133 -0
- package/shared/agent-import.d.mts +13 -0
- package/shared/agent-import.mjs +252 -0
- package/shared/agents.d.mts +190 -0
- package/shared/agents.mjs +937 -0
- package/shared/analytics-events.mjs +63 -0
- package/shared/answers.d.mts +12 -0
- package/shared/answers.mjs +80 -0
- package/shared/artifact-keys.d.mts +28 -0
- package/shared/artifact-keys.mjs +140 -0
- package/shared/bridge-map.mjs +128 -0
- package/shared/claude-commands.d.mts +20 -0
- package/shared/claude-commands.generated.mjs +149 -0
- package/shared/claude-commands.mjs +131 -0
- package/shared/claude-models.generated.d.mts +15 -0
- package/shared/claude-models.generated.mjs +26 -0
- package/shared/claude-usage.d.mts +16 -0
- package/shared/claude-usage.mjs +172 -0
- package/shared/codex-commands.d.mts +4 -0
- package/shared/codex-commands.mjs +17 -0
- package/shared/codex-import.d.mts +38 -0
- package/shared/codex-import.mjs +232 -0
- package/shared/codex-modes.mjs +159 -0
- package/shared/codex-usage.mjs +180 -0
- package/shared/contracts.mjs +412 -0
- package/shared/crash-scrub.mjs +182 -0
- package/shared/dashboard.mjs +481 -0
- package/shared/dead-run-trace.mjs +127 -0
- package/shared/demo-world.mjs +415 -0
- package/shared/effort-levels.d.mts +15 -0
- package/shared/effort-levels.mjs +76 -0
- package/shared/engines.d.mts +62 -0
- package/shared/engines.mjs +389 -0
- package/shared/first-run-examples.mjs +60 -0
- package/shared/first-run-line.mjs +149 -0
- package/shared/first-run-practice.d.mts +32 -0
- package/shared/first-run-practice.mjs +254 -0
- package/shared/first-run-shape.mjs +100 -0
- package/shared/fresh-user-home.mjs +189 -0
- package/shared/hook-failure.mjs +22 -0
- package/shared/install-id.mjs +39 -0
- package/shared/instruction-defaults.json +5 -0
- package/shared/library-folder.mjs +28 -0
- package/shared/notify-rules.mjs +120 -0
- package/shared/product-name.d.mts +6 -0
- package/shared/product-name.mjs +144 -0
- package/shared/project-folder-check.d.mts +22 -0
- package/shared/project-folder-check.mjs +152 -0
- package/shared/provider-commands.d.mts +8 -0
- package/shared/provider-commands.mjs +58 -0
- package/shared/rank.d.mts +19 -0
- package/shared/rank.mjs +146 -0
- package/shared/recovery.mjs +26 -0
- package/shared/referenced-files.d.mts +11 -0
- package/shared/referenced-files.mjs +122 -0
- package/shared/repeats.d.mts +45 -0
- package/shared/repeats.mjs +193 -0
- package/shared/schemes.mjs +33 -0
- package/shared/settings-terminal.d.mts +1 -0
- package/shared/settings-terminal.mjs +14 -0
- package/shared/side-build.mjs +57 -0
- package/shared/spawn-trouble.d.mts +8 -0
- package/shared/spawn-trouble.mjs +599 -0
- package/shared/terminal-state.d.mts +1 -0
- package/shared/terminal-state.mjs +58 -0
- package/shared/usage.d.mts +53 -0
- package/shared/usage.mjs +319 -0
- package/shared/work-items.mjs +561 -0
- package/shared/work-lines.mjs +360 -0
- package/worker-permissions.json +6 -0
package/mcp/tools.mjs
ADDED
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
// The protocol layer: tool names, schemas, descriptions, result formatting.
|
|
2
|
+
//
|
|
3
|
+
// Everything below is a thin call into mcp/core. Keeping the two apart is what
|
|
4
|
+
// lets the transport be swapped later (stdio today, HTTP when the app wants it)
|
|
5
|
+
// without touching a line of behavior, and it means core is testable without
|
|
6
|
+
// speaking MCP at all.
|
|
7
|
+
//
|
|
8
|
+
// On tool descriptions: they are the only documentation the calling model ever
|
|
9
|
+
// reads, and it is a capable model, so they say what a thing is FOR and what it
|
|
10
|
+
// costs, not a procedure to follow. Where a description states a rule, the rule
|
|
11
|
+
// is there because the file format depends on it, not because a model cannot be
|
|
12
|
+
// trusted to think.
|
|
13
|
+
|
|
14
|
+
import { z } from 'zod';
|
|
15
|
+
import { resolveAccount } from './core/account.mjs';
|
|
16
|
+
import { listProducts, getProduct, createProduct, updateProduct } from './core/products.mjs';
|
|
17
|
+
import { listDocuments, readDocument, writeDocument } from './core/documents.mjs';
|
|
18
|
+
import { listSkills, fetchSkill, fetchReference } from './core/skills.mjs';
|
|
19
|
+
import { listWorkItems, createItem, updateItem, createClaimRegistry } from './core/work.mjs';
|
|
20
|
+
import { listProductChats, readProductChat } from './core/chat.mjs';
|
|
21
|
+
import { lookAtPage } from './core/page.mjs';
|
|
22
|
+
import { WORK_ITEM_STATUSES } from '../shared/work-items.mjs';
|
|
23
|
+
import { AGENT_CREATION_KINDS } from '../shared/contracts.mjs';
|
|
24
|
+
// The descriptions below are read by every agent on every run, so the app is
|
|
25
|
+
// named in them from the one place it is named. They said "the app" while this
|
|
26
|
+
// server lived in that product's repo, which told every worker the wrong name
|
|
27
|
+
// for the thing it was writing to.
|
|
28
|
+
import { Name, possessive } from '../shared/product-name.mjs';
|
|
29
|
+
|
|
30
|
+
// What fetch_skill tells a reader it has, and what it tells them is missing.
|
|
31
|
+
// Stated once, here, so the header cannot claim a tool that was removed.
|
|
32
|
+
const CAPABILITIES = [
|
|
33
|
+
'products', 'documents (read and write, any path in the product)',
|
|
34
|
+
'personas and transcripts (they are documents)', 'skills', 'work items', 'past chats (read only)',
|
|
35
|
+
'looking at a page running on this machine (look_at_page, local addresses only)',
|
|
36
|
+
];
|
|
37
|
+
const MISSING = [
|
|
38
|
+
'create_database', 'deploy_app', 'connect_github', 'set_up_analytics',
|
|
39
|
+
'run_creation', 'browsing the web or anything she is signed into', 'ask_user',
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
const json = (value) => ({ content: [{ type: 'text', text: JSON.stringify(value, null, 2) }] });
|
|
43
|
+
const text = (s) => ({ content: [{ type: 'text', text: s }] });
|
|
44
|
+
|
|
45
|
+
// Any thrown error becomes a readable tool result rather than a protocol fault:
|
|
46
|
+
// a model that gets a sentence back can correct itself, where a transport error
|
|
47
|
+
// just ends the turn.
|
|
48
|
+
const guard = (fn) => async (args) => {
|
|
49
|
+
try { return await fn(args ?? {}); } catch (err) { return { ...text(`error: ${err?.message ?? err}`), isError: true }; }
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export function buildTools({ holder } = {}) {
|
|
53
|
+
const account = resolveAccount();
|
|
54
|
+
const claims = createClaimRegistry({ holder: holder ?? `mcp-${process.pid}` });
|
|
55
|
+
|
|
56
|
+
const tools = [
|
|
57
|
+
/* ------------------------------- products ------------------------------ */
|
|
58
|
+
{
|
|
59
|
+
name: 'list_products',
|
|
60
|
+
description: 'Every product this founder is building, with its one-line description, the path to its code repository, how many documents it has, and how much work is open, claimed, or blocked. Start here: it is the only call that shows the whole portfolio at once, and product names from it are what every other tool accepts.',
|
|
61
|
+
schema: { includeArchived: z.boolean().optional() },
|
|
62
|
+
run: ({ includeArchived }) => json(listProducts({ includeArchived })),
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
name: 'get_product',
|
|
66
|
+
description: 'One product in detail. Accepts its name, slug, or id.',
|
|
67
|
+
schema: { product: z.string() },
|
|
68
|
+
run: ({ product }) => json(getProduct(product)),
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
name: 'create_product',
|
|
72
|
+
description: `Start a new product: creates its directory in the store with the folders and starter notes the app expects, so it is indistinguishable from one created in the app itself. repoPath points at the ordinary git checkout where this product's code lives; ${Name} stores the thinking, not the software.`,
|
|
73
|
+
schema: {
|
|
74
|
+
name: z.string().describe('what the founder calls it'),
|
|
75
|
+
oneLiner: z.string().optional(),
|
|
76
|
+
repoPath: z.string().optional().describe('absolute path to the git checkout; must already exist'),
|
|
77
|
+
},
|
|
78
|
+
run: ({ name, oneLiner, repoPath }) => json(createProduct(name, { oneLiner, repoPath })),
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
name: 'update_product',
|
|
82
|
+
description: 'Change a product\'s name, one-liner, repo path, or archived flag.',
|
|
83
|
+
schema: {
|
|
84
|
+
product: z.string(),
|
|
85
|
+
name: z.string().optional(),
|
|
86
|
+
oneLiner: z.string().optional(),
|
|
87
|
+
repoPath: z.string().optional(),
|
|
88
|
+
archived: z.boolean().optional(),
|
|
89
|
+
},
|
|
90
|
+
run: ({ product, ...patch }) => json(updateProduct(product, patch)),
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
/* ------------------------------ documents ------------------------------ */
|
|
94
|
+
{
|
|
95
|
+
name: 'list_documents',
|
|
96
|
+
description: 'Everything readable in a product: titles, paths, kinds, and status, without opening anything. Personas, transcripts, the ICP, reports, notes, and designs are all documents at paths (personas/marcus.md, reports/first-run.md), so this one call finds any of them. Filter by pathPrefix to see a folder, or query to search titles and paths. Results marked cataloged false are files on disk that the founder\'s library index does not list, which is common and does not mean they are unimportant; they read exactly the same way.',
|
|
97
|
+
schema: {
|
|
98
|
+
product: z.string(),
|
|
99
|
+
folder: z.string().optional(),
|
|
100
|
+
kind: z.enum(AGENT_CREATION_KINDS).optional(),
|
|
101
|
+
status: z.enum(['current', 'history', 'exploration']).optional(),
|
|
102
|
+
pathPrefix: z.string().optional().describe('e.g. "personas/" or "reports/"'),
|
|
103
|
+
query: z.string().optional(),
|
|
104
|
+
catalogedOnly: z.boolean().optional().describe('only what the library index lists; omit to see everything'),
|
|
105
|
+
},
|
|
106
|
+
run: ({ product, ...filter }) => json(listDocuments(product, filter)),
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
name: 'read_document',
|
|
110
|
+
description: 'Read one document\'s full content, by id or by path.',
|
|
111
|
+
schema: { product: z.string(), id: z.string().optional(), path: z.string().optional() },
|
|
112
|
+
run: ({ product, id, path }) => json(readDocument(product, { id, path })),
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
name: 'write_document',
|
|
116
|
+
description: 'Create or update a document, and also how you retitle, move, or change the status of one. Pass content to write it; omit content to change only the metadata, which is how a rename happens without reading the whole document back first. A new document needs a path, a title, and content. The founder sees these in the app, so the title is what a person would call it, and the path says where it belongs (personas/, reports/, transcripts/, designs/). The extension is a real choice, not a formality: a .html document opens as a rendered page, a .md one as markdown, and which of those a founder can actually read is worth knowing before you pick.',
|
|
117
|
+
schema: {
|
|
118
|
+
product: z.string(),
|
|
119
|
+
id: z.string().optional().describe('omit to create; pass to update a known document'),
|
|
120
|
+
path: z.string().optional().describe('project-relative, e.g. "reports/kestrel-round-1.html" or "personas/marcus.md"'),
|
|
121
|
+
title: z.string().optional(),
|
|
122
|
+
kind: z.enum(AGENT_CREATION_KINDS).optional().describe('follows the path when you leave it out: .html renders as a page, anything else as markdown. Name it only when the path does not say enough (persona, transcript, canvas, app).'),
|
|
123
|
+
content: z.string().optional(),
|
|
124
|
+
status: z.enum(['current', 'history', 'exploration']).optional(),
|
|
125
|
+
},
|
|
126
|
+
run: async ({ product, ...doc }) => json(await writeDocument(product, doc)),
|
|
127
|
+
},
|
|
128
|
+
|
|
129
|
+
/* -------------------------------- skills ------------------------------- */
|
|
130
|
+
{
|
|
131
|
+
name: 'list_skills',
|
|
132
|
+
description: `The house method: the skills the in-app agent works from, covering how to ground an ICP, build a persona panel, run interviews and user tests, design, build, report findings, and act on feedback. Worth reading before doing any of that work from scratch.`,
|
|
133
|
+
schema: {},
|
|
134
|
+
run: () => json(listSkills()),
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
name: 'fetch_skill',
|
|
138
|
+
description: 'One skill in full, with a short header explaining which of its tools exist here and which are your own job. These were written for the agent inside the app, so the method applies exactly and some tool names do not.',
|
|
139
|
+
schema: { name: z.string() },
|
|
140
|
+
run: ({ name }) => json(fetchSkill(name, { capabilities: CAPABILITIES, missing: MISSING })),
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
name: 'fetch_reference',
|
|
144
|
+
description: 'A reference file that belongs to a skill, listed in that skill\'s references field. Larger than a skill and fetched separately for that reason.',
|
|
145
|
+
schema: { path: z.string().describe('e.g. "design/references/craft.md"') },
|
|
146
|
+
run: ({ path }) => text(fetchReference(path)),
|
|
147
|
+
},
|
|
148
|
+
|
|
149
|
+
/* ------------------------------ work items ----------------------------- */
|
|
150
|
+
{
|
|
151
|
+
name: 'list_work_items',
|
|
152
|
+
description: 'Open work across every product at once, or one product if you name it. This is the shared queue: several agent sessions may be running against it simultaneously, so what you read here is a snapshot, and an item you intend to act on should be claimed rather than assumed free.',
|
|
153
|
+
schema: {
|
|
154
|
+
product: z.string().optional(),
|
|
155
|
+
status: z.enum(WORK_ITEM_STATUSES).optional(),
|
|
156
|
+
kind: z.string().optional(),
|
|
157
|
+
labels: z.array(z.string()).optional(),
|
|
158
|
+
includeDone: z.boolean().optional(),
|
|
159
|
+
},
|
|
160
|
+
run: (args) => json(listWorkItems(args)),
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
name: 'create_work_item',
|
|
164
|
+
description: 'Record something that needs doing, on a product. kind and labels are free text and yours to choose; a question for the founder is a work item with kind "question". Anything you discover but cannot finish now belongs here rather than in a message, because the queue outlives this session and the message does not.',
|
|
165
|
+
schema: {
|
|
166
|
+
product: z.string(),
|
|
167
|
+
title: z.string(),
|
|
168
|
+
body: z.string().optional(),
|
|
169
|
+
kind: z.string().optional(),
|
|
170
|
+
labels: z.array(z.string()).optional(),
|
|
171
|
+
priority: z.number().int().optional().describe('higher goes first; defaults to 0'),
|
|
172
|
+
parent: z.string().optional().describe('another work item id, for a subtask'),
|
|
173
|
+
runAt: z.number().int().optional().describe('epoch ms before which nothing happens with this item: it cannot be claimed and will not start. Use it when the work genuinely belongs to a later moment. A missed moment is not lost; the item simply becomes available once it passes.'),
|
|
174
|
+
},
|
|
175
|
+
run: ({ product, ...fields }) => json(createItem(product, fields)),
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
name: 'claim_work_item',
|
|
179
|
+
description: 'Take exclusive ownership of a work item before starting it. Pass an id for a specific one, or a filter to pull the next available item (highest priority, then oldest) from any product. Returns claimed false with the current holder if someone else has it, which is an ordinary outcome: ask for the next one instead. The claim is kept alive automatically for as long as this session runs and is released if it dies, so there is nothing to renew and nothing to clean up. If an earlier session on this row lost its claim mid-write, that write comes back with the claim, in `pending`: it never reached the row or the founder, so read it and carry what is still true into your own note or result.',
|
|
180
|
+
schema: {
|
|
181
|
+
product: z.string().optional(),
|
|
182
|
+
id: z.string().optional(),
|
|
183
|
+
filter: z.object({
|
|
184
|
+
kind: z.string().optional(),
|
|
185
|
+
labels: z.array(z.string()).optional(),
|
|
186
|
+
product: z.string().optional(),
|
|
187
|
+
}).optional(),
|
|
188
|
+
},
|
|
189
|
+
run: async ({ product, id, filter }) => json(await claims.claim({ product, id, filter: filter ?? (id ? null : {}) })),
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
name: 'update_work_item',
|
|
193
|
+
description: 'Report progress on an item: set status to done when it is finished, blocked when it cannot proceed until something outside it changes, and leave a note saying where things stand. A worker the app started for a row ends with its answer as its last message, and the app writes that message onto the row as the result, so such a worker does not pass result for its own row: the same answer written twice is what she then reads twice. A row the founder wrote (labelled founder) is closed only when she asked for it: answering her is not finishing it, so the row stays open for her. To close one, pass closeBecause with her exact words asking you to; without them the rest of the write lands and the row is left open. Writing to a row this session is not holding takes the row first, so there is no need to claim before you speak; the write is only refused when another session is genuinely on it, and that refusal is kept rather than thrown away, parked where the error says and handed to whoever takes the row next. Finishing a row hands the claim straight back, and you can still answer on it afterwards.',
|
|
194
|
+
schema: {
|
|
195
|
+
id: z.string(),
|
|
196
|
+
status: z.enum(WORK_ITEM_STATUSES).optional(),
|
|
197
|
+
result: z.string().optional(),
|
|
198
|
+
note: z.string().optional(),
|
|
199
|
+
priority: z.number().int().optional(),
|
|
200
|
+
labels: z.array(z.string()).optional(),
|
|
201
|
+
runAt: z.number().int().optional().describe('epoch ms to defer this item until; 0 clears a schedule and makes it available now'),
|
|
202
|
+
closeBecause: z.string().optional().describe('only with status done on a row the founder wrote: her own words, quoted exactly from the row, asking you to close it'),
|
|
203
|
+
},
|
|
204
|
+
run: async ({ id, ...patch }) => json(await claims.update(id, patch)),
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
name: 'release_work_item',
|
|
208
|
+
description: 'Hand an item back unfinished so another session can take it. Worth doing when you find it is not what you thought, or belongs to work you are not equipped for; otherwise it stays held until this session ends.',
|
|
209
|
+
schema: { id: z.string() },
|
|
210
|
+
run: ({ id }) => json(claims.release(id)),
|
|
211
|
+
},
|
|
212
|
+
|
|
213
|
+
/* -------------------------------- chats -------------------------------- */
|
|
214
|
+
{
|
|
215
|
+
name: 'list_chats',
|
|
216
|
+
description: `Past conversations between the founder and ${possessive} own in-app agent, for one product, newest first.`,
|
|
217
|
+
schema: { product: z.string() },
|
|
218
|
+
run: ({ product }) => json(listProductChats(product)),
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
name: 'read_chat',
|
|
222
|
+
description: 'The recent end of one past conversation. Useful when something about a product looks deliberate and undocumented: the reason is often in here rather than in any document. Read-only; this server never writes to a conversation the app owns.',
|
|
223
|
+
schema: {
|
|
224
|
+
product: z.string(),
|
|
225
|
+
chatId: z.string().optional().describe('defaults to the most recent chat'),
|
|
226
|
+
limit: z.number().int().optional().describe('how many messages from the end; default 100'),
|
|
227
|
+
},
|
|
228
|
+
run: ({ product, chatId, limit }) => json(readProductChat(product, { chatId, limit })),
|
|
229
|
+
},
|
|
230
|
+
|
|
231
|
+
/* --------------------------------- pages ------------------------------- */
|
|
232
|
+
// LOOKING AT WHAT YOU BUILT, WHICH IS THE HALF OF A BROWSER THAT IS ACTUALLY
|
|
233
|
+
// MISSING (w-5ebf7bf7bb). The terminal clients this app keeps parity with
|
|
234
|
+
// mostly ship a browser now. A worker driving a browser of OURS is approved;
|
|
235
|
+
// a worker driving the one she is signed into never will be, because that
|
|
236
|
+
// one holds her logins and her cookies, and it is banned in CLAUDE.md with a
|
|
237
|
+
// test behind it. Loopback only here, because a worker that can fetch any
|
|
238
|
+
// address can post anything it has read to any address.
|
|
239
|
+
{
|
|
240
|
+
name: 'look_at_page',
|
|
241
|
+
description: `Open a page running on this machine, photograph it, and read its words back. For looking at what you just built: start the app, look at it, then name the picture in your message so she sees what you saw. Local addresses only, so it cannot reach the web or anything she is signed into. Returns the picture's path inside the product, plus the page's title and visible text.`,
|
|
242
|
+
schema: {
|
|
243
|
+
product: z.string(),
|
|
244
|
+
url: z.string().describe('a local address, as in http://localhost:3000/'),
|
|
245
|
+
width: z.number().int().optional().describe('viewport width; default 1280'),
|
|
246
|
+
height: z.number().int().optional().describe('viewport height; default 900'),
|
|
247
|
+
},
|
|
248
|
+
run: async ({ product, url, width, height }) => json(await lookAtPage({ product, url, width, height })),
|
|
249
|
+
},
|
|
250
|
+
];
|
|
251
|
+
|
|
252
|
+
return {
|
|
253
|
+
account,
|
|
254
|
+
claims,
|
|
255
|
+
tools: tools.map((t) => ({ ...t, run: guard(t.run) })),
|
|
256
|
+
};
|
|
257
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "agentbox-app",
|
|
3
|
+
"productName": "agentbox",
|
|
4
|
+
"version": "0.1.6",
|
|
5
|
+
"description": "An inbox for managing dozens of Claude Code and Codex agents.",
|
|
6
|
+
"author": "Savannah Feder",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/savannahfeder/agentbox.git"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/savannahfeder/agentbox#readme",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/savannahfeder/agentbox/issues"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"claude-code",
|
|
17
|
+
"codex",
|
|
18
|
+
"agents",
|
|
19
|
+
"ai-agents",
|
|
20
|
+
"coding-agent",
|
|
21
|
+
"inbox",
|
|
22
|
+
"cli",
|
|
23
|
+
"electron"
|
|
24
|
+
],
|
|
25
|
+
"license": "GPL-3.0-or-later",
|
|
26
|
+
"type": "module",
|
|
27
|
+
"main": "main/main.mjs",
|
|
28
|
+
"bin": {
|
|
29
|
+
"agentbox": "bin/agentbox.mjs"
|
|
30
|
+
},
|
|
31
|
+
"files": [
|
|
32
|
+
"bin/",
|
|
33
|
+
"main/**/*.mjs",
|
|
34
|
+
"shared/**/*.mjs",
|
|
35
|
+
"shared/**/*.mts",
|
|
36
|
+
"shared/**/*.json",
|
|
37
|
+
"mcp/**/*.mjs",
|
|
38
|
+
"mcp/**/*.sh",
|
|
39
|
+
"briefs/**/*.md",
|
|
40
|
+
"!briefs/founder.md",
|
|
41
|
+
"renderer/dist/",
|
|
42
|
+
"scripts/before-publish.mjs",
|
|
43
|
+
"scripts/after-install.mjs",
|
|
44
|
+
"scripts/approval-server.sh",
|
|
45
|
+
"worker-permissions.json",
|
|
46
|
+
"LICENSE",
|
|
47
|
+
"README.md"
|
|
48
|
+
],
|
|
49
|
+
"scripts": {
|
|
50
|
+
"dev": "node scripts/dev.mjs",
|
|
51
|
+
"build": "node scripts/prepare-terminal.mjs && node scripts/read-claude-commands.mjs && node scripts/read-claude-models.mjs && vite build renderer",
|
|
52
|
+
"start": "npm run build && electron .",
|
|
53
|
+
"test": "vitest run",
|
|
54
|
+
"icon": "electron scripts/make-icon.mjs",
|
|
55
|
+
"pack": "node scripts/release.mjs --no-sign",
|
|
56
|
+
"pack:side": "node scripts/pack-side.mjs",
|
|
57
|
+
"release": "node scripts/release.mjs",
|
|
58
|
+
"fresh": "node scripts/fresh-user.mjs",
|
|
59
|
+
"hooks:install": "node scripts/install-hooks.mjs",
|
|
60
|
+
"prepare": "node scripts/install-hooks.mjs",
|
|
61
|
+
"serve": "node bin/agentbox.mjs",
|
|
62
|
+
"prepack": "npm run build",
|
|
63
|
+
"postinstall": "node scripts/after-install.mjs",
|
|
64
|
+
"prepublishOnly": "node scripts/before-publish.mjs"
|
|
65
|
+
},
|
|
66
|
+
"devDependencies": {
|
|
67
|
+
"@fontsource-variable/source-sans-3": "^5.3.0",
|
|
68
|
+
"@tiptap/core": "^2.27.2",
|
|
69
|
+
"@tiptap/extension-link": "^2.27.2",
|
|
70
|
+
"@tiptap/extension-placeholder": "^2.27.2",
|
|
71
|
+
"@tiptap/extension-table": "^2.27.2",
|
|
72
|
+
"@tiptap/extension-table-cell": "^2.27.2",
|
|
73
|
+
"@tiptap/extension-table-header": "^2.27.2",
|
|
74
|
+
"@tiptap/extension-table-row": "^2.27.2",
|
|
75
|
+
"@tiptap/extension-task-item": "^2.27.2",
|
|
76
|
+
"@tiptap/extension-task-list": "^2.27.2",
|
|
77
|
+
"@tiptap/pm": "^2.27.2",
|
|
78
|
+
"@tiptap/react": "^2.27.2",
|
|
79
|
+
"@tiptap/starter-kit": "^2.27.2",
|
|
80
|
+
"@types/react": "^18.3.3",
|
|
81
|
+
"@types/react-dom": "^18.3.0",
|
|
82
|
+
"@vitejs/plugin-react": "^4.3.1",
|
|
83
|
+
"@xterm/addon-fit": "^0.11.0",
|
|
84
|
+
"@xterm/addon-serialize": "^0.14.0",
|
|
85
|
+
"@xterm/xterm": "^6.0.0",
|
|
86
|
+
"electron": "^43.0.0",
|
|
87
|
+
"electron-builder": "^26.15.3",
|
|
88
|
+
"pdfjs-dist": "^6.3.289",
|
|
89
|
+
"react": "^18.3.1",
|
|
90
|
+
"react-dom": "^18.3.1",
|
|
91
|
+
"react-markdown": "^9.0.1",
|
|
92
|
+
"remark-gfm": "^4.0.0",
|
|
93
|
+
"typescript": "^5.5.4",
|
|
94
|
+
"vite": "^5.4.0",
|
|
95
|
+
"vitest": "^2.0.5"
|
|
96
|
+
},
|
|
97
|
+
"dependencies": {
|
|
98
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
99
|
+
"electron-updater": "^6.8.9",
|
|
100
|
+
"node-pty": "^1.1.0",
|
|
101
|
+
"posthog-node": "^5.21.2",
|
|
102
|
+
"zod": "^4.4.3"
|
|
103
|
+
},
|
|
104
|
+
"build": {
|
|
105
|
+
"appId": "ac.astral.app",
|
|
106
|
+
"productName": "agentbox",
|
|
107
|
+
"copyright": "Copyright © 2026 Agentbox",
|
|
108
|
+
"directories": {
|
|
109
|
+
"output": "release",
|
|
110
|
+
"buildResources": "build"
|
|
111
|
+
},
|
|
112
|
+
"files": [
|
|
113
|
+
"main/**/*",
|
|
114
|
+
"shared/**/*",
|
|
115
|
+
"preload.cjs",
|
|
116
|
+
"briefs/**/*",
|
|
117
|
+
"!briefs/founder.md",
|
|
118
|
+
"!briefs/writing-rules.md",
|
|
119
|
+
"worker-permissions.json",
|
|
120
|
+
"scripts/approval-server.sh",
|
|
121
|
+
"renderer/dist/**/*",
|
|
122
|
+
"package.json"
|
|
123
|
+
],
|
|
124
|
+
"asarUnpack": [
|
|
125
|
+
"worker-permissions.json",
|
|
126
|
+
"scripts/approval-server.sh",
|
|
127
|
+
"node_modules/node-pty/**"
|
|
128
|
+
],
|
|
129
|
+
"mac": {
|
|
130
|
+
"extendInfo": {
|
|
131
|
+
"NSDesktopFolderUsageDescription": "Agentbox needs this to let your agents work in a project you keep on the Desktop.",
|
|
132
|
+
"NSDocumentsFolderUsageDescription": "Agentbox needs this to let your agents work in a project you keep in Documents.",
|
|
133
|
+
"NSDownloadsFolderUsageDescription": "Agentbox needs this to let your agents work in a project you keep in Downloads.",
|
|
134
|
+
"NSRemovableVolumesUsageDescription": "Agentbox needs this to let your agents work in a project you keep on an external disk.",
|
|
135
|
+
"NSAppleEventsUsageDescription": "Agentbox needs this to place the app you rest in, and when an agent you started asks another app to do something."
|
|
136
|
+
},
|
|
137
|
+
"category": "public.app-category.productivity",
|
|
138
|
+
"icon": "build/icon.icns",
|
|
139
|
+
"target": [
|
|
140
|
+
{
|
|
141
|
+
"target": "dmg",
|
|
142
|
+
"arch": [
|
|
143
|
+
"universal"
|
|
144
|
+
]
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
"target": "zip",
|
|
148
|
+
"arch": [
|
|
149
|
+
"universal"
|
|
150
|
+
]
|
|
151
|
+
}
|
|
152
|
+
],
|
|
153
|
+
"hardenedRuntime": true,
|
|
154
|
+
"gatekeeperAssess": false,
|
|
155
|
+
"entitlements": "build/entitlements.mac.plist",
|
|
156
|
+
"entitlementsInherit": "build/entitlements.mac.plist",
|
|
157
|
+
"notarize": false
|
|
158
|
+
},
|
|
159
|
+
"dmg": {
|
|
160
|
+
"title": "agentbox",
|
|
161
|
+
"contents": [
|
|
162
|
+
{
|
|
163
|
+
"x": 140,
|
|
164
|
+
"y": 200,
|
|
165
|
+
"type": "file"
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
"x": 400,
|
|
169
|
+
"y": 200,
|
|
170
|
+
"type": "link",
|
|
171
|
+
"path": "/Applications"
|
|
172
|
+
}
|
|
173
|
+
]
|
|
174
|
+
},
|
|
175
|
+
"publish": [
|
|
176
|
+
{
|
|
177
|
+
"provider": "github",
|
|
178
|
+
"owner": "Astral-Agent",
|
|
179
|
+
"repo": "astral-releases"
|
|
180
|
+
}
|
|
181
|
+
]
|
|
182
|
+
},
|
|
183
|
+
"engines": {
|
|
184
|
+
"node": ">=22"
|
|
185
|
+
}
|
|
186
|
+
}
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2014-2024 The xterm.js authors. All rights reserved.
|
|
3
|
+
* @license MIT
|
|
4
|
+
*
|
|
5
|
+
* Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)
|
|
6
|
+
* @license MIT
|
|
7
|
+
*
|
|
8
|
+
* Originally forked from (with the author's permission):
|
|
9
|
+
* Fabrice Bellard's javascript vt100 for jslinux:
|
|
10
|
+
* http://bellard.org/jslinux/
|
|
11
|
+
* Copyright (c) 2011 Fabrice Bellard
|
|
12
|
+
*/var c=2,d=1,_=class{activate(e){this._terminal=e}dispose(){}fit(){let e=this.proposeDimensions();if(!e||!this._terminal||isNaN(e.cols)||isNaN(e.rows))return;let i=this._terminal._core;(this._terminal.rows!==e.rows||this._terminal.cols!==e.cols)&&(i._renderService.clear(),this._terminal.resize(e.cols,e.rows))}proposeDimensions(){var l;if(!this._terminal||!this._terminal.element||!this._terminal.element.parentElement)return;let e=this._terminal._core._renderService.dimensions;if(e.css.cell.width===0||e.css.cell.height===0)return;let i=this._terminal.options.scrollback===0?0:((l=this._terminal.options.overviewRuler)==null?void 0:l.width)||14,s=window.getComputedStyle(this._terminal.element.parentElement),o=parseInt(s.getPropertyValue("height")),a=Math.max(0,parseInt(s.getPropertyValue("width"))),t=window.getComputedStyle(this._terminal.element),r={top:parseInt(t.getPropertyValue("padding-top")),bottom:parseInt(t.getPropertyValue("padding-bottom")),right:parseInt(t.getPropertyValue("padding-right")),left:parseInt(t.getPropertyValue("padding-left"))},n=r.top+r.bottom,h=r.right+r.left,m=o-n,p=a-h-i;return{cols:Math.max(c,Math.floor(p/e.css.cell.width)),rows:Math.max(d,Math.floor(m/e.css.cell.height))}}};export{_ as FitAddon};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2014-2024 The xterm.js authors. All rights reserved.
|
|
3
|
+
* @license MIT
|
|
4
|
+
*
|
|
5
|
+
* Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)
|
|
6
|
+
* @license MIT
|
|
7
|
+
*
|
|
8
|
+
* Originally forked from (with the author's permission):
|
|
9
|
+
* Fabrice Bellard's javascript vt100 for jslinux:
|
|
10
|
+
* http://bellard.org/jslinux/
|
|
11
|
+
* Copyright (c) 2011 Fabrice Bellard
|
|
12
|
+
*/var d=0,p=0,b=0,g=0,w;(t=>{function r(i,l,s,h){return h!==void 0?`#${v(i)}${v(l)}${v(s)}${v(h)}`:`#${v(i)}${v(l)}${v(s)}`}t.toCss=r;function e(i,l,s,h=255){return(i<<24|l<<16|s<<8|h)>>>0}t.toRgba=e;function n(i,l,s,h){return{css:t.toCss(i,l,s,h),rgba:t.toRgba(i,l,s,h)}}t.toColor=n})(w||(w={}));var F;(t=>{function r(a,o){if(g=(o.rgba&255)/255,g===1)return{css:o.css,rgba:o.rgba};let c=o.rgba>>24&255,_=o.rgba>>16&255,f=o.rgba>>8&255,u=a.rgba>>24&255,C=a.rgba>>16&255,x=a.rgba>>8&255;d=u+Math.round((c-u)*g),p=C+Math.round((_-C)*g),b=x+Math.round((f-x)*g);let M=w.toCss(d,p,b),$=w.toRgba(d,p,b);return{css:M,rgba:$}}t.blend=r;function e(a){return(a.rgba&255)===255}t.isOpaque=e;function n(a,o,c){let _=S.ensureContrastRatio(a.rgba,o.rgba,c);if(_)return w.toColor(_>>24&255,_>>16&255,_>>8&255)}t.ensureContrastRatio=n;function i(a){let o=(a.rgba|255)>>>0;return[d,p,b]=S.toChannels(o),{css:w.toCss(d,p,b),rgba:o}}t.opaque=i;function l(a,o){return g=Math.round(o*255),[d,p,b]=S.toChannels(a.rgba),{css:w.toCss(d,p,b,g),rgba:w.toRgba(d,p,b,g)}}t.opacity=l;function s(a,o){return g=a.rgba&255,l(a,g*o/255)}t.multiplyOpacity=s;function h(a){return[a.rgba>>24&255,a.rgba>>16&255,a.rgba>>8&255]}t.toColorRGB=h})(F||(F={}));var m;(t=>{let r,e;try{let i=document.createElement("canvas");i.width=1,i.height=1;let l=i.getContext("2d",{willReadFrequently:!0});l&&(r=l,r.globalCompositeOperation="copy",e=r.createLinearGradient(0,0,1,1))}catch{}function n(i){if(i.match(/#[\da-f]{3,8}/i))switch(i.length){case 4:return d=parseInt(i.slice(1,2).repeat(2),16),p=parseInt(i.slice(2,3).repeat(2),16),b=parseInt(i.slice(3,4).repeat(2),16),w.toColor(d,p,b);case 5:return d=parseInt(i.slice(1,2).repeat(2),16),p=parseInt(i.slice(2,3).repeat(2),16),b=parseInt(i.slice(3,4).repeat(2),16),g=parseInt(i.slice(4,5).repeat(2),16),w.toColor(d,p,b,g);case 7:return{css:i,rgba:(parseInt(i.slice(1),16)<<8|255)>>>0};case 9:return{css:i,rgba:parseInt(i.slice(1),16)>>>0}}let l=i.match(/rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(,\s*(0|1|\d?\.(\d+))\s*)?\)/);if(l)return d=parseInt(l[1]),p=parseInt(l[2]),b=parseInt(l[3]),g=Math.round((l[5]===void 0?1:parseFloat(l[5]))*255),w.toColor(d,p,b,g);if(!r||!e)throw new Error("css.toColor: Unsupported css format");if(r.fillStyle=e,r.fillStyle=i,typeof r.fillStyle!="string")throw new Error("css.toColor: Unsupported css format");if(r.fillRect(0,0,1,1),[d,p,b,g]=r.getImageData(0,0,1,1).data,g!==255)throw new Error("css.toColor: Unsupported css format");return{rgba:w.toRgba(d,p,b,g),css:i}}t.toColor=n})(m||(m={}));var B;(t=>{function r(n){return e(n>>16&255,n>>8&255,n&255)}t.relativeLuminance=r;function e(n,i,l){let s=n/255,h=i/255,a=l/255,o=s<=.03928?s/12.92:Math.pow((s+.055)/1.055,2.4),c=h<=.03928?h/12.92:Math.pow((h+.055)/1.055,2.4),_=a<=.03928?a/12.92:Math.pow((a+.055)/1.055,2.4);return o*.2126+c*.7152+_*.0722}t.relativeLuminance2=e})(B||(B={}));var S;(t=>{function r(s,h){if(g=(h&255)/255,g===1)return h;let a=h>>24&255,o=h>>16&255,c=h>>8&255,_=s>>24&255,f=s>>16&255,u=s>>8&255;return d=_+Math.round((a-_)*g),p=f+Math.round((o-f)*g),b=u+Math.round((c-u)*g),w.toRgba(d,p,b)}t.blend=r;function e(s,h,a){let o=B.relativeLuminance(s>>8),c=B.relativeLuminance(h>>8);if(R(o,c)<a){if(c<o){let u=n(s,h,a),C=R(o,B.relativeLuminance(u>>8));if(C<a){let x=i(s,h,a),M=R(o,B.relativeLuminance(x>>8));return C>M?u:x}return u}let _=i(s,h,a),f=R(o,B.relativeLuminance(_>>8));if(f<a){let u=n(s,h,a),C=R(o,B.relativeLuminance(u>>8));return f>C?_:u}return _}}t.ensureContrastRatio=e;function n(s,h,a){let o=s>>24&255,c=s>>16&255,_=s>>8&255,f=h>>24&255,u=h>>16&255,C=h>>8&255,x=R(B.relativeLuminance2(f,u,C),B.relativeLuminance2(o,c,_));for(;x<a&&(f>0||u>0||C>0);)f-=Math.max(0,Math.ceil(f*.1)),u-=Math.max(0,Math.ceil(u*.1)),C-=Math.max(0,Math.ceil(C*.1)),x=R(B.relativeLuminance2(f,u,C),B.relativeLuminance2(o,c,_));return(f<<24|u<<16|C<<8|255)>>>0}t.reduceLuminance=n;function i(s,h,a){let o=s>>24&255,c=s>>16&255,_=s>>8&255,f=h>>24&255,u=h>>16&255,C=h>>8&255,x=R(B.relativeLuminance2(f,u,C),B.relativeLuminance2(o,c,_));for(;x<a&&(f<255||u<255||C<255);)f=Math.min(255,f+Math.ceil((255-f)*.1)),u=Math.min(255,u+Math.ceil((255-u)*.1)),C=Math.min(255,C+Math.ceil((255-C)*.1)),x=R(B.relativeLuminance2(f,u,C),B.relativeLuminance2(o,c,_));return(f<<24|u<<16|C<<8|255)>>>0}t.increaseLuminance=i;function l(s){return[s>>24&255,s>>16&255,s>>8&255,s&255]}t.toChannels=l})(S||(S={}));function v(t){let r=t.toString(16);return r.length<2?"0"+r:r}function R(t,r){return t<r?(r+.05)/(t+.05):(t+.05)/(r+.05)}var A=Object.freeze((()=>{let t=[m.toColor("#2e3436"),m.toColor("#cc0000"),m.toColor("#4e9a06"),m.toColor("#c4a000"),m.toColor("#3465a4"),m.toColor("#75507b"),m.toColor("#06989a"),m.toColor("#d3d7cf"),m.toColor("#555753"),m.toColor("#ef2929"),m.toColor("#8ae234"),m.toColor("#fce94f"),m.toColor("#729fcf"),m.toColor("#ad7fa8"),m.toColor("#34e2e2"),m.toColor("#eeeeec")],r=[0,95,135,175,215,255];for(let e=0;e<216;e++){let n=r[e/36%6|0],i=r[e/6%6|0],l=r[e%6];t.push({css:w.toCss(n,i,l),rgba:w.toRgba(n,i,l)})}for(let e=0;e<24;e++){let n=8+e*10;t.push({css:w.toCss(n,n,n),rgba:w.toRgba(n,n,n)})}return t})());function L(t,r,e){return Math.max(r,Math.min(t,e))}function D(t){switch(t){case"&":return"&";case"<":return"<"}return t}var k=class{constructor(t){this._buffer=t}serialize(t,r){let e=this._buffer.getNullCell(),n=this._buffer.getNullCell(),i=e,l=t.start.y,s=t.end.y,h=t.start.x,a=t.end.x;this._beforeSerialize(s-l,l,s);for(let o=l;o<=s;o++){let c=this._buffer.getLine(o);if(c){let _=o===t.start.y?h:0,f=o===t.end.y?a:c.length;for(let u=_;u<f;u++){let C=c.getCell(u,i===e?n:e);if(!C){console.warn(`Can't get cell at row=${o}, col=${u}`);continue}this._nextCell(C,i,o,u),i=C}}this._rowEnd(o,o===s)}return this._afterSerialize(),this._serializeString(r)}_nextCell(t,r,e,n){}_rowEnd(t,r){}_beforeSerialize(t,r,e){}_afterSerialize(){}_serializeString(t){return""}};function I(t,r){return t.getFgColorMode()===r.getFgColorMode()&&t.getFgColor()===r.getFgColor()}function y(t,r){return t.getBgColorMode()===r.getBgColorMode()&&t.getBgColor()===r.getBgColor()}function z(t,r){return t.isInverse()===r.isInverse()&&t.isBold()===r.isBold()&&t.isUnderline()===r.isUnderline()&&t.isOverline()===r.isOverline()&&t.isBlink()===r.isBlink()&&t.isInvisible()===r.isInvisible()&&t.isItalic()===r.isItalic()&&t.isDim()===r.isDim()&&t.isStrikethrough()===r.isStrikethrough()}var E=class extends k{constructor(t,r){super(t),this._terminal=r,this._rowIndex=0,this._allRows=new Array,this._allRowSeparators=new Array,this._currentRow="",this._nullCellCount=0,this._cursorStyle=this._buffer.getNullCell(),this._cursorStyleRow=0,this._cursorStyleCol=0,this._backgroundCell=this._buffer.getNullCell(),this._firstRow=0,this._lastCursorRow=0,this._lastCursorCol=0,this._lastContentCursorRow=0,this._lastContentCursorCol=0,this._thisRowLastChar=this._buffer.getNullCell(),this._thisRowLastSecondChar=this._buffer.getNullCell(),this._nextRowFirstChar=this._buffer.getNullCell()}_beforeSerialize(t,r,e){this._allRows=new Array(t),this._lastContentCursorRow=r,this._lastCursorRow=r,this._firstRow=r}_rowEnd(t,r){var n;this._nullCellCount>0&&!y(this._cursorStyle,this._backgroundCell)&&(this._currentRow+=`\x1B[${this._nullCellCount}X`);let e="";if(!r){t-this._firstRow>=this._terminal.rows&&((n=this._buffer.getLine(this._cursorStyleRow))==null||n.getCell(this._cursorStyleCol,this._backgroundCell));let i=this._buffer.getLine(t),l=this._buffer.getLine(t+1);if(!l.isWrapped)e=`\r
|
|
13
|
+
`,this._lastCursorRow=t+1,this._lastCursorCol=0;else{e="";let s=i.getCell(i.length-1,this._thisRowLastChar),h=i.getCell(i.length-2,this._thisRowLastSecondChar),a=l.getCell(0,this._nextRowFirstChar),o=a.getWidth()>1,c=!1;(a.getChars()&&o?this._nullCellCount<=1:this._nullCellCount<=0)&&((s.getChars()||s.getWidth()===0)&&y(s,a)&&(c=!0),o&&(h.getChars()||h.getWidth()===0)&&y(s,a)&&y(h,a)&&(c=!0)),c||(e="-".repeat(this._nullCellCount+1),e+="\x1B[1D\x1B[1X",this._nullCellCount>0&&(e+="\x1B[A",e+=`\x1B[${i.length-this._nullCellCount}C`,e+=`\x1B[${this._nullCellCount}X`,e+=`\x1B[${i.length-this._nullCellCount}D`,e+="\x1B[B"),this._lastContentCursorRow=t+1,this._lastContentCursorCol=0,this._lastCursorRow=t+1,this._lastCursorCol=0)}}this._allRows[this._rowIndex]=this._currentRow,this._allRowSeparators[this._rowIndex++]=e,this._currentRow="",this._nullCellCount=0}_diffStyle(t,r){let e=[],n=!I(t,r),i=!y(t,r),l=!z(t,r);if(n||i||l)if(t.isAttributeDefault())r.isAttributeDefault()||e.push(0);else{if(n){let s=t.getFgColor();t.isFgRGB()?e.push(38,2,s>>>16&255,s>>>8&255,s&255):t.isFgPalette()?s>=16?e.push(38,5,s):e.push(s&8?90+(s&7):30+(s&7)):e.push(39)}if(i){let s=t.getBgColor();t.isBgRGB()?e.push(48,2,s>>>16&255,s>>>8&255,s&255):t.isBgPalette()?s>=16?e.push(48,5,s):e.push(s&8?100+(s&7):40+(s&7)):e.push(49)}l&&(t.isInverse()!==r.isInverse()&&e.push(t.isInverse()?7:27),t.isBold()!==r.isBold()&&e.push(t.isBold()?1:22),t.isUnderline()!==r.isUnderline()&&e.push(t.isUnderline()?4:24),t.isOverline()!==r.isOverline()&&e.push(t.isOverline()?53:55),t.isBlink()!==r.isBlink()&&e.push(t.isBlink()?5:25),t.isInvisible()!==r.isInvisible()&&e.push(t.isInvisible()?8:28),t.isItalic()!==r.isItalic()&&e.push(t.isItalic()?3:23),t.isDim()!==r.isDim()&&e.push(t.isDim()?2:22),t.isStrikethrough()!==r.isStrikethrough()&&e.push(t.isStrikethrough()?9:29))}return e}_nextCell(t,r,e,n){if(t.getWidth()===0)return;let i=t.getChars()==="",l=this._diffStyle(t,this._cursorStyle);if(i?!y(this._cursorStyle,t):l.length>0){this._nullCellCount>0&&(y(this._cursorStyle,this._backgroundCell)||(this._currentRow+=`\x1B[${this._nullCellCount}X`),this._currentRow+=`\x1B[${this._nullCellCount}C`,this._nullCellCount=0),this._lastContentCursorRow=this._lastCursorRow=e,this._lastContentCursorCol=this._lastCursorCol=n,this._currentRow+=`\x1B[${l.join(";")}m`;let s=this._buffer.getLine(e);s!==void 0&&(s.getCell(n,this._cursorStyle),this._cursorStyleRow=e,this._cursorStyleCol=n)}i?this._nullCellCount+=t.getWidth():(this._nullCellCount>0&&(y(this._cursorStyle,this._backgroundCell)?this._currentRow+=`\x1B[${this._nullCellCount}C`:(this._currentRow+=`\x1B[${this._nullCellCount}X`,this._currentRow+=`\x1B[${this._nullCellCount}C`),this._nullCellCount=0),this._currentRow+=t.getChars(),this._lastContentCursorRow=this._lastCursorRow=e,this._lastContentCursorCol=this._lastCursorCol=n+t.getWidth())}_serializeString(t){let r=this._allRows.length;this._buffer.length-this._firstRow<=this._terminal.rows&&(r=this._lastContentCursorRow+1-this._firstRow,this._lastCursorCol=this._lastContentCursorCol,this._lastCursorRow=this._lastContentCursorRow);let e="";for(let l=0;l<r;l++)e+=this._allRows[l],l+1<r&&(e+=this._allRowSeparators[l]);if(!t){let l=this._buffer.baseY+this._buffer.cursorY,s=this._buffer.cursorX,h=l!==this._lastCursorRow||s!==this._lastCursorCol,a=o=>{o>0?e+=`\x1B[${o}C`:o<0&&(e+=`\x1B[${-o}D`)};h&&((o=>{o>0?e+=`\x1B[${o}B`:o<0&&(e+=`\x1B[${-o}A`)})(l-this._lastCursorRow),a(s-this._lastCursorCol))}let n=this._terminal._core._inputHandler._curAttrData,i=this._diffStyle(n,this._cursorStyle);return i.length>0&&(e+=`\x1B[${i.join(";")}m`),e}},H=class{activate(t){this._terminal=t}_serializeBufferByScrollback(t,r,e){let n=r.length,i=e===void 0?n:L(e+t.rows,0,n);return this._serializeBufferByRange(t,r,{start:n-i,end:n-1},!1)}_serializeBufferByRange(t,r,e,n){return new E(r,t).serialize({start:{x:0,y:typeof e.start=="number"?e.start:e.start.line},end:{x:t.cols,y:typeof e.end=="number"?e.end:e.end.line}},n)}_serializeBufferAsHTML(t,r){var h;let e=t.buffer.active,n=new O(e,t,r),i=r.onlySelection??!1,l=r.range;if(l)return n.serialize({start:{x:l.startCol,y:(l.startLine,l.startLine)},end:{x:t.cols,y:(l.endLine,l.endLine)}});if(!i){let a=e.length,o=r.scrollback,c=o===void 0?a:L(o+t.rows,0,a);return n.serialize({start:{x:0,y:a-c},end:{x:t.cols,y:a-1}})}let s=(h=this._terminal)==null?void 0:h.getSelectionPosition();return s!==void 0?n.serialize({start:{x:s.start.x,y:s.start.y},end:{x:s.end.x,y:s.end.y}}):""}_serializeModes(t){let r="",e=t.modes;if(e.applicationCursorKeysMode&&(r+="\x1B[?1h"),e.applicationKeypadMode&&(r+="\x1B[?66h"),e.bracketedPasteMode&&(r+="\x1B[?2004h"),e.insertMode&&(r+="\x1B[4h"),e.originMode&&(r+="\x1B[?6h"),e.reverseWraparoundMode&&(r+="\x1B[?45h"),e.sendFocusMode&&(r+="\x1B[?1004h"),e.wraparoundMode===!1&&(r+="\x1B[?7l"),e.mouseTrackingMode!=="none")switch(e.mouseTrackingMode){case"x10":r+="\x1B[?9h";break;case"vt200":r+="\x1B[?1000h";break;case"drag":r+="\x1B[?1002h";break;case"any":r+="\x1B[?1003h";break}return r}serialize(t){if(!this._terminal)throw new Error("Cannot use addon until it has been loaded");let r=t!=null&&t.range?this._serializeBufferByRange(this._terminal,this._terminal.buffer.normal,t.range,!0):this._serializeBufferByScrollback(this._terminal,this._terminal.buffer.normal,t==null?void 0:t.scrollback);if(!(t!=null&&t.excludeAltBuffer)&&this._terminal.buffer.active.type==="alternate"){let e=this._serializeBufferByScrollback(this._terminal,this._terminal.buffer.alternate,void 0);r+=`\x1B[?1049h\x1B[H${e}`}return t!=null&&t.excludeModes||(r+=this._serializeModes(this._terminal)),r}serializeAsHTML(t){if(!this._terminal)throw new Error("Cannot use addon until it has been loaded");return this._serializeBufferAsHTML(this._terminal,t||{})}dispose(){}},O=class extends k{constructor(t,r,e){super(t),this._terminal=r,this._options=e,this._currentRow="",this._htmlContent="",r._core._themeService?this._ansiColors=r._core._themeService.colors.ansi:this._ansiColors=A}_padStart(t,r,e){return r=r>>0,e=e??" ",t.length>r?t:(r-=t.length,r>e.length&&(e+=e.repeat(r/e.length)),e.slice(0,r)+t)}_beforeSerialize(t,r,e){var s,h;this._htmlContent+="<html><body><!--StartFragment--><pre>";let n="#000000",i="#ffffff";(this._options.includeGlobalBackground??!1)&&(n=((s=this._terminal.options.theme)==null?void 0:s.foreground)??"#ffffff",i=((h=this._terminal.options.theme)==null?void 0:h.background)??"#000000");let l=[];l.push("color: "+n+";"),l.push("background-color: "+i+";"),l.push("font-family: "+this._terminal.options.fontFamily+";"),l.push("font-size: "+this._terminal.options.fontSize+"px;"),this._htmlContent+="<div style='"+l.join(" ")+"'>"}_afterSerialize(){this._htmlContent+="</div>",this._htmlContent+="</pre><!--EndFragment--></body></html>"}_rowEnd(t,r){this._htmlContent+="<div><span>"+this._currentRow+"</span></div>",this._currentRow=""}_getHexColor(t,r){let e=r?t.getFgColor():t.getBgColor();if(r?t.isFgRGB():t.isBgRGB())return"#"+[e>>16&255,e>>8&255,e&255].map(n=>this._padStart(n.toString(16),2,"0")).join("");if(r?t.isFgPalette():t.isBgPalette())return this._ansiColors[e].css}_diffStyle(t,r){let e=[],n=!I(t,r),i=!y(t,r),l=!z(t,r);if(n||i||l){let s=this._getHexColor(t,!0);s&&e.push("color: "+s+";");let h=this._getHexColor(t,!1);return h&&e.push("background-color: "+h+";"),t.isInverse()&&e.push("color: #000000; background-color: #BFBFBF;"),t.isBold()&&e.push("font-weight: bold;"),t.isUnderline()&&t.isOverline()?e.push("text-decoration: overline underline;"):t.isUnderline()?e.push("text-decoration: underline;"):t.isOverline()&&e.push("text-decoration: overline;"),t.isBlink()&&e.push("text-decoration: blink;"),t.isInvisible()&&e.push("visibility: hidden;"),t.isItalic()&&e.push("font-style: italic;"),t.isDim()&&e.push("opacity: 0.5;"),t.isStrikethrough()&&e.push("text-decoration: line-through;"),e}}_nextCell(t,r,e,n){if(t.getWidth()===0)return;let i=t.getChars()==="",l=this._diffStyle(t,r);l&&(this._currentRow+=l.length===0?"</span><span>":"</span><span style='"+l.join(" ")+"'>"),i?this._currentRow+=" ":this._currentRow+=D(t.getChars())}_serializeString(){return this._htmlContent}};export{O as HTMLSerializeHandler,H as SerializeAddon};
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|