windows-development-sandbox 1.11.7 → 1.13.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 +13 -6
- package/config.example.json +15 -4
- package/dist/app/features/access/access.def.d.ts +190 -0
- package/dist/app/features/access/access.def.js +273 -0
- package/dist/app/features/access/access.run.d.ts +79 -0
- package/dist/app/features/access/access.run.js +129 -0
- package/dist/app/features/access/index.d.ts +2 -0
- package/dist/app/features/access/index.js +3 -0
- package/dist/app/features/agents/agent-entry.js +5 -0
- package/dist/app/features/agents/agents.def.js +1 -0
- 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 +50 -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 +350 -0
- package/dist/app/features/audit/audit.def.js +457 -0
- package/dist/app/features/audit/audit.run.d.ts +110 -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 +349 -4
- package/dist/app/features/defs.js +16 -1
- package/dist/app/features/files/files.def.js +1 -0
- package/dist/app/features/health/health.def.js +1 -0
- package/dist/app/features/sandbox/sandbox.def.js +3 -0
- package/dist/app/features/sessions/sessions.def.d.ts +36 -0
- package/dist/app/features/sessions/sessions.def.js +77 -9
- package/dist/app/features/sessions/sessions.run.d.ts +25 -2
- package/dist/app/features/sessions/sessions.run.js +71 -3
- package/dist/app/features/settings/settings.def.d.ts +8 -0
- package/dist/app/features/settings/settings.def.js +17 -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/features/web/web.def.d.ts +9 -0
- package/dist/app/features/web/web.def.js +26 -1
- package/dist/app/features/web/web.run.d.ts +3 -0
- package/dist/app/features/web/web.run.js +11 -3
- package/dist/app/kernel/access.d.ts +33 -0
- package/dist/app/kernel/access.js +44 -0
- package/dist/app/kernel/define.d.ts +26 -2
- package/dist/app/kernel/index.d.ts +1 -0
- package/dist/app/kernel/index.js +1 -0
- package/dist/app/kernel/rpc.d.ts +5 -2
- package/dist/app/registry.d.ts +4 -1
- package/dist/app/registry.js +26 -4
- 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 +3 -2
- package/dist/cli/commands/remote.d.ts +2 -0
- package/dist/cli/commands/remote.js +49 -0
- package/dist/cli/commands/sessions/reopen.js +3 -0
- package/dist/cli/commands/web.js +66 -0
- package/dist/cli/manifest.d.ts +63 -45
- package/dist/cli/manifest.js +33 -18
- package/dist/cli/menu/io-menu.d.ts +5 -3
- package/dist/cli/menu/io-menu.js +12 -5
- 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/remote-menu.d.ts +19 -0
- package/dist/cli/menu/remote-menu.js +108 -0
- package/dist/cli/menu/views.js +4 -0
- package/dist/cli/operations/handler.js +15 -1
- package/dist/cli/services/remote-client.d.ts +16 -0
- package/dist/cli/services/remote-client.js +141 -0
- package/dist/cli/services/sessions-service.d.ts +5 -0
- package/dist/cli/services/sessions-service.js +17 -1
- package/dist/core/config/defaults.js +12 -2
- package/dist/core/local-config/file.js +16 -2
- package/dist/core/local-config/overrides.js +7 -0
- package/dist/core/run/versions.js +9 -8
- package/dist/core/schema/apps.d.ts +9 -0
- package/dist/core/schema/apps.js +9 -0
- package/dist/core/schema/config.d.ts +23 -3
- package/dist/core/schema/config.js +42 -5
- package/dist/core/schema/sandbox.d.ts +1 -0
- package/dist/core/schema/sandbox.js +11 -3
- package/dist/core/schema/web.d.ts +51 -0
- package/dist/core/schema/web.js +46 -1
- package/dist/core/util/fs.d.ts +5 -0
- package/dist/core/util/fs.js +23 -0
- package/dist/core/util/index.d.ts +1 -1
- package/dist/core/util/index.js +1 -1
- 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/daemon.js +2 -0
- package/dist/guest/daemon/idle-shutdown.js +2 -1
- package/dist/guest/daemon/sessions.d.ts +23 -2
- package/dist/guest/daemon/sessions.js +151 -20
- package/dist/guest/entries/daemon.js +2 -1
- package/dist/guest/git/decision.js +7 -0
- package/dist/guest/sessions/cmdline.d.ts +14 -0
- package/dist/guest/sessions/cmdline.js +10 -0
- 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/guest/toolchains/setup.js +3 -0
- 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 +45 -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/host/channel/channel.d.ts +5 -1
- package/dist/host/channel/channel.js +12 -0
- package/dist/host/guard/agent-files.js +7 -1
- package/dist/host/guard/commands.js +13 -1
- package/dist/host/guard/git-config.js +27 -7
- package/dist/host/guard/git-dirs.js +78 -3
- package/dist/host/guard/mark-of-the-web.js +3 -1
- package/dist/host/guard/nested-repos.js +7 -2
- package/dist/host/guard/store.js +1 -0
- package/dist/host/guard/types.d.ts +2 -0
- package/dist/host/launch/mappings.js +11 -1
- package/dist/host/launch/project-scan.js +2 -21
- package/dist/host/launch/stage.js +4 -0
- package/dist/host/life/session-ledger.js +2 -1
- package/dist/host/remote/access-store.d.ts +156 -0
- package/dist/host/remote/access-store.js +308 -0
- package/dist/host/remote/cert.d.ts +20 -0
- package/dist/host/remote/cert.js +103 -0
- package/dist/host/remote/door-client.d.ts +13 -0
- package/dist/host/remote/door-client.js +54 -0
- package/dist/host/remote/firewall.d.ts +44 -0
- package/dist/host/remote/firewall.js +118 -0
- package/dist/host/remote/gate.d.ts +125 -0
- package/dist/host/remote/gate.js +391 -0
- package/dist/host/remote/index.d.ts +8 -0
- package/dist/host/remote/index.js +10 -0
- package/dist/host/remote/lan.d.ts +24 -0
- package/dist/host/remote/lan.js +88 -0
- package/dist/host/remote/tls-store.d.ts +22 -0
- package/dist/host/remote/tls-store.js +63 -0
- package/dist/host/remote/windows-helper.d.ts +67 -0
- package/dist/host/remote/windows-helper.js +129 -0
- package/dist/host/web/bridge.d.ts +5 -1
- package/dist/host/web/bridge.js +6 -2
- package/dist/host/web/browser.js +1 -1
- package/dist/host/web/control.d.ts +4 -1
- package/dist/host/web/control.js +28 -4
- package/dist/host/web/rpc.d.ts +16 -2
- package/dist/host/web/rpc.js +37 -4
- package/dist/host/web/runner.d.ts +4 -0
- package/dist/host/web/runner.js +12 -0
- package/dist/host/web/server.d.ts +18 -2
- package/dist/host/web/server.js +97 -24
- package/dist/host/web/state.d.ts +5 -0
- package/dist/host/web/state.js +8 -0
- package/dist/runtime/agent.js +184 -18
- package/dist/runtime/daemon.js +772 -338
- package/dist/runtime/db.js +184 -18
- package/dist/runtime/gitguard.js +6 -0
- package/dist/runtime/main.js +966 -553
- package/dist/runtime/netproxy.js +184 -18
- package/dist/runtime/view.js +186 -18
- package/dist/selftest/app.test.js +66 -7
- package/dist/selftest/audit.test.js +354 -47
- package/dist/selftest/channel.test.js +16 -0
- package/dist/selftest/cmdline.test.js +16 -0
- package/dist/selftest/git-access.test.js +8 -0
- package/dist/selftest/hcs.test.js +35 -0
- package/dist/selftest/hostile-input.test.js +12 -1
- package/dist/selftest/index.js +2 -0
- package/dist/selftest/live-daemon.test.js +25 -0
- package/dist/selftest/local-config.test.js +30 -0
- package/dist/selftest/menu-screens.js +1 -1
- package/dist/selftest/menu.test.js +3 -3
- package/dist/selftest/remote.test.d.ts +2 -0
- package/dist/selftest/remote.test.js +536 -0
- package/dist/selftest/repo-guard.test.js +56 -2
- package/dist/selftest/resume.test.js +2 -1
- package/dist/selftest/sharing.test.js +9 -2
- package/dist/selftest/support/engine-golden.js +9 -0
- package/dist/selftest/support/fixtures.d.ts +14 -0
- package/dist/selftest/support/web-preview.js +157 -0
- package/dist/selftest/toolchains.test.js +147 -2
- package/dist/selftest/web.test.js +2 -0
- 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/shared/channel/schema.d.ts +23 -0
- package/dist/shared/channel/schema.js +8 -0
- package/dist/shared/pty/protocol.d.ts +2 -0
- package/dist/shared/pty/protocol.js +2 -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-Z2WYAE2Z.js +221 -0
- 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/native/csharp/HostTools/Program.cs +17 -1
- package/native/csharp/HostTools/WebHelper.cs +499 -0
- package/native/scripts/get-network-profile.ps1 +19 -0
- package/native/scripts/set-folder-access.ps1 +11 -0
- package/native/scripts/set-web-firewall.ps1 +103 -0
- 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/sandbox/csharp/PtyHost.cs +20 -0
- package/schemas/config.schema.json +78 -4
- package/schemas/sandbox-config.schema.json +2 -20
- package/dist/cli/commands/maintenance/audit.d.ts +0 -3
- package/dist/cli/commands/maintenance/audit.js +0 -58
- package/dist/web/main-K2JMCAZI.js +0 -219
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
// Remote access's work (docs/design/remote-access.md): the access list in <storage>\web\access.json (shared with the
|
|
2
|
+
// running web server, which reads it again when it changes and closes the pages of whoever was signed out), the
|
|
3
|
+
// remote settings, the firewall rule and the network check of LAN mode. The one-time code is returned once and only
|
|
4
|
+
// its hash is kept.
|
|
5
|
+
import { requireOnPc } from '../../../cli/commands/context.js';
|
|
6
|
+
import { fail } from '../../../core/util/index.js';
|
|
7
|
+
import { AccessStore, addRuleRequest, applyFirewall, chooseLanAddress, lanUrl, networkProblem, networkProfile, removeRuleRequest, showCode, } from '../../../host/remote/index.js';
|
|
8
|
+
import { SecretStore } from '../../../host/secrets/index.js';
|
|
9
|
+
import { protectPrivateFolder } from '../../../host/sharing/index.js';
|
|
10
|
+
import { selfTestRunning } from '../../../host/test-guard.js';
|
|
11
|
+
import { WebStore } from '../../../host/web/index.js';
|
|
12
|
+
import { implement } from '../../context.js';
|
|
13
|
+
import { ConfigFile } from '../settings/index.js';
|
|
14
|
+
import { accessStatus, allowDevice, lanOff, lanOn, revokeAccess, revokeAllAccess } from './access.def.js';
|
|
15
|
+
/** The access list of this user (the folder is the web state's private one). */
|
|
16
|
+
export function accessStoreOf(ctx) {
|
|
17
|
+
return AccessStore.at(ctx.storageRoot, SecretStore.standard(), {
|
|
18
|
+
// The self-test never changes folder permissions; its storage is a temp folder.
|
|
19
|
+
protect: selfTestRunning() ? undefined : (dir) => protectPrivateFolder(dir),
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
function lanStateOf(ctx) {
|
|
23
|
+
const lan = new WebStore(ctx.storageRoot).read()?.lan;
|
|
24
|
+
return { url: lan?.url ?? null, fingerprint: lan?.fingerprint ?? null, problem: lan?.problem ?? null };
|
|
25
|
+
}
|
|
26
|
+
function setLan(ctx, enabled) {
|
|
27
|
+
const outcome = ConfigFile.user(ctx.loaded.configPath).set(['web', 'remote', 'lan', 'enabled'], enabled);
|
|
28
|
+
if (typeof outcome === 'object')
|
|
29
|
+
fail('Could not change web.remote.lan.enabled: ' + outcome.problems.join('; '));
|
|
30
|
+
}
|
|
31
|
+
export const accessStatusRun = implement(accessStatus, (ctx) => {
|
|
32
|
+
requireOnPc('web');
|
|
33
|
+
const remote = ctx.loaded.config.web.remote;
|
|
34
|
+
const data = accessStoreOf(ctx).read();
|
|
35
|
+
const now = Date.now();
|
|
36
|
+
const alive = (iso) => Date.parse(iso) > now;
|
|
37
|
+
const lan = lanStateOf(ctx);
|
|
38
|
+
return {
|
|
39
|
+
lan: {
|
|
40
|
+
enabled: remote.lan.enabled,
|
|
41
|
+
running: remote.lan.enabled && lan.url !== null,
|
|
42
|
+
address: remote.lan.enabled ? lan.url : null,
|
|
43
|
+
fingerprint: remote.lan.enabled ? lan.fingerprint : null,
|
|
44
|
+
problem: remote.lan.enabled ? lan.problem : null,
|
|
45
|
+
network: data.network,
|
|
46
|
+
},
|
|
47
|
+
windowsSignIn: remote.windowsSignIn,
|
|
48
|
+
users: remote.users.map((u) => ({ name: u.name, role: u.role })),
|
|
49
|
+
rememberDays: remote.rememberDays,
|
|
50
|
+
devices: data.devices
|
|
51
|
+
.filter((d) => alive(d.expiresAt))
|
|
52
|
+
.map((d) => ({
|
|
53
|
+
id: d.id,
|
|
54
|
+
name: d.name,
|
|
55
|
+
role: d.role,
|
|
56
|
+
address: d.address,
|
|
57
|
+
createdAt: d.createdAt,
|
|
58
|
+
expiresAt: d.expiresAt,
|
|
59
|
+
lastSeen: d.lastSeen ?? null,
|
|
60
|
+
})),
|
|
61
|
+
windows: data.windows
|
|
62
|
+
.filter((w) => alive(w.expiresAt))
|
|
63
|
+
.map((w) => ({ id: w.id, user: w.user, createdAt: w.createdAt, expiresAt: w.expiresAt })),
|
|
64
|
+
pending: data.code && alive(data.code.expiresAt) ? { role: data.code.role, expiresAt: data.code.expiresAt } : null,
|
|
65
|
+
codeVoided: data.codeVoided,
|
|
66
|
+
};
|
|
67
|
+
});
|
|
68
|
+
export const allowDeviceRun = implement(allowDevice, (ctx, input) => {
|
|
69
|
+
requireOnPc('web');
|
|
70
|
+
if (!ctx.loaded.config.web.remote.lan.enabled)
|
|
71
|
+
fail('LAN mode is off, so no device on your network can reach the web UI.', 'Turn it on first: sandbox web lan-on');
|
|
72
|
+
const { code, expiresAt } = accessStoreOf(ctx).newDeviceCode(input.role);
|
|
73
|
+
const lan = lanStateOf(ctx);
|
|
74
|
+
return { code: showCode(code), expiresAt, role: input.role, address: lan.url, fingerprint: lan.fingerprint };
|
|
75
|
+
});
|
|
76
|
+
export const revokeAccessRun = implement(revokeAccess, (ctx, input) => {
|
|
77
|
+
requireOnPc('web');
|
|
78
|
+
if (!accessStoreOf(ctx).revoke(input.id))
|
|
79
|
+
fail('There is no device or sign-in ' + input.id + '.', 'See sandbox web devices.');
|
|
80
|
+
return { message: 'Signed out. Its open pages close within half a minute.' };
|
|
81
|
+
});
|
|
82
|
+
export const revokeAllAccessRun = implement(revokeAllAccess, async (ctx, _input, io) => {
|
|
83
|
+
requireOnPc('web');
|
|
84
|
+
const ok = await io.confirm({
|
|
85
|
+
title: 'Sign out every device and Windows user now?',
|
|
86
|
+
danger: true,
|
|
87
|
+
yes: 'Sign out all',
|
|
88
|
+
});
|
|
89
|
+
if (!ok)
|
|
90
|
+
return { message: 'Nothing changed.' };
|
|
91
|
+
accessStoreOf(ctx).revokeAll('everything');
|
|
92
|
+
return { message: 'Everyone is signed out, and a code not used yet is void.' };
|
|
93
|
+
});
|
|
94
|
+
export const lanOnRun = implement(lanOn, async (ctx, _input, io) => {
|
|
95
|
+
requireOnPc('web');
|
|
96
|
+
const lan = ctx.loaded.config.web.remote.lan;
|
|
97
|
+
const bound = chooseLanAddress(lan.address);
|
|
98
|
+
io.progress('Checking the network');
|
|
99
|
+
const profile = await networkProfile(bound.address);
|
|
100
|
+
const problem = networkProblem(profile, null);
|
|
101
|
+
if (problem)
|
|
102
|
+
fail(problem, 'LAN mode only runs on a network you marked Private (Windows Settings > Network > your network).');
|
|
103
|
+
io.progress('Adding the firewall rule (Windows asks for permission)');
|
|
104
|
+
await applyFirewall(addRuleRequest(lan.port, bound.address));
|
|
105
|
+
accessStoreOf(ctx).setNetwork(profile.name);
|
|
106
|
+
setLan(ctx, true);
|
|
107
|
+
const address = lanUrl(bound.address, lan.port);
|
|
108
|
+
return {
|
|
109
|
+
address,
|
|
110
|
+
message: 'LAN mode is on for the network "' +
|
|
111
|
+
profile.name +
|
|
112
|
+
'": devices open ' +
|
|
113
|
+
address +
|
|
114
|
+
' (the running web UI starts listening within half a minute). Next: Allow a new device.',
|
|
115
|
+
};
|
|
116
|
+
});
|
|
117
|
+
export const lanOffRun = implement(lanOff, async (ctx, _input, io) => {
|
|
118
|
+
requireOnPc('web');
|
|
119
|
+
const ok = await io.confirm({ title: 'Turn off LAN mode and sign out every device?', danger: true, yes: 'Turn off' });
|
|
120
|
+
if (!ok)
|
|
121
|
+
return { message: 'Nothing changed.' };
|
|
122
|
+
setLan(ctx, false);
|
|
123
|
+
const store = accessStoreOf(ctx);
|
|
124
|
+
store.revokeAll('devices');
|
|
125
|
+
store.setNetwork(null);
|
|
126
|
+
io.progress('Removing the firewall rule (Windows asks for permission)');
|
|
127
|
+
await applyFirewall(removeRuleRequest());
|
|
128
|
+
return { message: 'LAN mode is off: the firewall rule is gone and every device is signed out.' };
|
|
129
|
+
});
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
// adding and removing one change. Node side (it checks names with the config schema); the agents run writes them.
|
|
3
3
|
import { DEFAULTS } from '../../../core/config/index.js';
|
|
4
4
|
import { HarnessSchema, NameSchema, describeIssues } from '../../../core/schema/index.js';
|
|
5
|
+
/** Names an agent cannot have. */
|
|
6
|
+
const RESERVED = ['shell', 'pwsh', 'powershell', 'cmd', 'close'];
|
|
5
7
|
/** Nothing wrong with the name (true), or what is wrong. Pure. */
|
|
6
8
|
export function agentNameProblem(name, taken) {
|
|
7
9
|
const typed = name.trim();
|
|
@@ -12,6 +14,9 @@ export function agentNameProblem(name, taken) {
|
|
|
12
14
|
return 'Use letters, numbers, - and _ only.';
|
|
13
15
|
if (taken.some((t) => t.toLowerCase() === typed.toLowerCase()))
|
|
14
16
|
return typed + ' is an agent already.';
|
|
17
|
+
// Shell sessions are listed by these names, and sandbox shell / close are commands.
|
|
18
|
+
if (RESERVED.includes(typed.toLowerCase()))
|
|
19
|
+
return typed + ' is taken by the sandbox; choose another name.';
|
|
15
20
|
return true;
|
|
16
21
|
}
|
|
17
22
|
/** The command a package usually provides: "@scope/my-agent@latest" gives "my-agent". Pure. */
|
|
@@ -21,7 +21,10 @@ export interface AppsSource {
|
|
|
21
21
|
}
|
|
22
22
|
/** The files as they are on disk. */
|
|
23
23
|
export declare const SAVED: AppsSource;
|
|
24
|
-
/**
|
|
24
|
+
/**
|
|
25
|
+
* The catalogue entries a file can change (a project cannot change an app that has a setting of its own, nor an agent
|
|
26
|
+
* add-on that is yours only).
|
|
27
|
+
*/
|
|
25
28
|
export declare function entriesOf(file: ConfigFile): AppEntry[];
|
|
26
29
|
/** Is the entry on in this source? */
|
|
27
30
|
export declare function appIsOn(entry: AppEntry, file: ConfigFile, source: AppsSource): boolean;
|
|
@@ -17,9 +17,12 @@ export const SAVED = {
|
|
|
17
17
|
setting: (file, key) => _.get(file.userConfig(), key),
|
|
18
18
|
custom: (file) => (file.isProject ? {} : file.userConfig().apps.custom),
|
|
19
19
|
};
|
|
20
|
-
/**
|
|
20
|
+
/**
|
|
21
|
+
* The catalogue entries a file can change (a project cannot change an app that has a setting of its own, nor an agent
|
|
22
|
+
* add-on that is yours only).
|
|
23
|
+
*/
|
|
21
24
|
export function entriesOf(file) {
|
|
22
|
-
return APPS.filter((a) => !(file.isProject && a.setting));
|
|
25
|
+
return APPS.filter((a) => !(file.isProject && (a.setting || a.userOnly)));
|
|
23
26
|
}
|
|
24
27
|
/** Downloaded already (into this data folder), so the next sandbox starts it without a download. */
|
|
25
28
|
function installed(entry, dataPath) {
|
|
@@ -57,6 +60,8 @@ export function appRows(file, dataPath, source = SAVED) {
|
|
|
57
60
|
cli: Boolean(entry.cli),
|
|
58
61
|
mesa: entry.needs?.includes('mesa') ?? false,
|
|
59
62
|
fullGitOnly: Boolean(entry.fullGitOnly),
|
|
63
|
+
...(entry.plugsInto ? { plugsInto: entry.plugsInto } : {}),
|
|
64
|
+
...(entry.service ? { service: true } : {}),
|
|
60
65
|
...settingParts(entry, file, source),
|
|
61
66
|
installed: installed(entry, dataPath),
|
|
62
67
|
on: appIsOn(entry, file, source),
|
|
@@ -32,6 +32,8 @@ export declare const AppRowSchema: z.ZodObject<{
|
|
|
32
32
|
cli: z.ZodBoolean;
|
|
33
33
|
mesa: z.ZodBoolean;
|
|
34
34
|
fullGitOnly: z.ZodBoolean;
|
|
35
|
+
plugsInto: z.ZodOptional<z.ZodString>;
|
|
36
|
+
service: z.ZodOptional<z.ZodBoolean>;
|
|
35
37
|
setting: z.ZodOptional<z.ZodString>;
|
|
36
38
|
settingField: z.ZodOptional<z.ZodType<Field, unknown, z.core.$ZodTypeInternals<Field, unknown>>>;
|
|
37
39
|
settingValue: z.ZodOptional<z.ZodUnknown>;
|
|
@@ -63,6 +65,8 @@ export declare const AppsListSchema: z.ZodObject<{
|
|
|
63
65
|
cli: z.ZodBoolean;
|
|
64
66
|
mesa: z.ZodBoolean;
|
|
65
67
|
fullGitOnly: z.ZodBoolean;
|
|
68
|
+
plugsInto: z.ZodOptional<z.ZodString>;
|
|
69
|
+
service: z.ZodOptional<z.ZodBoolean>;
|
|
66
70
|
setting: z.ZodOptional<z.ZodString>;
|
|
67
71
|
settingField: z.ZodOptional<z.ZodType<Field, unknown, z.core.$ZodTypeInternals<Field, unknown>>>;
|
|
68
72
|
settingValue: z.ZodOptional<z.ZodUnknown>;
|
|
@@ -100,6 +104,8 @@ export declare const listApps: import("../../kernel/define.js").QueryDef<z.ZodOb
|
|
|
100
104
|
cli: z.ZodBoolean;
|
|
101
105
|
mesa: z.ZodBoolean;
|
|
102
106
|
fullGitOnly: z.ZodBoolean;
|
|
107
|
+
plugsInto: z.ZodOptional<z.ZodString>;
|
|
108
|
+
service: z.ZodOptional<z.ZodBoolean>;
|
|
103
109
|
setting: z.ZodOptional<z.ZodString>;
|
|
104
110
|
settingField: z.ZodOptional<z.ZodType<Field, unknown, z.core.$ZodTypeInternals<Field, unknown>>>;
|
|
105
111
|
settingValue: z.ZodOptional<z.ZodUnknown>;
|
|
@@ -137,6 +143,17 @@ export declare const setCustomApps: import("../../kernel/define.js").OperationDe
|
|
|
137
143
|
export declare const requestApp: import("../../kernel/define.js").OperationDef<z.ZodObject<{}, z.core.$strip>, z.ZodObject<{
|
|
138
144
|
url: z.ZodString;
|
|
139
145
|
}, z.core.$strip>, "apps.request">;
|
|
146
|
+
/** One line under an app: a short label ('' for a warning on its own) and its text. */
|
|
147
|
+
export interface AppDetail {
|
|
148
|
+
readonly label: string;
|
|
149
|
+
readonly text: string;
|
|
150
|
+
readonly warn?: boolean;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* The lines under an app: its download, how it starts, what it plugs into, what it needs. A setting-backed entry with
|
|
154
|
+
* nothing to download (claude-mem) has no size, so no download lines. The menu draws the same lines with colours.
|
|
155
|
+
*/
|
|
156
|
+
export declare function appDetails(row: AppRow): AppDetail[];
|
|
140
157
|
/** The apps page of one scope: a category per group (with a switch per app), your own programs and their actions. */
|
|
141
158
|
export declare function appsPage(o: AppsList): Page;
|
|
142
159
|
export declare const appsView: import("../../kernel/define.js").ViewDef<{
|
|
@@ -161,6 +178,8 @@ export declare const appsView: import("../../kernel/define.js").ViewDef<{
|
|
|
161
178
|
cli: z.ZodBoolean;
|
|
162
179
|
mesa: z.ZodBoolean;
|
|
163
180
|
fullGitOnly: z.ZodBoolean;
|
|
181
|
+
plugsInto: z.ZodOptional<z.ZodString>;
|
|
182
|
+
service: z.ZodOptional<z.ZodBoolean>;
|
|
164
183
|
setting: z.ZodOptional<z.ZodString>;
|
|
165
184
|
settingField: z.ZodOptional<z.ZodType<Field, unknown, z.core.$ZodTypeInternals<Field, unknown>>>;
|
|
166
185
|
settingValue: z.ZodOptional<z.ZodUnknown>;
|
|
@@ -10,6 +10,12 @@ export const AppsScopeSchema = z
|
|
|
10
10
|
.describe("Your own settings (all projects) or this folder's .sandbox-config.json (this project)");
|
|
11
11
|
/** The groups the page shows; the menu and the web list the apps by these, so the navigation stays short. */
|
|
12
12
|
export const APP_GROUPS = [
|
|
13
|
+
{
|
|
14
|
+
id: 'agents',
|
|
15
|
+
label: 'Agent add-ons',
|
|
16
|
+
description: 'Plug-ins for the agents: rtk and Headroom save tokens, claude-mem gives Claude a memory, ccusage reports token use, Playwright, Context7 and REA add MCP servers. Only your own settings turn them on (except REA).',
|
|
17
|
+
members: ['agents'],
|
|
18
|
+
},
|
|
13
19
|
{
|
|
14
20
|
id: 'editors',
|
|
15
21
|
label: 'Editors & docs',
|
|
@@ -79,6 +85,8 @@ export const AppRowSchema = z.object({
|
|
|
79
85
|
cli: z.boolean().describe('A command-line tool (on PATH), not a desktop app'),
|
|
80
86
|
mesa: z.boolean().describe('Needs OpenGL on the CPU (Mesa)'),
|
|
81
87
|
fullGitOnly: z.boolean().describe('Only with full git access'),
|
|
88
|
+
plugsInto: z.string().optional().describe('Which agents an add-on plugs into, and how'),
|
|
89
|
+
service: z.boolean().optional().describe('Started by the sandbox or the agent (a proxy, an MCP server)'),
|
|
82
90
|
setting: z.string().optional().describe('The setting it stands for (VS Code, Windows Terminal)'),
|
|
83
91
|
/** That setting's own Field and value (on, off or auto), so it is changed with its own control. */
|
|
84
92
|
settingField: FieldSchema.optional(),
|
|
@@ -137,6 +145,7 @@ export const setCustomApps = defineOperation({
|
|
|
137
145
|
});
|
|
138
146
|
export const requestApp = defineOperation({
|
|
139
147
|
id: 'apps.request',
|
|
148
|
+
access: 'control',
|
|
140
149
|
title: 'Request an app…',
|
|
141
150
|
summary: 'open a prefilled GitHub request for an app that is not in the list',
|
|
142
151
|
input: z.object({}),
|
|
@@ -144,23 +153,52 @@ export const requestApp = defineOperation({
|
|
|
144
153
|
text: (o) => 'Opened the request form in your browser: ' + o.url,
|
|
145
154
|
surfaces: { except: { cli: 'a request is filed on GitHub; the address is in docs' } },
|
|
146
155
|
});
|
|
147
|
-
/**
|
|
148
|
-
|
|
156
|
+
/**
|
|
157
|
+
* The lines under an app: its download, how it starts, what it plugs into, what it needs. A setting-backed entry with
|
|
158
|
+
* nothing to download (claude-mem) has no size, so no download lines. The menu draws the same lines with colours.
|
|
159
|
+
*/
|
|
160
|
+
export function appDetails(row) {
|
|
161
|
+
const downloads = row.size !== '';
|
|
149
162
|
return [
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
163
|
+
...(downloads
|
|
164
|
+
? [
|
|
165
|
+
{
|
|
166
|
+
label: 'Download',
|
|
167
|
+
text: '~' +
|
|
168
|
+
row.size +
|
|
169
|
+
' once, kept in C:\\AgentData\\tools\\' +
|
|
170
|
+
(row.setting ? '' : 'apps\\') +
|
|
171
|
+
row.id +
|
|
172
|
+
(row.installed ? ' (downloaded already)' : ''),
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
label: 'Starts',
|
|
176
|
+
text: row.service
|
|
177
|
+
? 'by the sandbox or the agent (no shortcut, not on PATH)'
|
|
178
|
+
: row.cli
|
|
179
|
+
? 'from a terminal (on PATH)'
|
|
180
|
+
: 'from the desktop or Start > Sandbox apps',
|
|
181
|
+
},
|
|
182
|
+
]
|
|
183
|
+
: []),
|
|
184
|
+
...(row.plugsInto ? [{ label: 'Plugs in', text: row.plugsInto }] : []),
|
|
185
|
+
...(row.mesa ? [{ label: 'Graphics', text: 'OpenGL on the CPU (Mesa) is added for it' }] : []),
|
|
158
186
|
...(row.fullGitOnly
|
|
159
|
-
? [
|
|
187
|
+
? [
|
|
188
|
+
{
|
|
189
|
+
label: '',
|
|
190
|
+
text: 'Needs full git access (Settings > Projects & setup > Git): it pushes with its own git.',
|
|
191
|
+
warn: true,
|
|
192
|
+
},
|
|
193
|
+
]
|
|
160
194
|
: []),
|
|
161
|
-
...(row.setting ? ['Setting
|
|
195
|
+
...(row.setting ? [{ label: 'Setting', text: row.setting }] : []),
|
|
162
196
|
];
|
|
163
197
|
}
|
|
198
|
+
/** The lines as plain text, like the CLI's. */
|
|
199
|
+
function details(row) {
|
|
200
|
+
return appDetails(row).map((d) => (d.label ? d.label.padEnd(10) : '') + d.text);
|
|
201
|
+
}
|
|
164
202
|
/** The switch that turns an app on or off: one boolean field, changed in place. */
|
|
165
203
|
function switchField(row) {
|
|
166
204
|
return { name: 'on', key: ['apps', 'install'], kind: 'boolean', label: row.name, optional: false };
|
|
@@ -19,6 +19,8 @@ export declare const listAppsRun: import("../../kernel/define.js").Implementatio
|
|
|
19
19
|
cli: import("zod").ZodBoolean;
|
|
20
20
|
mesa: import("zod").ZodBoolean;
|
|
21
21
|
fullGitOnly: import("zod").ZodBoolean;
|
|
22
|
+
plugsInto: import("zod").ZodOptional<import("zod").ZodString>;
|
|
23
|
+
service: import("zod").ZodOptional<import("zod").ZodBoolean>;
|
|
22
24
|
setting: import("zod").ZodOptional<import("zod").ZodString>;
|
|
23
25
|
settingField: import("zod").ZodOptional<import("zod").ZodType<import("../../kernel/fields.js").Field, unknown, import("zod/v4/core").$ZodTypeInternals<import("../../kernel/fields.js").Field, unknown>>>;
|
|
24
26
|
settingValue: import("zod").ZodOptional<import("zod").ZodUnknown>;
|
|
@@ -26,7 +26,7 @@ export const listAppsRun = implement(listApps, (ctx, input) => {
|
|
|
26
26
|
export const toggleAppRun = implement(toggleApp, (ctx, input) => {
|
|
27
27
|
const file = settingsFiles(ctx)[input.scope];
|
|
28
28
|
const entry = APPS.find((a) => a.id === input.id);
|
|
29
|
-
if (!entry || (file.isProject && entry.setting))
|
|
29
|
+
if (!entry || (file.isProject && (entry.setting || entry.userOnly)))
|
|
30
30
|
fail('"' + input.id + '" is not an app this scope can change.', 'Pick one of the apps the Apps page lists.');
|
|
31
31
|
const name = entry.name;
|
|
32
32
|
// A setting-backed app (VS Code, Windows Terminal) is its setting; a catalogue app is one id in apps.install.
|