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
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Runs a definition as a command: its input from the command line, its questions on the terminal, and its output as
|
|
2
2
|
// --json, as the definition's one-line text, or as the page of the view that shows it. A declined question prints
|
|
3
|
-
// "Nothing changed." and exits 1, as the commands always have.
|
|
3
|
+
// "Nothing changed." and exits 1, as the commands always have. An operation that opens a session's terminal (a new
|
|
4
|
+
// shell) attaches this terminal to it afterwards.
|
|
4
5
|
import { VIEWS } from '../../app/features/defs.js';
|
|
5
6
|
import { runDefinition } from '../../app/registry.js';
|
|
6
7
|
import { Cancelled } from '../../app/kernel/index.js';
|
|
@@ -33,6 +34,16 @@ function printOutput(def, output, input) {
|
|
|
33
34
|
else
|
|
34
35
|
console.log(JSON.stringify(output, null, 2));
|
|
35
36
|
}
|
|
37
|
+
/** Attaches this terminal to the session an operation opened (in a script: says how to). */
|
|
38
|
+
async function attachOpened(ctx, session) {
|
|
39
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
40
|
+
nextStep('sandbox attach ' + session);
|
|
41
|
+
return 0;
|
|
42
|
+
}
|
|
43
|
+
// Loaded when needed: attach reaches the launch commands, which reach this handler (no import cycle).
|
|
44
|
+
const { attach } = await import('../commands/sessions/attach.js');
|
|
45
|
+
return attach({ ...ctx, options: { ...ctx.options, positionals: [session] } });
|
|
46
|
+
}
|
|
36
47
|
/** The command for a definition. */
|
|
37
48
|
export function definitionHandler(def) {
|
|
38
49
|
return async (ctx) => {
|
|
@@ -47,6 +58,9 @@ export function definitionHandler(def) {
|
|
|
47
58
|
return code;
|
|
48
59
|
}
|
|
49
60
|
printOutput(def, output, def.input.parse(input));
|
|
61
|
+
const opened = def.kind === 'operation' ? def.opens?.(output) : undefined;
|
|
62
|
+
if (opened?.kind === 'terminal')
|
|
63
|
+
return attachOpened(ctx, opened.session);
|
|
50
64
|
return code;
|
|
51
65
|
}
|
|
52
66
|
catch (e) {
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type OperationIO } from '../../app/kernel/index.js';
|
|
2
|
+
export declare class RemoteClient {
|
|
3
|
+
private readonly socket;
|
|
4
|
+
private readonly pending;
|
|
5
|
+
private next;
|
|
6
|
+
private constructor();
|
|
7
|
+
/** Trades the one-time token for a sign-in cookie, then opens the RPC socket. */
|
|
8
|
+
static connect(port: number, token: string): Promise<RemoteClient>;
|
|
9
|
+
private receive;
|
|
10
|
+
private send;
|
|
11
|
+
/** Runs a definition on the owner's side (a query or an operation) and returns its output. */
|
|
12
|
+
run(id: string, input: unknown, io: OperationIO): Promise<unknown>;
|
|
13
|
+
close(): void;
|
|
14
|
+
}
|
|
15
|
+
/** Connects, or fails with the reason and what to do. */
|
|
16
|
+
export declare function remoteClient(port: number, token: string): Promise<RemoteClient>;
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
// `sandbox remote`'s line to the owner's web UI (docs/design/remote-access.md): signs in with the one-time token of
|
|
2
|
+
// Windows sign-in, then runs the shared core's queries and operations over the RPC socket, as the browser does. What
|
|
3
|
+
// the owner's server sends is checked against the kernel's schemas (anything else is dropped) and every text is shown
|
|
4
|
+
// through safeText by the menu. The owner's server checks the role of every call.
|
|
5
|
+
import http from 'node:http';
|
|
6
|
+
import { WebSocket } from 'ws';
|
|
7
|
+
import { fromWireError, ServerMessageSchema, } from '../../app/kernel/index.js';
|
|
8
|
+
import { fail } from '../../core/util/index.js';
|
|
9
|
+
import { CSRF_HEADER, CSRF_VALUE } from '../../shared/web/index.js';
|
|
10
|
+
const CONNECT_TIMEOUT_MS = 10_000;
|
|
11
|
+
/** Asks an OperationIO a question that came over the socket. */
|
|
12
|
+
function askIO(io, q) {
|
|
13
|
+
switch (q.kind) {
|
|
14
|
+
case 'confirm':
|
|
15
|
+
return io.confirm(q);
|
|
16
|
+
case 'choose':
|
|
17
|
+
return io.choose(q);
|
|
18
|
+
case 'checklist':
|
|
19
|
+
return io.checklist(q);
|
|
20
|
+
case 'text':
|
|
21
|
+
return io.text(q);
|
|
22
|
+
case 'secret':
|
|
23
|
+
return io.secret(q);
|
|
24
|
+
case 'path':
|
|
25
|
+
return io.path(q);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export class RemoteClient {
|
|
29
|
+
socket;
|
|
30
|
+
pending = new Map();
|
|
31
|
+
next = 1;
|
|
32
|
+
constructor(socket) {
|
|
33
|
+
this.socket = socket;
|
|
34
|
+
socket.on('message', (data, binary) => {
|
|
35
|
+
if (binary)
|
|
36
|
+
return;
|
|
37
|
+
let value;
|
|
38
|
+
try {
|
|
39
|
+
value = JSON.parse(data.toString());
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const parsed = ServerMessageSchema.safeParse(value);
|
|
45
|
+
if (parsed.success)
|
|
46
|
+
this.receive(parsed.data);
|
|
47
|
+
});
|
|
48
|
+
socket.on('close', () => {
|
|
49
|
+
for (const p of this.pending.values())
|
|
50
|
+
p.reject(new Error('The connection to the web UI closed.'));
|
|
51
|
+
this.pending.clear();
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
/** Trades the one-time token for a sign-in cookie, then opens the RPC socket. */
|
|
55
|
+
static async connect(port, token) {
|
|
56
|
+
const origin = 'http://127.0.0.1:' + port;
|
|
57
|
+
const cookie = await new Promise((resolve, reject) => {
|
|
58
|
+
const body = JSON.stringify({ token });
|
|
59
|
+
const req = http.request({
|
|
60
|
+
host: '127.0.0.1',
|
|
61
|
+
port,
|
|
62
|
+
method: 'POST',
|
|
63
|
+
path: '/api/login/once',
|
|
64
|
+
timeout: CONNECT_TIMEOUT_MS,
|
|
65
|
+
headers: {
|
|
66
|
+
Origin: origin,
|
|
67
|
+
[CSRF_HEADER]: CSRF_VALUE,
|
|
68
|
+
'Content-Type': 'application/json',
|
|
69
|
+
'Content-Length': Buffer.byteLength(body),
|
|
70
|
+
},
|
|
71
|
+
}, (res) => {
|
|
72
|
+
res.resume();
|
|
73
|
+
const value = (res.headers['set-cookie'] ?? [])[0]?.split(';')[0];
|
|
74
|
+
if (res.statusCode === 200 && value)
|
|
75
|
+
resolve(value);
|
|
76
|
+
else
|
|
77
|
+
reject(new Error('The web UI did not sign you in (' + res.statusCode + ').'));
|
|
78
|
+
});
|
|
79
|
+
req.on('timeout', () => req.destroy(new Error('The web UI did not answer.')));
|
|
80
|
+
req.on('error', reject);
|
|
81
|
+
req.end(body);
|
|
82
|
+
});
|
|
83
|
+
const socket = new WebSocket('ws://127.0.0.1:' + port + '/api/rpc', {
|
|
84
|
+
headers: { Cookie: cookie, Origin: origin },
|
|
85
|
+
handshakeTimeout: CONNECT_TIMEOUT_MS,
|
|
86
|
+
});
|
|
87
|
+
await new Promise((resolve, reject) => {
|
|
88
|
+
socket.once('open', () => resolve());
|
|
89
|
+
socket.once('error', reject);
|
|
90
|
+
socket.once('unexpected-response', (_req, res) => reject(new Error('Refused (' + res.statusCode + ').')));
|
|
91
|
+
});
|
|
92
|
+
return new RemoteClient(socket);
|
|
93
|
+
}
|
|
94
|
+
receive(m) {
|
|
95
|
+
const p = this.pending.get(m.id);
|
|
96
|
+
if (!p)
|
|
97
|
+
return;
|
|
98
|
+
switch (m.t) {
|
|
99
|
+
case 'progress':
|
|
100
|
+
return p.io.progress(m.text);
|
|
101
|
+
case 'log':
|
|
102
|
+
return p.io.log(m.text, m.tone);
|
|
103
|
+
case 'ask':
|
|
104
|
+
void askIO(p.io, m.question).then((value) => this.send({ t: 'answer', id: m.id, value }), () => this.send({ t: 'answer', id: m.id, value: m.question.kind === 'confirm' ? false : undefined }));
|
|
105
|
+
return;
|
|
106
|
+
case 'result':
|
|
107
|
+
case 'data':
|
|
108
|
+
this.pending.delete(m.id);
|
|
109
|
+
return p.resolve(m.output);
|
|
110
|
+
case 'error':
|
|
111
|
+
this.pending.delete(m.id);
|
|
112
|
+
return p.reject(fromWireError(m.error));
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
send(message) {
|
|
116
|
+
if (this.socket.readyState === WebSocket.OPEN)
|
|
117
|
+
this.socket.send(JSON.stringify(message));
|
|
118
|
+
}
|
|
119
|
+
/** Runs a definition on the owner's side (a query or an operation) and returns its output. */
|
|
120
|
+
run(id, input, io) {
|
|
121
|
+
if (this.socket.readyState !== WebSocket.OPEN)
|
|
122
|
+
return Promise.reject(new Error('Not connected to the web UI.'));
|
|
123
|
+
const callId = 'c' + this.next++;
|
|
124
|
+
return new Promise((resolve, reject) => {
|
|
125
|
+
this.pending.set(callId, { io, resolve, reject });
|
|
126
|
+
this.send({ t: 'call', id: callId, op: id, input: input ?? {} });
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
close() {
|
|
130
|
+
this.socket.close(1000);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
/** Connects, or fails with the reason and what to do. */
|
|
134
|
+
export async function remoteClient(port, token) {
|
|
135
|
+
try {
|
|
136
|
+
return await RemoteClient.connect(port, token);
|
|
137
|
+
}
|
|
138
|
+
catch (e) {
|
|
139
|
+
return fail('Could not reach the web UI on port ' + port + ': ' + (e instanceof Error ? e.message : String(e)), 'Ask its owner to start it (sandbox web start), then try again.');
|
|
140
|
+
}
|
|
141
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { SessionStatus } from '../../core/types.js';
|
|
2
|
+
import type { ShellChoice } from '../../shared/channel/index.js';
|
|
2
3
|
import type { RegistryEntry } from '../../host/projects/index.js';
|
|
3
4
|
import type { Viewers } from '../../shared/pty/index.js';
|
|
4
5
|
import type { Context } from '../commands/context.js';
|
|
@@ -38,5 +39,9 @@ export declare class SessionsService {
|
|
|
38
39
|
restartScope(all: boolean): RestartScope;
|
|
39
40
|
/** Restarts the agents (one kind, or all) of a project or of every session; returns the sandbox's message. */
|
|
40
41
|
restart(harness: string | null, scope?: string): Promise<string>;
|
|
42
|
+
/** Starts a plain shell in a folder of the sandbox and waits until it runs. */
|
|
43
|
+
openShell(shell: ShellChoice, guestDir: string): Promise<SessionStatus>;
|
|
44
|
+
/** Ends a running session and everything it started; returns the sandbox's message (throws when it refused). */
|
|
45
|
+
close(sessionId: string): Promise<string>;
|
|
41
46
|
private projects;
|
|
42
47
|
}
|
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
//
|
|
1
|
+
// Sessions of the running sandbox (agents and plain shells): list, pick one to attach to, restart the agents, open a
|
|
2
|
+
// shell, close one. Returns data, never prints.
|
|
2
3
|
import _ from 'lodash';
|
|
4
|
+
import { fail } from '../../core/util/index.js';
|
|
3
5
|
import { SandboxChannel, sessionsFirstIn, sessionViewers } from '../../host/channel/index.js';
|
|
4
6
|
import { isSandboxRunning } from '../../host/sandbox/index.js';
|
|
5
7
|
import { RunDir } from '../../shared/channel/index.js';
|
|
6
8
|
import { isWithin, projectForGuestDir } from '../../shared/paths/index.js';
|
|
7
9
|
import { hereDir, locateHere, registryOf } from '../commands/projects/here.js';
|
|
10
|
+
/** A shell needs no install: the longest it may take is its project's first setup. */
|
|
11
|
+
const SHELL_START_MS = 15 * 60 * 1000;
|
|
8
12
|
export function viewerText(v) {
|
|
9
13
|
return v === undefined
|
|
10
14
|
? '-'
|
|
@@ -75,6 +79,18 @@ export class SessionsService {
|
|
|
75
79
|
async restart(harness, scope) {
|
|
76
80
|
return (await this.channel.restart(harness, scope)).message;
|
|
77
81
|
}
|
|
82
|
+
/** Starts a plain shell in a folder of the sandbox and waits until it runs. */
|
|
83
|
+
openShell(shell, guestDir) {
|
|
84
|
+
const channel = this.channel;
|
|
85
|
+
return channel.waitForSession(channel.requestShell(shell, guestDir), SHELL_START_MS, () => undefined);
|
|
86
|
+
}
|
|
87
|
+
/** Ends a running session and everything it started; returns the sandbox's message (throws when it refused). */
|
|
88
|
+
async close(sessionId) {
|
|
89
|
+
const answer = await this.channel.closeSession(sessionId);
|
|
90
|
+
if (!answer.ok)
|
|
91
|
+
fail(answer.message);
|
|
92
|
+
return answer.message;
|
|
93
|
+
}
|
|
78
94
|
projects() {
|
|
79
95
|
return registryOf(this.ctx).read()?.projects ?? [];
|
|
80
96
|
}
|
|
@@ -3,7 +3,7 @@ export const DEFAULTS = {
|
|
|
3
3
|
guestWorkspace: 'auto',
|
|
4
4
|
readOnly: false,
|
|
5
5
|
workspaceFilter: { enabled: false, exclude: [], include: [] },
|
|
6
|
-
audit: { enabled:
|
|
6
|
+
audit: { enabled: true, days: 7, maxMb: 100, processes: true, files: true, commits: true },
|
|
7
7
|
mappings: [],
|
|
8
8
|
sandbox: {
|
|
9
9
|
engine: 'windows-sandbox',
|
|
@@ -95,7 +95,17 @@ export const DEFAULTS = {
|
|
|
95
95
|
sqlserver: { enabled: 'auto' },
|
|
96
96
|
},
|
|
97
97
|
attach: { enabled: true, reuseSession: true, auto: false, autoHide: false, confirmCtrlC: true },
|
|
98
|
-
web: {
|
|
98
|
+
web: {
|
|
99
|
+
enabled: false,
|
|
100
|
+
port: 47800,
|
|
101
|
+
openOnLaunch: false,
|
|
102
|
+
remote: {
|
|
103
|
+
windowsSignIn: false,
|
|
104
|
+
users: [],
|
|
105
|
+
rememberDays: 30,
|
|
106
|
+
lan: { enabled: false, port: 47801, address: '' },
|
|
107
|
+
},
|
|
108
|
+
},
|
|
99
109
|
agentCommands: { enabled: false, agents: 'auto' },
|
|
100
110
|
secrets: { strip: true, allow: [], patterns: [], warnFiles: true },
|
|
101
111
|
personalization: { enabled: true },
|
|
@@ -1,13 +1,27 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import crypto from 'node:crypto';
|
|
4
|
-
import { fileExists, parseJsonc } from '../util/index.js';
|
|
4
|
+
import { fail, fileExists, parseJsonc } from '../util/index.js';
|
|
5
5
|
export const LOCAL_CONFIG_NAME = '.sandbox-config.json';
|
|
6
|
+
const MAX_LOCAL_CONFIG_BYTES = 1024 * 1024;
|
|
6
7
|
export function readLocalConfig(dir) {
|
|
7
8
|
const file = path.join(dir, LOCAL_CONFIG_NAME);
|
|
8
9
|
if (!fileExists(file))
|
|
9
10
|
return null;
|
|
10
|
-
|
|
11
|
+
// One handle for the size check and the read (the hash covers exactly the bytes parsed); a repo's file over 1 MB is
|
|
12
|
+
// refused, not loaded.
|
|
13
|
+
const fd = fs.openSync(file, 'r');
|
|
14
|
+
let raw;
|
|
15
|
+
try {
|
|
16
|
+
const size = fs.fstatSync(fd).size;
|
|
17
|
+
if (size > MAX_LOCAL_CONFIG_BYTES)
|
|
18
|
+
fail(file + ' is over 1 MB, so it is not read.', 'Make it smaller, or start with --no-local-config.');
|
|
19
|
+
raw = Buffer.alloc(size);
|
|
20
|
+
fs.readSync(fd, raw, 0, size, 0);
|
|
21
|
+
}
|
|
22
|
+
finally {
|
|
23
|
+
fs.closeSync(fd);
|
|
24
|
+
}
|
|
11
25
|
return {
|
|
12
26
|
path: file,
|
|
13
27
|
hash: crypto.createHash('sha256').update(raw).digest('hex'),
|
|
@@ -14,6 +14,11 @@ export function normalizeOverrides(data, dir, source) {
|
|
|
14
14
|
const missing = scripts?.filter((s) => !fileExists(s)) ?? [];
|
|
15
15
|
if (missing.length)
|
|
16
16
|
fail(source + ' is not valid:\n - scripts: file not found: ' + missing.join(', '));
|
|
17
|
+
// A repo ships this file: a script path outside the project (%USERPROFILE%\.ssh\id_ed25519, ..\..) would copy that
|
|
18
|
+
// file of this PC into the launch folder, which the sandbox reads.
|
|
19
|
+
const outside = scripts?.filter((s) => !insideProject(dir, s)) ?? [];
|
|
20
|
+
if (outside.length)
|
|
21
|
+
fail(source + ' is not valid:\n - scripts: only files inside the project can be used: ' + outside.join(', '));
|
|
17
22
|
const mappings = overrides.mappings?.map((m) => ({ ...m, host: expandPath(m.host, dir) }));
|
|
18
23
|
return { ...overrides, scripts, mappings };
|
|
19
24
|
}
|
|
@@ -62,6 +67,8 @@ export const OFF_IS_SAFER = [
|
|
|
62
67
|
'claudeProfile.enabled',
|
|
63
68
|
'claudeProfile.syncHistory',
|
|
64
69
|
'claudeProfile.syncClaudeMem',
|
|
70
|
+
// Maps this PC's Node, Git, nvm and agent folders into the sandbox.
|
|
71
|
+
'provisioning.copyFromHost',
|
|
65
72
|
];
|
|
66
73
|
export const GIT_STRICTNESS = { full: 0, readonly: 1, none: 2 };
|
|
67
74
|
export const NEVER_LOOSER = [...ON_IS_SAFER, ...OFF_IS_SAFER, 'provisioning.git.access'];
|
|
@@ -1,17 +1,18 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
1
|
import path from 'node:path';
|
|
3
2
|
import { z } from 'zod';
|
|
4
|
-
import { fileExists } from '../util/index.js';
|
|
3
|
+
import { fileExists, readSmallFile } from '../util/index.js';
|
|
4
|
+
/** A version spec as version files hold it (20, lts/iron, 3.12.1, v18.*): anything else is ignored, never printed. */
|
|
5
|
+
const VERSION_SPEC = /^[\w./*~^-]{1,64}$/;
|
|
5
6
|
const firstLine = (file) => {
|
|
6
|
-
|
|
7
|
+
const text = fileExists(file) ? readSmallFile(file, 64 * 1024) : null;
|
|
8
|
+
if (text === null)
|
|
7
9
|
return null;
|
|
8
|
-
const line =
|
|
9
|
-
.readFileSync(file, 'utf8')
|
|
10
|
+
const line = text
|
|
10
11
|
.replace(/^\uFEFF/, '')
|
|
11
12
|
.split(/\r?\n/)
|
|
12
13
|
.map((l) => l.replace(/#.*$/, '').trim())
|
|
13
14
|
.find((l) => l !== '');
|
|
14
|
-
return line
|
|
15
|
+
return line !== undefined && VERSION_SPEC.test(line) ? line : null;
|
|
15
16
|
};
|
|
16
17
|
const VOLTA_PIN = 'package.json volta.node';
|
|
17
18
|
const PackageJsonSchema = z.object({
|
|
@@ -32,8 +33,8 @@ export function voltaNode(workspace) {
|
|
|
32
33
|
if (!fileExists(file))
|
|
33
34
|
return null;
|
|
34
35
|
try {
|
|
35
|
-
return (PackageJsonSchema.safeParse(JSON.parse(
|
|
36
|
-
null);
|
|
36
|
+
return (PackageJsonSchema.safeParse(JSON.parse((readSmallFile(file, 4 * 1024 * 1024) ?? '').replace(/^\uFEFF/, ''))).data
|
|
37
|
+
?.volta?.node ?? null);
|
|
37
38
|
}
|
|
38
39
|
catch {
|
|
39
40
|
return null;
|
|
@@ -2,6 +2,9 @@ import { z } from 'zod';
|
|
|
2
2
|
export declare const AppIdSchema: z.ZodEnum<{
|
|
3
3
|
[x: string]: string;
|
|
4
4
|
}>;
|
|
5
|
+
export declare const ProjectAppIdSchema: z.ZodEnum<{
|
|
6
|
+
[x: string]: string;
|
|
7
|
+
}>;
|
|
5
8
|
export declare const CustomAppIdSchema: z.ZodString;
|
|
6
9
|
export declare const CustomAppSchema: z.ZodObject<{
|
|
7
10
|
name: z.ZodString;
|
|
@@ -18,6 +21,12 @@ export declare const CustomAppSchema: z.ZodObject<{
|
|
|
18
21
|
exe: z.ZodString;
|
|
19
22
|
cli: z.ZodOptional<z.ZodBoolean>;
|
|
20
23
|
}, z.core.$strict>;
|
|
24
|
+
/** What a project file may say about apps: catalogue apps it adds to yours (no add-ons that are yours only). */
|
|
25
|
+
export declare const ProjectAppsSchema: z.ZodObject<{
|
|
26
|
+
install: z.ZodArray<z.ZodEnum<{
|
|
27
|
+
[x: string]: string;
|
|
28
|
+
}>>;
|
|
29
|
+
}, z.core.$strict>;
|
|
21
30
|
export declare const AppsSchema: z.ZodObject<{
|
|
22
31
|
install: z.ZodArray<z.ZodEnum<{
|
|
23
32
|
[x: string]: string;
|
package/dist/core/schema/apps.js
CHANGED
|
@@ -3,6 +3,9 @@ import { z } from 'zod';
|
|
|
3
3
|
import { APPS } from '../../shared/apps/index.js';
|
|
4
4
|
const CATALOGUE_IDS = APPS.filter((a) => !a.setting).map((a) => a.id);
|
|
5
5
|
export const AppIdSchema = z.enum(CATALOGUE_IDS);
|
|
6
|
+
/** The apps a project's .sandbox-config.json may add: not the add-ons that are yours only (they change the agents). */
|
|
7
|
+
const PROJECT_IDS = APPS.filter((a) => !a.setting && !a.userOnly).map((a) => a.id);
|
|
8
|
+
export const ProjectAppIdSchema = z.enum(PROJECT_IDS);
|
|
6
9
|
export const CustomAppIdSchema = z.string().regex(/^[a-z0-9][a-z0-9-]{0,40}$/, 'lower-case letters, digits and - only');
|
|
7
10
|
const compiles = (pattern) => {
|
|
8
11
|
try {
|
|
@@ -51,6 +54,12 @@ export const CustomAppSchema = z
|
|
|
51
54
|
.refine((a) => (a.github && a.asset ? !a.url : Boolean(a.url) && !a.github && !a.asset), {
|
|
52
55
|
message: 'give github + asset, or url (not both)',
|
|
53
56
|
});
|
|
57
|
+
/** What a project file may say about apps: catalogue apps it adds to yours (no add-ons that are yours only). */
|
|
58
|
+
export const ProjectAppsSchema = z.strictObject({
|
|
59
|
+
install: z
|
|
60
|
+
.array(ProjectAppIdSchema)
|
|
61
|
+
.describe('Apps from the catalogue this project adds to yours (agent add-ons such as rtk, Headroom and the MCP servers are set in your own settings only).'),
|
|
62
|
+
});
|
|
54
63
|
export const AppsSchema = z.strictObject({
|
|
55
64
|
install: z
|
|
56
65
|
.array(AppIdSchema)
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* An environment variable a repo's .sandbox-config.json may not set: the git guard's and the sandbox's own (setting
|
|
4
|
+
* AGENT_GIT_ACCESS or GIT_CONFIG_COUNT would undo provisioning.git.access), interpreter hooks (NODE_OPTIONS reaches the
|
|
5
|
+
* git guard's node), proxies and API endpoints (ANTHROPIC_BASE_URL would send your API key to the repo's server).
|
|
6
|
+
*/
|
|
7
|
+
export declare function reservedEnvName(name: string): boolean;
|
|
2
8
|
export declare const ConfigSchema: z.ZodObject<{
|
|
3
9
|
$schema: z.ZodOptional<z.ZodString>;
|
|
4
10
|
defaultHarness: z.ZodString;
|
|
@@ -15,6 +21,7 @@ export declare const ConfigSchema: z.ZodObject<{
|
|
|
15
21
|
maxMb: z.ZodNumber;
|
|
16
22
|
processes: z.ZodBoolean;
|
|
17
23
|
files: z.ZodBoolean;
|
|
24
|
+
commits: z.ZodBoolean;
|
|
18
25
|
}, z.core.$strict>;
|
|
19
26
|
mappings: z.ZodArray<z.ZodObject<{
|
|
20
27
|
host: z.ZodString;
|
|
@@ -252,6 +259,22 @@ export declare const ConfigSchema: z.ZodObject<{
|
|
|
252
259
|
enabled: z.ZodBoolean;
|
|
253
260
|
port: z.ZodNumber;
|
|
254
261
|
openOnLaunch: z.ZodBoolean;
|
|
262
|
+
remote: z.ZodObject<{
|
|
263
|
+
windowsSignIn: z.ZodBoolean;
|
|
264
|
+
users: z.ZodArray<z.ZodObject<{
|
|
265
|
+
name: z.ZodString;
|
|
266
|
+
role: z.ZodEnum<{
|
|
267
|
+
view: "view";
|
|
268
|
+
control: "control";
|
|
269
|
+
}>;
|
|
270
|
+
}, z.core.$strict>>;
|
|
271
|
+
rememberDays: z.ZodNumber;
|
|
272
|
+
lan: z.ZodObject<{
|
|
273
|
+
enabled: z.ZodBoolean;
|
|
274
|
+
port: z.ZodNumber;
|
|
275
|
+
address: z.ZodString;
|
|
276
|
+
}, z.core.$strict>;
|
|
277
|
+
}, z.core.$strict>;
|
|
255
278
|
}, z.core.$strict>;
|
|
256
279
|
agentCommands: z.ZodObject<{
|
|
257
280
|
enabled: z.ZodBoolean;
|
|
@@ -366,9 +389,6 @@ export declare const LocalOverridesSchema: z.ZodObject<{
|
|
|
366
389
|
scripts: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
367
390
|
sandbox: z.ZodOptional<z.ZodObject<{
|
|
368
391
|
memoryMb: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<"auto">, z.ZodNumber]>>;
|
|
369
|
-
maxMemoryMb: z.ZodOptional<z.ZodNumber>;
|
|
370
|
-
maxMemoryPercent: z.ZodOptional<z.ZodNumber>;
|
|
371
|
-
reserveHostMb: z.ZodOptional<z.ZodNumber>;
|
|
372
392
|
vGpu: z.ZodOptional<z.ZodBoolean>;
|
|
373
393
|
networking: z.ZodOptional<z.ZodBoolean>;
|
|
374
394
|
clipboard: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -8,12 +8,45 @@ import { HarnessSchema } from './harness.js';
|
|
|
8
8
|
import { AgentCommandsSchema, AttachSchema, ClaudeProfileSchema, SecretsSchema } from './profile.js';
|
|
9
9
|
import { AuditSchema, DownloadsSchema, SharingSchema, GitSchema, GuestWorkspaceSchema, MappingSchema, NetworkSchema, ProvisioningSchema, SandboxSchema, WorkspaceFilterSchema, } from './sandbox.js';
|
|
10
10
|
import { ToolchainsSchema } from './toolchains.js';
|
|
11
|
-
import { AppsSchema } from './apps.js';
|
|
11
|
+
import { AppsSchema, ProjectAppsSchema } from './apps.js';
|
|
12
12
|
import { ProjectsSchema } from './projects.js';
|
|
13
13
|
import { ResumeSchema } from './resume.js';
|
|
14
14
|
import { WebSchema } from './web.js';
|
|
15
15
|
import { VpnSchema } from './vpn.js';
|
|
16
16
|
import { GithubSchema } from './github.js';
|
|
17
|
+
const RESERVED_ENV_PREFIX = /^(AGENT_|GIT_|GH_|GITHUB_|NODE_|NPM_CONFIG_(USERCONFIG|GLOBALCONFIG|PREFIX|SCRIPT_SHELL)|SSL_|CLAUDE|ANTHROPIC_|OPENAI_|CODEX_|GEMINI_|GOOGLE_|COPILOT_|AWS_|AZURE_)/i;
|
|
18
|
+
const RESERVED_ENV_SUFFIX = /(_PROXY|_BASE_URL|_ENDPOINT|_CA_BUNDLE|_CA_CERTS)$/i;
|
|
19
|
+
const RESERVED_ENV_NAMES = new Set([
|
|
20
|
+
'PATH',
|
|
21
|
+
'PATHEXT',
|
|
22
|
+
'BASH_ENV',
|
|
23
|
+
'ENV',
|
|
24
|
+
'COMSPEC',
|
|
25
|
+
'PSMODULEPATH',
|
|
26
|
+
'PYTHONPATH',
|
|
27
|
+
'PYTHONSTARTUP',
|
|
28
|
+
'PERL5OPT',
|
|
29
|
+
'RUBYOPT',
|
|
30
|
+
'JAVA_TOOL_OPTIONS',
|
|
31
|
+
'LD_PRELOAD',
|
|
32
|
+
'SYSTEMROOT',
|
|
33
|
+
'WINDIR',
|
|
34
|
+
'HOME',
|
|
35
|
+
'USERPROFILE',
|
|
36
|
+
'APPDATA',
|
|
37
|
+
'LOCALAPPDATA',
|
|
38
|
+
'PROGRAMDATA',
|
|
39
|
+
'TEMP',
|
|
40
|
+
'TMP',
|
|
41
|
+
].map((n) => n.toUpperCase()));
|
|
42
|
+
/**
|
|
43
|
+
* An environment variable a repo's .sandbox-config.json may not set: the git guard's and the sandbox's own (setting
|
|
44
|
+
* AGENT_GIT_ACCESS or GIT_CONFIG_COUNT would undo provisioning.git.access), interpreter hooks (NODE_OPTIONS reaches the
|
|
45
|
+
* git guard's node), proxies and API endpoints (ANTHROPIC_BASE_URL would send your API key to the repo's server).
|
|
46
|
+
*/
|
|
47
|
+
export function reservedEnvName(name) {
|
|
48
|
+
return RESERVED_ENV_PREFIX.test(name) || RESERVED_ENV_SUFFIX.test(name) || RESERVED_ENV_NAMES.has(name.toUpperCase());
|
|
49
|
+
}
|
|
17
50
|
export const ConfigSchema = z
|
|
18
51
|
.strictObject({
|
|
19
52
|
$schema: SchemaRefSchema.optional(),
|
|
@@ -88,10 +121,14 @@ export const LocalOverridesSchema = z
|
|
|
88
121
|
.array(MappingSchema)
|
|
89
122
|
.describe('Extra folders (relative paths are relative to this file), e.g. a read-only data folder at another place in the sandbox. Folders outside the project are used only as your projects.allowOutsideMappings setting allows (by default you are asked once).'),
|
|
90
123
|
scripts: StringsSchema.describe('Scripts (.ps1 / .mjs / .cmd, relative to this file) run inside the sandbox before the agent.'),
|
|
91
|
-
// This PC's resources are yours to share: CPU
|
|
124
|
+
// This PC's resources are yours to share: CPU, paging file (this PC's disk) and the RAM kept for this PC are not a
|
|
125
|
+
// project's to set.
|
|
92
126
|
sandbox: SandboxSchema.omit({
|
|
93
127
|
cpuLimit: true,
|
|
94
128
|
pageFileMb: true,
|
|
129
|
+
reserveHostMb: true,
|
|
130
|
+
maxMemoryPercent: true,
|
|
131
|
+
maxMemoryMb: true,
|
|
95
132
|
engine: true,
|
|
96
133
|
agentMemoryHeadroomMb: true,
|
|
97
134
|
pressureResponse: true,
|
|
@@ -108,15 +145,15 @@ export const LocalOverridesSchema = z
|
|
|
108
145
|
toolchains: z
|
|
109
146
|
.strictObject({
|
|
110
147
|
..._.mapValues(_.omit(ToolchainsSchema.shape, 'environment'), (shape) => shape.partial()),
|
|
111
|
-
environment: ToolchainsSchema.shape.environment,
|
|
148
|
+
environment: ToolchainsSchema.shape.environment.refine((env) => !Object.keys(env).some(reservedEnvName), 'names the sandbox sets or that carry logins (AGENT_*, GIT_*, PATH, BASH_ENV, NODE_*, proxies, ANTHROPIC_*, CLAUDE_* ...) cannot be set by a project'),
|
|
112
149
|
})
|
|
113
150
|
.partial(),
|
|
114
151
|
databases: z.strictObject(_.mapValues(DatabasesSchema.shape, (shape) => shape.partial())).partial(),
|
|
115
152
|
attach: AttachSchema.partial(),
|
|
116
153
|
personalization: toggle('Copy your desktop look into the sandbox.').partial(),
|
|
117
154
|
// Catalogue apps only (a vetted list): your own programs (apps.custom) download anything, so a repo cannot add
|
|
118
|
-
// them.
|
|
119
|
-
apps:
|
|
155
|
+
// them, nor agent add-ons that are yours only (rtk hooks every agent command, Headroom sits in its API path).
|
|
156
|
+
apps: ProjectAppsSchema.partial(),
|
|
120
157
|
claudeProfile: ClaudeProfileSchema.pick({
|
|
121
158
|
enabled: true,
|
|
122
159
|
overwrite: true,
|
|
@@ -108,8 +108,13 @@ export const NetworkSchema = z.strictObject({
|
|
|
108
108
|
export const AuditSchema = z.strictObject({
|
|
109
109
|
enabled: z
|
|
110
110
|
.boolean()
|
|
111
|
-
.describe('Keep an audit log on this PC of what
|
|
112
|
-
days: z
|
|
111
|
+
.describe('Keep an audit log on this PC of what your agents did: programs started (with their command lines, secrets hidden), files changed in the projects, git commits, sessions, and what the repo guard caught, each linked to the agent session that did it where that can be known. The sandbox cannot change it. Read it with sandbox audit or on the Activity page.'),
|
|
112
|
+
days: z
|
|
113
|
+
.number()
|
|
114
|
+
.int()
|
|
115
|
+
.min(1)
|
|
116
|
+
.max(365)
|
|
117
|
+
.describe('Days the audit log is kept (1 to 30; a larger number keeps 30); older days are deleted when a sandbox starts and once a day.'),
|
|
113
118
|
maxMb: z
|
|
114
119
|
.number()
|
|
115
120
|
.int()
|
|
@@ -119,7 +124,10 @@ export const AuditSchema = z.strictObject({
|
|
|
119
124
|
processes: z
|
|
120
125
|
.boolean()
|
|
121
126
|
.describe('Record every program started in the sandbox with its command line (Windows process auditing; repeated runs are counted, not listed).'),
|
|
122
|
-
files: z.boolean().describe('Record the files changed in the
|
|
127
|
+
files: z.boolean().describe('Record the files changed in the projects while the sandbox runs.'),
|
|
128
|
+
commits: z
|
|
129
|
+
.boolean()
|
|
130
|
+
.describe("Record the git commits made in the projects while the sandbox runs (read from each project's .git folder on this PC)."),
|
|
123
131
|
});
|
|
124
132
|
export const WorkspaceFilterSchema = z.strictObject({
|
|
125
133
|
enabled: z
|
|
@@ -1,7 +1,58 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
export declare const WEB_DEFAULT_PORT = 47800;
|
|
3
|
+
export declare const WEB_LAN_DEFAULT_PORT = 47801;
|
|
4
|
+
export declare const REMOTE_ROLES: readonly ["view", "control"];
|
|
5
|
+
export type RemoteRole = (typeof REMOTE_ROLES)[number];
|
|
6
|
+
/** A Windows account name: "bob", "PC\bob" or "DOMAIN\bob" (no paths, quotes or wildcards). */
|
|
7
|
+
export declare const WindowsUserNameSchema: z.ZodString;
|
|
8
|
+
export declare const RemoteUserSchema: z.ZodObject<{
|
|
9
|
+
name: z.ZodString;
|
|
10
|
+
role: z.ZodEnum<{
|
|
11
|
+
view: "view";
|
|
12
|
+
control: "control";
|
|
13
|
+
}>;
|
|
14
|
+
}, z.core.$strict>;
|
|
15
|
+
export type RemoteUser = z.output<typeof RemoteUserSchema>;
|
|
16
|
+
export declare const WebLanSchema: z.ZodObject<{
|
|
17
|
+
enabled: z.ZodBoolean;
|
|
18
|
+
port: z.ZodNumber;
|
|
19
|
+
address: z.ZodString;
|
|
20
|
+
}, z.core.$strict>;
|
|
21
|
+
export declare const WebRemoteSchema: z.ZodObject<{
|
|
22
|
+
windowsSignIn: z.ZodBoolean;
|
|
23
|
+
users: z.ZodArray<z.ZodObject<{
|
|
24
|
+
name: z.ZodString;
|
|
25
|
+
role: z.ZodEnum<{
|
|
26
|
+
view: "view";
|
|
27
|
+
control: "control";
|
|
28
|
+
}>;
|
|
29
|
+
}, z.core.$strict>>;
|
|
30
|
+
rememberDays: z.ZodNumber;
|
|
31
|
+
lan: z.ZodObject<{
|
|
32
|
+
enabled: z.ZodBoolean;
|
|
33
|
+
port: z.ZodNumber;
|
|
34
|
+
address: z.ZodString;
|
|
35
|
+
}, z.core.$strict>;
|
|
36
|
+
}, z.core.$strict>;
|
|
37
|
+
export type WebRemote = z.output<typeof WebRemoteSchema>;
|
|
3
38
|
export declare const WebSchema: z.ZodObject<{
|
|
4
39
|
enabled: z.ZodBoolean;
|
|
5
40
|
port: z.ZodNumber;
|
|
6
41
|
openOnLaunch: z.ZodBoolean;
|
|
42
|
+
remote: z.ZodObject<{
|
|
43
|
+
windowsSignIn: z.ZodBoolean;
|
|
44
|
+
users: z.ZodArray<z.ZodObject<{
|
|
45
|
+
name: z.ZodString;
|
|
46
|
+
role: z.ZodEnum<{
|
|
47
|
+
view: "view";
|
|
48
|
+
control: "control";
|
|
49
|
+
}>;
|
|
50
|
+
}, z.core.$strict>>;
|
|
51
|
+
rememberDays: z.ZodNumber;
|
|
52
|
+
lan: z.ZodObject<{
|
|
53
|
+
enabled: z.ZodBoolean;
|
|
54
|
+
port: z.ZodNumber;
|
|
55
|
+
address: z.ZodString;
|
|
56
|
+
}, z.core.$strict>;
|
|
57
|
+
}, z.core.$strict>;
|
|
7
58
|
}, z.core.$strict>;
|