windows-development-sandbox 1.11.7 → 1.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -6
- package/config.example.json +4 -3
- package/dist/app/features/apps/apps-rows.d.ts +4 -1
- package/dist/app/features/apps/apps-rows.js +7 -2
- package/dist/app/features/apps/apps.def.d.ts +19 -0
- package/dist/app/features/apps/apps.def.js +49 -12
- package/dist/app/features/apps/apps.run.d.ts +2 -0
- package/dist/app/features/apps/apps.run.js +1 -1
- package/dist/app/features/audit/audit.def.d.ts +341 -0
- package/dist/app/features/audit/audit.def.js +454 -0
- package/dist/app/features/audit/audit.run.d.ts +107 -0
- package/dist/app/features/audit/audit.run.js +133 -0
- package/dist/app/features/audit/index.d.ts +2 -0
- package/dist/app/features/audit/index.js +3 -0
- package/dist/app/features/defs.d.ts +194 -1
- package/dist/app/features/defs.js +4 -0
- package/dist/app/features/settings/settings.run.d.ts +3 -0
- package/dist/app/features/settings/settings.run.js +16 -2
- package/dist/app/kernel/define.d.ts +12 -1
- package/dist/app/registry.js +2 -0
- package/dist/cli/args.d.ts +0 -3
- package/dist/cli/args.js +3 -4
- package/dist/cli/commands/index.d.ts +1 -1
- package/dist/cli/commands/index.js +1 -1
- package/dist/cli/commands/launch/prepare.js +1 -1
- package/dist/cli/commands/maintenance/index.d.ts +0 -1
- package/dist/cli/commands/maintenance/index.js +0 -1
- package/dist/cli/commands/registry.js +1 -2
- package/dist/cli/manifest.d.ts +0 -41
- package/dist/cli/manifest.js +0 -14
- package/dist/cli/menu/pages/apps-page.js +2 -15
- package/dist/cli/menu/pages/maintenance-page.d.ts +4 -0
- package/dist/cli/menu/pages/maintenance-page.js +51 -8
- package/dist/cli/menu/views.js +2 -0
- package/dist/core/config/defaults.js +1 -1
- package/dist/core/schema/apps.d.ts +9 -0
- package/dist/core/schema/apps.js +9 -0
- package/dist/core/schema/config.d.ts +1 -0
- package/dist/core/schema/config.js +3 -3
- package/dist/core/schema/sandbox.d.ts +1 -0
- package/dist/core/schema/sandbox.js +11 -3
- package/dist/guest/apps/app-installer.js +50 -4
- package/dist/guest/apps/index.d.ts +1 -1
- package/dist/guest/apps/index.js +1 -1
- package/dist/guest/apps/sources.js +1 -1
- package/dist/guest/audit/parse.d.ts +32 -2
- package/dist/guest/audit/parse.js +43 -5
- package/dist/guest/audit/process-audit.d.ts +3 -1
- package/dist/guest/audit/process-audit.js +9 -6
- package/dist/guest/boot/boot.js +12 -3
- package/dist/guest/boot/guide.d.ts +1 -1
- package/dist/guest/boot/guide.js +27 -3
- package/dist/guest/daemon/sessions.d.ts +7 -0
- package/dist/guest/daemon/sessions.js +27 -2
- package/dist/guest/entries/daemon.js +2 -1
- package/dist/guest/setup/addon-mcp.d.ts +2 -0
- package/dist/guest/setup/addon-mcp.js +49 -0
- package/dist/guest/setup/agent-addons.d.ts +9 -0
- package/dist/guest/setup/agent-addons.js +22 -0
- package/dist/guest/setup/headroom.d.ts +7 -0
- package/dist/guest/setup/headroom.js +60 -0
- package/dist/guest/setup/managed-policy.d.ts +2 -1
- package/dist/guest/setup/managed-policy.js +15 -6
- package/dist/guest/toolchains/plan.js +8 -3
- package/dist/host/audit/attribute.d.ts +46 -0
- package/dist/host/audit/attribute.js +76 -0
- package/dist/host/audit/collect.d.ts +29 -3
- package/dist/host/audit/collect.js +256 -61
- package/dist/host/audit/git-commits.d.ts +29 -0
- package/dist/host/audit/git-commits.js +157 -0
- package/dist/host/audit/index.d.ts +5 -0
- package/dist/host/audit/index.js +6 -0
- package/dist/host/audit/log.d.ts +44 -7
- package/dist/host/audit/log.js +148 -45
- package/dist/host/audit/redact.d.ts +2 -0
- package/dist/host/audit/redact.js +67 -0
- package/dist/runtime/agent.js +110 -15
- package/dist/runtime/daemon.js +569 -316
- package/dist/runtime/db.js +110 -15
- package/dist/runtime/main.js +1011 -672
- package/dist/runtime/netproxy.js +110 -15
- package/dist/runtime/view.js +110 -15
- package/dist/selftest/app.test.js +25 -1
- package/dist/selftest/audit.test.js +354 -47
- package/dist/selftest/hcs.test.js +35 -0
- package/dist/selftest/menu-screens.js +1 -1
- package/dist/selftest/menu.test.js +3 -3
- package/dist/selftest/support/engine-golden.js +9 -0
- package/dist/selftest/support/fixtures.d.ts +1 -0
- package/dist/selftest/support/web-preview.js +94 -0
- package/dist/selftest/toolchains.test.js +147 -2
- package/dist/shared/apps/agent-addons.d.ts +64 -0
- package/dist/shared/apps/agent-addons.js +95 -0
- package/dist/shared/apps/catalogue.d.ts +33 -2
- package/dist/shared/apps/catalogue.js +102 -11
- package/dist/shared/apps/index.d.ts +3 -2
- package/dist/shared/apps/index.js +3 -2
- package/dist/shared/apps/selection.d.ts +2 -0
- package/dist/shared/apps/selection.js +7 -0
- package/dist/sync/watcher.js +9 -1
- package/dist/web/index.html +2 -2
- package/dist/web/{main-RQFN4WBY.css → main-UAQPI5XD.css} +1 -1
- package/dist/web/{main-K2JMCAZI.js → main-UD4QCXZR.js} +66 -66
- package/native/csharp/HcsHost/AssemblyInfo.cs +5 -3
- package/native/csharp/HcsHost/Hcs.cs +9 -0
- package/native/csharp/HcsHost/Program.cs +26 -1
- package/native/csharp/HcsHost/Share.cs +173 -0
- package/native/csharp/HostTools/AssemblyInfo.cs +3 -3
- package/package.json +1 -1
- package/sandbox/addons/ccusage/package-lock.json +111 -0
- package/sandbox/addons/ccusage/package.json +7 -0
- package/sandbox/addons/context7/package-lock.json +1313 -0
- package/sandbox/addons/context7/package.json +7 -0
- package/sandbox/addons/headroom/requirements.in +1 -0
- package/sandbox/addons/headroom/requirements.txt +2922 -0
- package/sandbox/addons/playwright-mcp/package-lock.json +56 -0
- package/sandbox/addons/playwright-mcp/package.json +7 -0
- package/schemas/config.schema.json +13 -4
- package/schemas/sandbox-config.schema.json +2 -2
- package/dist/cli/commands/maintenance/audit.d.ts +0 -3
- package/dist/cli/commands/maintenance/audit.js +0 -58
|
@@ -17,6 +17,8 @@ import { showData } from '../../app/features/data/index.js';
|
|
|
17
17
|
import { updatesStatus } from '../../app/features/updates/index.js';
|
|
18
18
|
import { containedStatus, showStorage } from '../../app/features/storage/index.js';
|
|
19
19
|
import { listSessions } from '../../app/features/sessions/index.js';
|
|
20
|
+
import { auditListing, listAudit } from '../../app/features/audit/index.js';
|
|
21
|
+
import { AuditLog } from '../../host/audit/index.js';
|
|
20
22
|
import { cancelled } from '../../app/kernel/index.js';
|
|
21
23
|
import { runDefinition } from '../../app/registry.js';
|
|
22
24
|
import { webSession } from '../../cli/services/index.js';
|
|
@@ -509,10 +511,101 @@ function sampleLogTail(input) {
|
|
|
509
511
|
next: 0,
|
|
510
512
|
});
|
|
511
513
|
}
|
|
514
|
+
/** A plausible audit log: two agents' sessions, the sandbox's own setup, a commit each, a guard finding. */
|
|
515
|
+
function sampleAuditLog(dir) {
|
|
516
|
+
const log = new AuditLog(dir, { days: 7, maxMb: 100 });
|
|
517
|
+
const at = (minutesAgo) => new Date(Date.now() - minutesAgo * MINUTE).toISOString();
|
|
518
|
+
const claude = { s: SESSIONS[0].id, ag: 'claude', pj: 'windows-sandbox', sv: 'p' };
|
|
519
|
+
const codex = { s: 'mva7codex2k91x', ag: 'codex', pj: 'mw2-agent', sv: 'p' };
|
|
520
|
+
const git = 'C:\\AgentData\\tools\\git\\cmd\\git.exe';
|
|
521
|
+
const node = 'C:\\nvm\\nodejs\\node.exe';
|
|
522
|
+
const ws = 'F:\\AgentProjects\\windows-sandbox';
|
|
523
|
+
const mw = 'F:\\AgentProjects\\mw2-agent';
|
|
524
|
+
const proc = (m, p, c, who, n) => ({
|
|
525
|
+
t: at(m),
|
|
526
|
+
k: 'proc',
|
|
527
|
+
p,
|
|
528
|
+
c,
|
|
529
|
+
pp: node,
|
|
530
|
+
...who,
|
|
531
|
+
...(n ? { n } : {}),
|
|
532
|
+
});
|
|
533
|
+
const file = (m, p, w, who) => ({
|
|
534
|
+
t: at(m),
|
|
535
|
+
k: 'file',
|
|
536
|
+
a: 'change',
|
|
537
|
+
p,
|
|
538
|
+
w,
|
|
539
|
+
...who,
|
|
540
|
+
});
|
|
541
|
+
log.append([
|
|
542
|
+
{ t: at(95), k: 'note', p: 'sandbox started; audit: programs, files, commits, sessions' },
|
|
543
|
+
proc(94, 'C:\\AgentData\\tools\\uv\\uv.exe', 'uv tool install aider-chat', {}),
|
|
544
|
+
{ t: at(90), k: 'session', a: 'start', p: 'claude', w: SESSIONS[0].cwd, c: claude.s, ...claude, sv: undefined },
|
|
545
|
+
proc(88, git, 'git status --short', claude),
|
|
546
|
+
proc(87, node, 'node dist/bin/agent-sandbox.js self-test audit', claude, 2),
|
|
547
|
+
file(80, 'src/host/audit/collect.ts', ws, claude),
|
|
548
|
+
file(79, 'src/app/features/audit/audit.def.ts', ws, claude),
|
|
549
|
+
proc(70, 'C:\\nvm\\nodejs\\npm.cmd', 'npm run build', claude),
|
|
550
|
+
{
|
|
551
|
+
t: at(66),
|
|
552
|
+
k: 'session',
|
|
553
|
+
a: 'start',
|
|
554
|
+
p: 'codex',
|
|
555
|
+
w: 'C:\\Projects\\mw2-agent',
|
|
556
|
+
c: codex.s,
|
|
557
|
+
...codex,
|
|
558
|
+
sv: undefined,
|
|
559
|
+
},
|
|
560
|
+
proc(64, 'C:\\Users\\WDAGUtilityAccount\\.cargo\\bin\\cargo.exe', 'cargo build --release', codex, 3),
|
|
561
|
+
file(60, 'src/main.rs', mw, { ...codex, sv: 't' }),
|
|
562
|
+
proc(52, git, 'git commit -m "Audit: link programs to their session"', claude),
|
|
563
|
+
{
|
|
564
|
+
t: at(52),
|
|
565
|
+
k: 'commit',
|
|
566
|
+
a: 'commit',
|
|
567
|
+
p: 'Audit: link programs to their session',
|
|
568
|
+
h: 'a3f9c2e81b7d4c60a1e2f3b4c5d6e7f8a9b0c1d2',
|
|
569
|
+
au: 'Josh <josh@example.com>',
|
|
570
|
+
w: ws,
|
|
571
|
+
...claude,
|
|
572
|
+
},
|
|
573
|
+
proc(40, git, 'git push --set-upstream origin https://***@github.com/example/mw2-agent.git', codex),
|
|
574
|
+
{ t: at(38), k: 'guard', p: 'hook: a pre-push hook added inside the sandbox was neutralised' },
|
|
575
|
+
proc(30, 'C:\\Users\\WDAGUtilityAccount\\.cargo\\bin\\cargo.exe', 'cargo test', codex, 4),
|
|
576
|
+
{
|
|
577
|
+
t: at(28),
|
|
578
|
+
k: 'commit',
|
|
579
|
+
a: 'commit',
|
|
580
|
+
p: 'Fix the map loader',
|
|
581
|
+
h: '7be0d41c9a2f4e3b8c1d0e9f8a7b6c5d4e3f2a1b',
|
|
582
|
+
au: 'Josh <josh@example.com>',
|
|
583
|
+
w: mw,
|
|
584
|
+
...codex,
|
|
585
|
+
},
|
|
586
|
+
{
|
|
587
|
+
t: at(20),
|
|
588
|
+
k: 'session',
|
|
589
|
+
a: 'end',
|
|
590
|
+
p: 'codex',
|
|
591
|
+
w: 'C:\\Projects\\mw2-agent',
|
|
592
|
+
c: codex.s + ' exit 0',
|
|
593
|
+
...codex,
|
|
594
|
+
sv: undefined,
|
|
595
|
+
},
|
|
596
|
+
proc(5, node, 'node dist/bin/agent-sandbox.js self-test app', claude),
|
|
597
|
+
]);
|
|
598
|
+
return log;
|
|
599
|
+
}
|
|
512
600
|
/** The real features where they only touch the temp folder, sample data for the rest. */
|
|
513
601
|
function previewRpc(ctx, state) {
|
|
602
|
+
const auditDir = path.join(ctx.storageRoot, 'audit', 'preview');
|
|
603
|
+
const audit = sampleAuditLog(auditDir);
|
|
514
604
|
const sample = async (id, input, io) => {
|
|
515
605
|
switch (id) {
|
|
606
|
+
case 'audit.list':
|
|
607
|
+
// The real listing over a sample log in the temp folder.
|
|
608
|
+
return auditListing(audit, { enabled: true, days: 7, maxMb: 100 }, listAudit.input.parse(input ?? {}), auditDir);
|
|
516
609
|
case 'sandbox.status':
|
|
517
610
|
return sampleStatus(state.running);
|
|
518
611
|
case 'vpn.status':
|
|
@@ -792,6 +885,7 @@ function previewRpc(ctx, state) {
|
|
|
792
885
|
'logs.tail': 'query',
|
|
793
886
|
'engine.status': 'query',
|
|
794
887
|
'engine.leftovers': 'query',
|
|
888
|
+
'audit.list': 'query',
|
|
795
889
|
};
|
|
796
890
|
return {
|
|
797
891
|
kind: (id) => kinds[id],
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
// Picking tool versions and release assets: Node/Go/Python/Java version rules, GitHub release pages, PowerShell 7,
|
|
2
|
-
// cargo-binstall, the small CLI tools (gh, zstd, openssl),
|
|
2
|
+
// cargo-binstall, the small CLI tools (gh, zstd, openssl), when PowerShell 7 is wanted, and the apps' agent add-ons.
|
|
3
|
+
import fs from 'node:fs';
|
|
3
4
|
import path from 'node:path';
|
|
4
5
|
import { javaMajorFrom, pickGoRelease, pickNodeVersion, pickPackageVersion, pickSpreadVersions, projectUses, specFromVersionFile, } from '../core/detect/index.js';
|
|
5
6
|
import { parseReleasePage, parseReleaseTags } from '../shared/download/index.js';
|
|
6
7
|
import { decidePwsh, pcHasPwsh, PWSH_MAX_BYTES, readPwshScripts, usesPwsh } from '../core/detect/index.js';
|
|
7
8
|
import { binstallAsset, CLI_TOOLS, decideCli, pwshAsset } from '../guest/toolchains/catalogue.js';
|
|
8
9
|
import { missingExtensions, vscodeExtensions, vscodeSettings } from '../guest/toolchains/tools/vscode-profile.js';
|
|
9
|
-
import { APP_CATEGORIES, APPS, appsNeedMesa, appsToInstall, exeMatcher } from '../shared/apps/index.js';
|
|
10
|
+
import { addonGuideLine, addonMcpServer, APP_CATEGORIES, APPS, appsNeedMesa, appsNeedUv, appsToInstall, CCUSAGE_VERSION, claudeManagedSettings, CONTEXT7_MCP_VERSION, exeMatcher, HEADROOM_VERSION, headroomArgs, headroomEnv, headroomUrl, PLAYWRIGHT_MCP_VERSION, RTK_VERSION, withAddonMcpServers, } from '../shared/apps/index.js';
|
|
10
11
|
import { ConfigSchema, LocalOverridesSchema } from '../core/schema/index.js';
|
|
11
12
|
export const toolchainsSuite = {
|
|
12
13
|
name: 'toolchains',
|
|
@@ -236,5 +237,149 @@ export const toolchainsSuite = {
|
|
|
236
237
|
!custom({ name: 'E', github: 'o/r', asset: 'x', url: 'https://x.org', exe: 'e.exe' }), 'apps: your own program comes from a GitHub release or an https link; plain http, a broken pattern or both are refused');
|
|
237
238
|
t.check(!LocalOverridesSchema.safeParse({ apps: { custom: {} } }).success &&
|
|
238
239
|
LocalOverridesSchema.safeParse({ apps: { install: ['jq'] } }).success, 'apps: a project file may add catalogue apps but never programs of its own');
|
|
240
|
+
t.section('agent add-ons');
|
|
241
|
+
const rtk = APPS.find((a) => a.id === 'rtk');
|
|
242
|
+
const rtkSource = rtk?.source;
|
|
243
|
+
t.check(rtk?.category === 'agents' &&
|
|
244
|
+
rtk.cli === true &&
|
|
245
|
+
rtk.userOnly === true &&
|
|
246
|
+
rtkSource !== undefined &&
|
|
247
|
+
'url' in rtkSource &&
|
|
248
|
+
rtkSource.url.startsWith('https://github.com/rtk-ai/rtk/releases/download/v' + RTK_VERSION + '/') &&
|
|
249
|
+
rtkSource.url.endsWith('-x86_64-pc-windows-msvc.zip') &&
|
|
250
|
+
/^[0-9a-f]{64}$/.test(rtkSource.sha256 ?? ''), 'rtk: a pinned https release file of rtk-ai/rtk with its SHA-256, on PATH, in your own settings only');
|
|
251
|
+
t.check(APPS.every((a) => !('url' in a.source) || a.source.url.startsWith('https://')) &&
|
|
252
|
+
APPS.every((a) => !('url' in a.source) || a.source.sha256 === undefined || /^[0-9a-f]{64}$/.test(a.source.sha256)), 'apps: every direct link is https, and a pinned checksum is 64 hex characters');
|
|
253
|
+
const agentApps = APPS.filter((a) => a.category === 'agents').map((a) => a.id);
|
|
254
|
+
t.check(APP_CATEGORIES.some((c) => c.id === 'agents' && c.label === 'Agent add-ons') &&
|
|
255
|
+
agentApps.includes('rtk') &&
|
|
256
|
+
agentApps.includes('claude-mem') &&
|
|
257
|
+
APPS.find((a) => a.id === 'claude-mem')?.setting === 'claudeProfile.syncClaudeMem', 'apps: the Agent add-ons category holds rtk and claude-mem (claude-mem through its own setting)');
|
|
258
|
+
t.check(!LocalOverridesSchema.safeParse({ apps: { install: ['rtk'] } }).success &&
|
|
259
|
+
!LocalOverridesSchema.safeParse({ apps: { install: ['claude-mem'] } }).success &&
|
|
260
|
+
ConfigSchema.shape.apps.shape.install.safeParse(['rtk']).success, 'rtk: only your own settings turn it on; a project file cannot');
|
|
261
|
+
const exe = 'C:\\AgentData\\tools\\apps\\rtk\\rtk.exe';
|
|
262
|
+
const withRtk = claudeManagedSettings([], exe);
|
|
263
|
+
const both = claudeManagedSettings(['Bash(git push:*)'], exe);
|
|
264
|
+
const gitOnly = claudeManagedSettings(['Bash(git push:*)'], null);
|
|
265
|
+
t.equal(withRtk?.hooks?.PreToolUse, [{ matcher: 'Bash', hooks: [{ type: 'command', command: '"C:/AgentData/tools/apps/rtk/rtk.exe" hook claude' }] }], "rtk: its documented Bash PreToolUse hook goes into the sandbox's managed settings");
|
|
266
|
+
t.check(claudeManagedSettings([], null) === null &&
|
|
267
|
+
gitOnly?.hooks === undefined &&
|
|
268
|
+
gitOnly?.permissions?.deny.length === 1 &&
|
|
269
|
+
both?.permissions?.deny.length === 1 &&
|
|
270
|
+
both.hooks?.PreToolUse.length === 1 &&
|
|
271
|
+
withRtk?.permissions === undefined, 'rtk: no hook when it is off; the git deny rules stay beside it; nothing to write when neither');
|
|
272
|
+
t.section('agent add-ons: Headroom, ccusage, Playwright MCP, Context7 MCP');
|
|
273
|
+
const pinned = {
|
|
274
|
+
headroom: ['uv', 'headroom-ai', HEADROOM_VERSION],
|
|
275
|
+
ccusage: ['npm', 'ccusage', CCUSAGE_VERSION],
|
|
276
|
+
'playwright-mcp': ['npm', '@playwright/mcp', PLAYWRIGHT_MCP_VERSION],
|
|
277
|
+
context7: ['npm', '@upstash/context7-mcp', CONTEXT7_MCP_VERSION],
|
|
278
|
+
};
|
|
279
|
+
for (const [id, [kind, pkg, version]] of Object.entries(pinned)) {
|
|
280
|
+
const entry = APPS.find((a) => a.id === id);
|
|
281
|
+
const source = entry?.source;
|
|
282
|
+
t.check(entry?.category === 'agents' &&
|
|
283
|
+
entry.userOnly === true &&
|
|
284
|
+
source !== undefined &&
|
|
285
|
+
'locked' in source &&
|
|
286
|
+
source.locked === kind &&
|
|
287
|
+
source.package === pkg &&
|
|
288
|
+
source.version === version &&
|
|
289
|
+
/^\d+\.\d+\.\d+$/.test(version) &&
|
|
290
|
+
!LocalOverridesSchema.safeParse({ apps: { install: [id] } }).success &&
|
|
291
|
+
ConfigSchema.shape.apps.shape.install.safeParse([id]).success, id + ': in Agent add-ons, a pinned ' + kind + ' package (' + pkg + ' ' + version + '), your own settings only');
|
|
292
|
+
const lockDir = path.join(t.packageRoot, 'sandbox', 'addons', id);
|
|
293
|
+
if (kind === 'npm') {
|
|
294
|
+
const manifest = JSON.parse(fs.readFileSync(path.join(lockDir, 'package.json'), 'utf8'));
|
|
295
|
+
const lock = JSON.parse(fs.readFileSync(path.join(lockDir, 'package-lock.json'), 'utf8'));
|
|
296
|
+
const deps = Object.entries(lock.packages).filter(([key]) => key !== '');
|
|
297
|
+
t.check(manifest.dependencies[pkg] === version &&
|
|
298
|
+
Object.keys(manifest.dependencies).length === 1 &&
|
|
299
|
+
lock.lockfileVersion === 3 &&
|
|
300
|
+
lock.packages['node_modules/' + pkg]?.version === version &&
|
|
301
|
+
deps.length > 0 &&
|
|
302
|
+
deps.every(([, p]) => !p.link &&
|
|
303
|
+
(p.resolved ?? '').startsWith('https://registry.npmjs.org/') &&
|
|
304
|
+
/^sha512-[A-Za-z0-9+/]+=*$/.test(p.integrity ?? '')), id + ': its lock file pins ' + pkg + ' ' + version + '; every package comes from npm with a sha512 integrity');
|
|
305
|
+
}
|
|
306
|
+
else {
|
|
307
|
+
const lines = fs.readFileSync(path.join(lockDir, 'requirements.txt'), 'utf8').split(/\r?\n/);
|
|
308
|
+
const requirements = lines.filter((l) => /^[A-Za-z0-9]/.test(l));
|
|
309
|
+
const blocks = lines
|
|
310
|
+
.join('\n')
|
|
311
|
+
.split(/\n(?=[A-Za-z0-9])/)
|
|
312
|
+
.slice(1);
|
|
313
|
+
t.check(requirements.includes(pkg + '==' + version + ' \\') &&
|
|
314
|
+
requirements.length > 10 &&
|
|
315
|
+
requirements.every((l) => /^[A-Za-z0-9._-]+(\[[^\]]+\])?==[^\s]+ \\$/.test(l)) &&
|
|
316
|
+
blocks.every((b) => /--hash=sha256:[0-9a-f]{64}/.test(b)) &&
|
|
317
|
+
!lines.some((l) => /^\s*(-e|--index-url|--extra-index-url|-f|--find-links|git\+|https?:)/.test(l.trim())), id + ': its requirements.txt pins every package to one version with SHA-256 hashes, from PyPI only');
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
t.check(appsNeedUv({ install: ['headroom'], custom: {} }) &&
|
|
321
|
+
!appsNeedUv({ install: ['ccusage', 'rtk'], custom: {} }) &&
|
|
322
|
+
!appsNeedUv(undefined), 'headroom: choosing it brings the uv step (it installs with uv)');
|
|
323
|
+
const services = APPS.filter((a) => a.service).map((a) => a.id);
|
|
324
|
+
t.check(['headroom', 'playwright-mcp', 'context7'].every((id) => services.includes(id)) &&
|
|
325
|
+
APPS.find((a) => a.id === 'ccusage')?.cli === true &&
|
|
326
|
+
!services.includes('ccusage'), 'add-ons: the proxy and MCP servers get no shortcut and no PATH entry; ccusage is a command on PATH');
|
|
327
|
+
// Headroom: loopback only, Anthropic only, no telemetry, and none of the agents' secrets in its environment.
|
|
328
|
+
const args = headroomArgs();
|
|
329
|
+
t.check(args[0] === 'proxy' &&
|
|
330
|
+
args[args.indexOf('--host') + 1] === '127.0.0.1' &&
|
|
331
|
+
args[args.indexOf('--port') + 1] === '8787' &&
|
|
332
|
+
args[args.indexOf('--anthropic-api-url') + 1] === 'https://api.anthropic.com' &&
|
|
333
|
+
args.includes('--no-telemetry') &&
|
|
334
|
+
headroomUrl() === 'http://127.0.0.1:8787', 'headroom: the proxy listens on 127.0.0.1 only and forwards only to api.anthropic.com, telemetry off');
|
|
335
|
+
const proxyEnv = headroomEnv({
|
|
336
|
+
PATH: 'C:\\Windows',
|
|
337
|
+
ANTHROPIC_API_KEY: 'sk-ant-x',
|
|
338
|
+
CLAUDE_CODE_OAUTH_TOKEN: 'oauth',
|
|
339
|
+
GITHUB_TOKEN: 'ghp',
|
|
340
|
+
OPENAI_API_KEY: 'sk',
|
|
341
|
+
DB_PASSWORD: 'pw',
|
|
342
|
+
HTTPS_PROXY: 'http://127.0.0.1:3128',
|
|
343
|
+
HEADROOM_OFFLINE: '0',
|
|
344
|
+
});
|
|
345
|
+
t.check(proxyEnv.PATH === 'C:\\Windows' &&
|
|
346
|
+
proxyEnv.HTTPS_PROXY === 'http://127.0.0.1:3128' &&
|
|
347
|
+
!Object.values(proxyEnv).some((v) => ['sk-ant-x', 'oauth', 'ghp', 'sk', 'pw'].includes(v)) &&
|
|
348
|
+
proxyEnv.HEADROOM_OFFLINE === '1' &&
|
|
349
|
+
proxyEnv.HEADROOM_TELEMETRY === 'off' &&
|
|
350
|
+
proxyEnv.HEADROOM_UPDATE_CHECK === 'off', 'headroom: starts without keys or tokens, offline apart from forwarding (no telemetry, updates or downloads)');
|
|
351
|
+
const routed = claudeManagedSettings([], null, headroomUrl());
|
|
352
|
+
t.check(routed?.env?.ANTHROPIC_BASE_URL === 'http://127.0.0.1:8787' &&
|
|
353
|
+
claudeManagedSettings([], exe)?.env === undefined &&
|
|
354
|
+
claudeManagedSettings(['Bash(git push:*)'], null)?.env === undefined &&
|
|
355
|
+
claudeManagedSettings(['Bash(git push:*)'], exe, headroomUrl())?.hooks?.PreToolUse.length === 1, "headroom: Claude Code is pointed at the proxy only while it runs (the sandbox's managed settings env)");
|
|
356
|
+
// MCP servers: set while on, removed when off, your own servers never touched.
|
|
357
|
+
const playwright = APPS.find((a) => a.id === 'playwright-mcp')?.mcp;
|
|
358
|
+
const context7 = APPS.find((a) => a.id === 'context7')?.mcp;
|
|
359
|
+
const known = APPS.flatMap((a) => (a.mcp ? [a.mcp.name] : []));
|
|
360
|
+
const pw = playwright ? addonMcpServer('C:\\AgentData\\tools\\apps\\playwright-mcp', playwright) : null;
|
|
361
|
+
t.equal(pw, {
|
|
362
|
+
type: 'stdio',
|
|
363
|
+
command: 'node',
|
|
364
|
+
args: [
|
|
365
|
+
'C:\\AgentData\\tools\\apps\\playwright-mcp\\node_modules\\@playwright\\mcp\\cli.js',
|
|
366
|
+
'--browser',
|
|
367
|
+
'msedge',
|
|
368
|
+
],
|
|
369
|
+
env: {},
|
|
370
|
+
}, 'playwright-mcp: a stdio MCP server run by node from its own folder, with Edge');
|
|
371
|
+
const mine = { playwright: { type: 'stdio', command: 'npx', args: ['@playwright/mcp'] } };
|
|
372
|
+
const on = withAddonMcpServers(mine, pw ? { 'sbx-playwright': pw } : {}, known);
|
|
373
|
+
const off = withAddonMcpServers({ ...on, 'sbx-context7': {} }, {}, known);
|
|
374
|
+
t.check(known.length === 2 &&
|
|
375
|
+
known.every((n) => n.startsWith('sbx-')) &&
|
|
376
|
+
context7?.name === 'sbx-context7' &&
|
|
377
|
+
'sbx-playwright' in on &&
|
|
378
|
+
'playwright' in on &&
|
|
379
|
+
Object.keys(off).join() === 'playwright' &&
|
|
380
|
+
withAddonMcpServers({ 'sbx-other': 1 }, {}, known)['sbx-other'] === 1, 'add-ons: their sbx-... MCP servers are set while on and removed when off; servers of your own stay');
|
|
381
|
+
t.check(addonGuideLine([], false).length === 0 &&
|
|
382
|
+
addonGuideLine(['sbx-playwright'], true).length === 2 &&
|
|
383
|
+
addonGuideLine(['sbx-playwright'], false)[0].includes('sbx-playwright'), 'add-ons: the agent guide names the MCP servers and ccusage only when they are on');
|
|
239
384
|
},
|
|
240
385
|
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { AddonMcp } from './catalogue.js';
|
|
2
|
+
/** One hook command in Claude Code's settings. */
|
|
3
|
+
interface ClaudeHook {
|
|
4
|
+
readonly type: 'command';
|
|
5
|
+
readonly command: string;
|
|
6
|
+
}
|
|
7
|
+
/** Claude Code's managed-settings.json as the sandbox writes it. */
|
|
8
|
+
export interface ManagedSettings {
|
|
9
|
+
readonly permissions?: {
|
|
10
|
+
readonly deny: readonly string[];
|
|
11
|
+
};
|
|
12
|
+
readonly hooks?: {
|
|
13
|
+
readonly PreToolUse: readonly {
|
|
14
|
+
readonly matcher: string;
|
|
15
|
+
readonly hooks: readonly ClaudeHook[];
|
|
16
|
+
}[];
|
|
17
|
+
};
|
|
18
|
+
readonly env?: Readonly<Record<string, string>>;
|
|
19
|
+
}
|
|
20
|
+
/** The command Claude Code runs for rtk's hook: the program by its full path (quoted, forward slashes for Git Bash). */
|
|
21
|
+
export declare function rtkHookCommand(exe: string): string;
|
|
22
|
+
/**
|
|
23
|
+
* The managed settings for the git deny rules, rtk's Bash hook when rtk is installed (`rtkExe`) and the Headroom proxy
|
|
24
|
+
* when it runs (`headroomUrl`). Null when there is nothing to write (the file is removed then).
|
|
25
|
+
*/
|
|
26
|
+
export declare function claudeManagedSettings(deny: readonly string[], rtkExe: string | null, headroomUrl?: string | null): ManagedSettings | null;
|
|
27
|
+
/** The line every agent's sandbox guide gets while rtk is installed (Claude's commands go through it on their own). */
|
|
28
|
+
export declare const RTK_GUIDE_LINE = "- rtk is on PATH and compacts command output: Claude Code's Bash commands go through it on their own; in other agents prefix shell commands with `rtk` (`rtk git status`, `rtk cargo test`, `rtk ls`).";
|
|
29
|
+
/** Headroom listens here, on loopback only (its default port). */
|
|
30
|
+
export declare const HEADROOM_PORT = 8787;
|
|
31
|
+
/** The only upstream Headroom forwards to: Anthropic's API, set on its command line. */
|
|
32
|
+
export declare const HEADROOM_UPSTREAM = "https://api.anthropic.com";
|
|
33
|
+
/** Claude Code's ANTHROPIC_BASE_URL while the proxy runs. */
|
|
34
|
+
export declare function headroomUrl(port?: number): string;
|
|
35
|
+
/** `headroom proxy` on loopback, forwarding only to Anthropic's API, with no telemetry. */
|
|
36
|
+
export declare function headroomArgs(port?: number): string[];
|
|
37
|
+
/**
|
|
38
|
+
* Headroom's own switches: HEADROOM_OFFLINE refuses every connection Headroom would make on its own (telemetry, update
|
|
39
|
+
* checks, model downloads, usage polling) and keeps only the forwarding of the agent's requests.
|
|
40
|
+
*/
|
|
41
|
+
export declare const HEADROOM_ENV: Readonly<Record<string, string>>;
|
|
42
|
+
/** The environment Headroom starts with: the sandbox's, minus anything secret, plus its own switches. */
|
|
43
|
+
export declare function headroomEnv(base: Readonly<Record<string, string | undefined>>): Record<string, string>;
|
|
44
|
+
/** The guide line while the proxy runs. */
|
|
45
|
+
export declare function headroomGuideLine(logFile: string): string;
|
|
46
|
+
/** A user-scope MCP server in Claude Code's .claude.json. */
|
|
47
|
+
export interface McpServerConfig {
|
|
48
|
+
readonly type: 'stdio';
|
|
49
|
+
readonly command: string;
|
|
50
|
+
readonly args: readonly string[];
|
|
51
|
+
readonly env: Readonly<Record<string, string>>;
|
|
52
|
+
}
|
|
53
|
+
/** The name prefix of the sandbox's own MCP servers: names with it are the sandbox's to add and remove. */
|
|
54
|
+
export declare const ADDON_MCP_PREFIX = "sbx-";
|
|
55
|
+
/** The MCP server of an installed add-on: `node <folder>\<script> <args>` (Node is on PATH in every sandbox). */
|
|
56
|
+
export declare function addonMcpServer(folder: string, mcp: AddonMcp): McpServerConfig;
|
|
57
|
+
/**
|
|
58
|
+
* The user-scope MCP servers with the add-ons' servers set: every sbx-... server that `known` lists and that is not in
|
|
59
|
+
* `servers` is removed (an add-on turned off); servers of your own are never touched.
|
|
60
|
+
*/
|
|
61
|
+
export declare function withAddonMcpServers(current: Readonly<Record<string, unknown>>, servers: Readonly<Record<string, McpServerConfig>>, known: readonly string[]): Record<string, unknown>;
|
|
62
|
+
/** The guide line for the add-ons' MCP servers and tools (none: no line). */
|
|
63
|
+
export declare function addonGuideLine(mcpNames: readonly string[], ccusage: boolean): string[];
|
|
64
|
+
export {};
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/** The command Claude Code runs for rtk's hook: the program by its full path (quoted, forward slashes for Git Bash). */
|
|
2
|
+
export function rtkHookCommand(exe) {
|
|
3
|
+
return '"' + exe.replace(/\\/g, '/') + '" hook claude';
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* The managed settings for the git deny rules, rtk's Bash hook when rtk is installed (`rtkExe`) and the Headroom proxy
|
|
7
|
+
* when it runs (`headroomUrl`). Null when there is nothing to write (the file is removed then).
|
|
8
|
+
*/
|
|
9
|
+
export function claudeManagedSettings(deny, rtkExe, headroomUrl = null) {
|
|
10
|
+
if (!deny.length && !rtkExe && !headroomUrl)
|
|
11
|
+
return null;
|
|
12
|
+
return {
|
|
13
|
+
...(deny.length ? { permissions: { deny } } : {}),
|
|
14
|
+
...(rtkExe
|
|
15
|
+
? { hooks: { PreToolUse: [{ matcher: 'Bash', hooks: [{ type: 'command', command: rtkHookCommand(rtkExe) }] }] } }
|
|
16
|
+
: {}),
|
|
17
|
+
...(headroomUrl ? { env: { ANTHROPIC_BASE_URL: headroomUrl } } : {}),
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
/** The line every agent's sandbox guide gets while rtk is installed (Claude's commands go through it on their own). */
|
|
21
|
+
export const RTK_GUIDE_LINE = "- rtk is on PATH and compacts command output: Claude Code's Bash commands go through it on their own; in other agents prefix shell commands with `rtk` (`rtk git status`, `rtk cargo test`, `rtk ls`).";
|
|
22
|
+
/** Headroom listens here, on loopback only (its default port). */
|
|
23
|
+
export const HEADROOM_PORT = 8787;
|
|
24
|
+
/** The only upstream Headroom forwards to: Anthropic's API, set on its command line. */
|
|
25
|
+
export const HEADROOM_UPSTREAM = 'https://api.anthropic.com';
|
|
26
|
+
/** Claude Code's ANTHROPIC_BASE_URL while the proxy runs. */
|
|
27
|
+
export function headroomUrl(port = HEADROOM_PORT) {
|
|
28
|
+
return 'http://127.0.0.1:' + port;
|
|
29
|
+
}
|
|
30
|
+
/** `headroom proxy` on loopback, forwarding only to Anthropic's API, with no telemetry. */
|
|
31
|
+
export function headroomArgs(port = HEADROOM_PORT) {
|
|
32
|
+
return [
|
|
33
|
+
'proxy',
|
|
34
|
+
'--host',
|
|
35
|
+
'127.0.0.1',
|
|
36
|
+
'--port',
|
|
37
|
+
String(port),
|
|
38
|
+
'--anthropic-api-url',
|
|
39
|
+
HEADROOM_UPSTREAM,
|
|
40
|
+
'--no-telemetry',
|
|
41
|
+
];
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Headroom's own switches: HEADROOM_OFFLINE refuses every connection Headroom would make on its own (telemetry, update
|
|
45
|
+
* checks, model downloads, usage polling) and keeps only the forwarding of the agent's requests.
|
|
46
|
+
*/
|
|
47
|
+
export const HEADROOM_ENV = {
|
|
48
|
+
HEADROOM_OFFLINE: '1',
|
|
49
|
+
HEADROOM_TELEMETRY: 'off',
|
|
50
|
+
HEADROOM_BEACON: 'off',
|
|
51
|
+
HEADROOM_UPDATE_CHECK: 'off',
|
|
52
|
+
DO_NOT_TRACK: '1',
|
|
53
|
+
HF_HUB_OFFLINE: '1',
|
|
54
|
+
PYTHONIOENCODING: 'utf-8',
|
|
55
|
+
};
|
|
56
|
+
/** Variables the proxy never gets: keys, tokens and passwords of the agents (it only forwards what Claude sends). */
|
|
57
|
+
const SECRET_NAME = /(KEY|TOKEN|SECRET|PASSWORD|PASSWD|CREDENTIAL|AUTH)/i;
|
|
58
|
+
/** The environment Headroom starts with: the sandbox's, minus anything secret, plus its own switches. */
|
|
59
|
+
export function headroomEnv(base) {
|
|
60
|
+
const env = {};
|
|
61
|
+
for (const [name, value] of Object.entries(base))
|
|
62
|
+
if (value !== undefined && !SECRET_NAME.test(name) && !/^(ANTHROPIC|OPENAI|CLAUDE)_/i.test(name))
|
|
63
|
+
env[name] = value;
|
|
64
|
+
return { ...env, ...HEADROOM_ENV };
|
|
65
|
+
}
|
|
66
|
+
/** The guide line while the proxy runs. */
|
|
67
|
+
export function headroomGuideLine(logFile) {
|
|
68
|
+
return ("- Headroom compresses Claude Code's requests: Claude Code reaches the API through its proxy on " +
|
|
69
|
+
headroomUrl() +
|
|
70
|
+
' (log: `' +
|
|
71
|
+
logFile +
|
|
72
|
+
'`). If Claude Code cannot reach the API, the proxy stopped: it starts again with the next sandbox, or turn Headroom off in Apps.');
|
|
73
|
+
}
|
|
74
|
+
/** The name prefix of the sandbox's own MCP servers: names with it are the sandbox's to add and remove. */
|
|
75
|
+
export const ADDON_MCP_PREFIX = 'sbx-';
|
|
76
|
+
/** The MCP server of an installed add-on: `node <folder>\<script> <args>` (Node is on PATH in every sandbox). */
|
|
77
|
+
export function addonMcpServer(folder, mcp) {
|
|
78
|
+
const script = folder.replace(/[\\/]+$/, '') + '\\' + mcp.script.replace(/\//g, '\\');
|
|
79
|
+
return { type: 'stdio', command: 'node', args: [script, ...(mcp.args ?? [])], env: {} };
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* The user-scope MCP servers with the add-ons' servers set: every sbx-... server that `known` lists and that is not in
|
|
83
|
+
* `servers` is removed (an add-on turned off); servers of your own are never touched.
|
|
84
|
+
*/
|
|
85
|
+
export function withAddonMcpServers(current, servers, known) {
|
|
86
|
+
const kept = Object.fromEntries(Object.entries(current).filter(([name]) => !known.includes(name) || name in servers));
|
|
87
|
+
return { ...kept, ...servers };
|
|
88
|
+
}
|
|
89
|
+
/** The guide line for the add-ons' MCP servers and tools (none: no line). */
|
|
90
|
+
export function addonGuideLine(mcpNames, ccusage) {
|
|
91
|
+
return [
|
|
92
|
+
...(mcpNames.length ? ['- MCP servers in Claude Code from Apps: ' + mcpNames.join(', ') + '.'] : []),
|
|
93
|
+
...(ccusage ? ['- ccusage is on PATH: `ccusage daily` (or `monthly`, `session`) reports token use and cost.'] : []),
|
|
94
|
+
];
|
|
95
|
+
}
|
|
@@ -1,20 +1,36 @@
|
|
|
1
|
-
export type AppCategory = 'editors' | 'databases' | 'api' | 'git' | 'utilities' | 'browsers' | 'gamedev' | 'cloud' | 'diagrams' | 'network' | 'reversing' | 'cli';
|
|
1
|
+
export type AppCategory = 'agents' | 'editors' | 'databases' | 'api' | 'git' | 'utilities' | 'browsers' | 'gamedev' | 'cloud' | 'diagrams' | 'network' | 'reversing' | 'cli';
|
|
2
2
|
export declare const APP_CATEGORIES: {
|
|
3
3
|
id: AppCategory;
|
|
4
4
|
label: string;
|
|
5
5
|
description: string;
|
|
6
6
|
}[];
|
|
7
|
-
/**
|
|
7
|
+
/**
|
|
8
|
+
* Where a download comes from. Resolvers (sublime, kubectl ...) are code in guest/apps, named here by id. `locked`: a
|
|
9
|
+
* pinned package installed from the lock file in sandbox\addons\<id> (npm: package.json + package-lock.json, every
|
|
10
|
+
* tarball checked against its integrity; uv: requirements.txt, every wheel checked against its SHA-256).
|
|
11
|
+
*/
|
|
8
12
|
export type AppSource = {
|
|
9
13
|
github: string;
|
|
10
14
|
asset: string;
|
|
11
15
|
} | {
|
|
12
16
|
url: string;
|
|
17
|
+
sha256?: string;
|
|
13
18
|
} | {
|
|
14
19
|
resolve: string;
|
|
15
20
|
} | {
|
|
16
21
|
npm: string;
|
|
22
|
+
} | {
|
|
23
|
+
locked: 'npm' | 'uv';
|
|
24
|
+
package: string;
|
|
25
|
+
version: string;
|
|
17
26
|
};
|
|
27
|
+
/** An MCP server an add-on adds to the sandbox's Claude Code: `node <its folder>\<script> <args>`. */
|
|
28
|
+
export interface AddonMcp {
|
|
29
|
+
/** The server's name in Claude Code (sbx-...: the sandbox removes its own names again when the add-on is off). */
|
|
30
|
+
readonly name: string;
|
|
31
|
+
readonly script: string;
|
|
32
|
+
readonly args?: readonly string[];
|
|
33
|
+
}
|
|
18
34
|
export interface AppEntry {
|
|
19
35
|
id: string;
|
|
20
36
|
name: string;
|
|
@@ -39,6 +55,21 @@ export interface AppEntry {
|
|
|
39
55
|
setting?: string;
|
|
40
56
|
/** Only with full git access (it pushes on its own, past the sandbox's git guard). */
|
|
41
57
|
fullGitOnly?: boolean;
|
|
58
|
+
/** Which agents it plugs into, and how (agent add-ons). */
|
|
59
|
+
plugsInto?: string;
|
|
60
|
+
/** Only in your own settings: a project's .sandbox-config.json cannot turn it on (it changes what the agents run). */
|
|
61
|
+
userOnly?: boolean;
|
|
62
|
+
/** Started by the sandbox or the agent (a proxy, an MCP server): not on PATH and no shortcut. */
|
|
63
|
+
service?: boolean;
|
|
64
|
+
/** The MCP server it adds to the sandbox's Claude Code while it is on. */
|
|
65
|
+
mcp?: AddonMcp;
|
|
42
66
|
}
|
|
67
|
+
/** rtk (rtk-ai/rtk), pinned: the Apache-2.0 edition's Windows build. The checksum is the release's checksums.txt. */
|
|
68
|
+
export declare const RTK_VERSION = "1.0.1";
|
|
69
|
+
/** Pinned agent add-ons, installed from the lock files in sandbox\addons\<id> (renewing them: docs/toolchains.md). */
|
|
70
|
+
export declare const HEADROOM_VERSION = "0.41.0";
|
|
71
|
+
export declare const CCUSAGE_VERSION = "20.0.36";
|
|
72
|
+
export declare const PLAYWRIGHT_MCP_VERSION = "0.0.83";
|
|
73
|
+
export declare const CONTEXT7_MCP_VERSION = "4.4.2";
|
|
43
74
|
export declare const APPS: AppEntry[];
|
|
44
75
|
export declare function appById(id: string): AppEntry | undefined;
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
// The apps catalogue: desktop programs and command-line tools the sandbox can install on request (apps.install), each a
|
|
2
2
|
// portable download kept in C:\AgentData\tools\apps\<id> (installers would be lost when the sandbox closes). Pure data,
|
|
3
3
|
// shared by the menu on the PC (to list them) and the sandbox (to install them, guest/apps). VS Code and Windows
|
|
4
|
-
// Terminal have settings of their own (toolchains.vscode, provisioning.terminal) and are listed here too
|
|
4
|
+
// Terminal have settings of their own (toolchains.vscode, provisioning.terminal) and are listed here too, like
|
|
5
|
+
// claude-mem (claudeProfile.syncClaudeMem) among the agent add-ons.
|
|
5
6
|
export const APP_CATEGORIES = [
|
|
7
|
+
{
|
|
8
|
+
id: 'agents',
|
|
9
|
+
label: 'Agent add-ons',
|
|
10
|
+
description: 'Things that plug into the agents: token savers, memory, MCP servers.',
|
|
11
|
+
},
|
|
6
12
|
{
|
|
7
13
|
id: 'editors',
|
|
8
14
|
label: 'Editors & IDEs',
|
|
@@ -49,7 +55,102 @@ export const APP_CATEGORIES = [
|
|
|
49
55
|
},
|
|
50
56
|
];
|
|
51
57
|
const gh = (github, asset) => ({ github, asset });
|
|
58
|
+
/** rtk (rtk-ai/rtk), pinned: the Apache-2.0 edition's Windows build. The checksum is the release's checksums.txt. */
|
|
59
|
+
export const RTK_VERSION = '1.0.1';
|
|
60
|
+
const RTK_URL = 'https://github.com/rtk-ai/rtk/releases/download/v' + RTK_VERSION + '/rtk-oss-x86_64-pc-windows-msvc.zip';
|
|
61
|
+
const RTK_SHA256 = '0e53e2eca471e15fbd3636e64e6bc0b182cbc278ed0339c80450a2dd03f4cd6b';
|
|
62
|
+
/** Pinned agent add-ons, installed from the lock files in sandbox\addons\<id> (renewing them: docs/toolchains.md). */
|
|
63
|
+
export const HEADROOM_VERSION = '0.41.0';
|
|
64
|
+
export const CCUSAGE_VERSION = '20.0.36';
|
|
65
|
+
export const PLAYWRIGHT_MCP_VERSION = '0.0.83';
|
|
66
|
+
export const CONTEXT7_MCP_VERSION = '4.4.2';
|
|
52
67
|
export const APPS = [
|
|
68
|
+
// ---- agent add-ons
|
|
69
|
+
{
|
|
70
|
+
id: 'rtk',
|
|
71
|
+
name: 'rtk (Rust Token Killer)',
|
|
72
|
+
category: 'agents',
|
|
73
|
+
about: "Compacts the output of shell commands (git, tests, builds, ls ...) before the agent reads it, so it uses fewer tokens. Claude Code's Bash commands go through it on their own.",
|
|
74
|
+
size: '6 MB',
|
|
75
|
+
source: { url: RTK_URL, sha256: RTK_SHA256 },
|
|
76
|
+
exe: 'rtk.exe',
|
|
77
|
+
cli: true,
|
|
78
|
+
plugsInto: "Claude Code through a hook in the sandbox's managed settings; other agents are told to use it",
|
|
79
|
+
userOnly: true,
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
id: 'headroom',
|
|
83
|
+
name: 'Headroom',
|
|
84
|
+
category: 'agents',
|
|
85
|
+
about: "Compresses what Claude Code sends to the model (tool output, logs, files) so it uses fewer tokens: a local proxy on 127.0.0.1:8787 that sits in Claude Code's API path, offline apart from forwarding to api.anthropic.com.",
|
|
86
|
+
size: '150 MB',
|
|
87
|
+
source: { locked: 'uv', package: 'headroom-ai', version: HEADROOM_VERSION },
|
|
88
|
+
exe: 'headroom.exe',
|
|
89
|
+
service: true,
|
|
90
|
+
plugsInto: 'Claude Code: the sandbox starts the proxy and points Claude Code at it (managed settings)',
|
|
91
|
+
userOnly: true,
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
id: 'ccusage',
|
|
95
|
+
name: 'ccusage',
|
|
96
|
+
category: 'agents',
|
|
97
|
+
about: "Reports the agents' token use and cost by day, month or session from their logs in the sandbox (`ccusage daily`; add --offline to skip fetching prices).",
|
|
98
|
+
size: '5 MB',
|
|
99
|
+
source: { locked: 'npm', package: 'ccusage', version: CCUSAGE_VERSION },
|
|
100
|
+
exe: 'ccusage.cmd',
|
|
101
|
+
cli: true,
|
|
102
|
+
plugsInto: "Reads Claude Code's session logs in the sandbox; changes nothing",
|
|
103
|
+
userOnly: true,
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
id: 'playwright-mcp',
|
|
107
|
+
name: 'Playwright MCP',
|
|
108
|
+
category: 'agents',
|
|
109
|
+
about: 'Lets Claude Code drive a browser (Microsoft Edge, already in the sandbox) to open pages, click, fill forms and take screenshots: the MCP server of microsoft/playwright-mcp.',
|
|
110
|
+
size: '20 MB',
|
|
111
|
+
source: { locked: 'npm', package: '@playwright/mcp', version: PLAYWRIGHT_MCP_VERSION },
|
|
112
|
+
exe: 'playwright-mcp.cmd',
|
|
113
|
+
service: true,
|
|
114
|
+
mcp: { name: 'sbx-playwright', script: 'node_modules/@playwright/mcp/cli.js', args: ['--browser', 'msedge'] },
|
|
115
|
+
plugsInto: "Claude Code as the MCP server sbx-playwright (the sandbox's Claude settings)",
|
|
116
|
+
userOnly: true,
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
id: 'context7',
|
|
120
|
+
name: 'Context7 MCP',
|
|
121
|
+
category: 'agents',
|
|
122
|
+
about: "Up-to-date documentation and code examples for libraries, looked up by Claude Code while it works (upstash/context7). The library names and questions go to Upstash's context7.com.",
|
|
123
|
+
size: '55 MB',
|
|
124
|
+
source: { locked: 'npm', package: '@upstash/context7-mcp', version: CONTEXT7_MCP_VERSION },
|
|
125
|
+
exe: 'context7-mcp.cmd',
|
|
126
|
+
service: true,
|
|
127
|
+
mcp: { name: 'sbx-context7', script: 'node_modules/@upstash/context7-mcp/dist/index.js' },
|
|
128
|
+
plugsInto: "Claude Code as the MCP server sbx-context7 (the sandbox's Claude settings)",
|
|
129
|
+
userOnly: true,
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
id: 'claude-mem',
|
|
133
|
+
name: 'claude-mem',
|
|
134
|
+
category: 'agents',
|
|
135
|
+
about: 'Persistent memory for Claude Code (a plugin you install on this PC): its database is brought into the sandbox.',
|
|
136
|
+
size: '',
|
|
137
|
+
source: { resolve: 'setting' },
|
|
138
|
+
exe: '',
|
|
139
|
+
setting: 'claudeProfile.syncClaudeMem',
|
|
140
|
+
plugsInto: 'Claude Code, with your Claude settings copied from this PC',
|
|
141
|
+
userOnly: true,
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
id: 'rea',
|
|
145
|
+
name: 'REA (Reverse Engineer Anything)',
|
|
146
|
+
category: 'agents',
|
|
147
|
+
about: 'Agent-driven reverse engineering (MCP server and CLI, by morluto). Run `rea setup` once to add it to Claude Code and Codex. Native binaries need Ghidra; needs Node 22.19+.',
|
|
148
|
+
size: '8 MB',
|
|
149
|
+
source: { npm: 'rea-agents' },
|
|
150
|
+
exe: 'rea.cmd',
|
|
151
|
+
cli: true,
|
|
152
|
+
plugsInto: 'Claude Code and Codex as an MCP server, after `rea setup`',
|
|
153
|
+
},
|
|
53
154
|
// ---- editors
|
|
54
155
|
{
|
|
55
156
|
id: 'vscode',
|
|
@@ -511,16 +612,6 @@ export const APPS = [
|
|
|
511
612
|
cli: true,
|
|
512
613
|
},
|
|
513
614
|
// ---- reverse engineering
|
|
514
|
-
{
|
|
515
|
-
id: 'rea',
|
|
516
|
-
name: 'REA (Reverse Engineer Anything)',
|
|
517
|
-
category: 'reversing',
|
|
518
|
-
about: 'Agent-driven reverse engineering (MCP server and CLI, by morluto). Run `rea setup` once to add it to Claude Code and Codex. Native binaries need Ghidra; needs Node 22.19+.',
|
|
519
|
-
size: '8 MB',
|
|
520
|
-
source: { npm: 'rea-agents' },
|
|
521
|
-
exe: 'rea.cmd',
|
|
522
|
-
cli: true,
|
|
523
|
-
},
|
|
524
615
|
{
|
|
525
616
|
id: 'ghidra',
|
|
526
617
|
name: 'Ghidra',
|
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
export { type AppCategory, APP_CATEGORIES, type AppSource, type AppEntry, APPS, appById } from './catalogue.js';
|
|
2
|
-
export { exeMatcher, appsToInstall, appsNeedMesa } from './selection.js';
|
|
1
|
+
export { type AddonMcp, type AppCategory, APP_CATEGORIES, type AppSource, type AppEntry, APPS, appById, CCUSAGE_VERSION, CONTEXT7_MCP_VERSION, HEADROOM_VERSION, PLAYWRIGHT_MCP_VERSION, RTK_VERSION, } from './catalogue.js';
|
|
2
|
+
export { exeMatcher, appsToInstall, appsNeedMesa, appsNeedUv } from './selection.js';
|
|
3
|
+
export { ADDON_MCP_PREFIX, addonGuideLine, addonMcpServer, claudeManagedSettings, HEADROOM_ENV, HEADROOM_PORT, HEADROOM_UPSTREAM, headroomArgs, headroomEnv, headroomGuideLine, headroomUrl, type ManagedSettings, type McpServerConfig, rtkHookCommand, RTK_GUIDE_LINE, withAddonMcpServers, } from './agent-addons.js';
|