windows-development-sandbox 1.1.0 → 1.4.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 +3 -2
- package/config.example.json +38 -5
- package/dist/cli/args.d.ts +3 -0
- package/dist/cli/args.js +3 -1
- package/dist/cli/commands/accounts.js +2 -2
- package/dist/cli/commands/agent-commands.js +1 -1
- package/dist/cli/commands/context.js +2 -1
- package/dist/cli/commands/data/clean.js +6 -1
- package/dist/cli/commands/data/collect.js +3 -3
- package/dist/cli/commands/index.d.ts +1 -1
- package/dist/cli/commands/index.js +1 -1
- package/dist/cli/commands/launch.js +36 -3
- package/dist/cli/commands/maintenance.d.ts +2 -0
- package/dist/cli/commands/maintenance.js +35 -2
- package/dist/cli/commands/sessions.js +3 -3
- package/dist/cli/commands/user-config.js +1 -1
- package/dist/cli/index.js +6 -4
- package/dist/cli/manifest.js +5 -0
- package/dist/cli/menu/apps.d.ts +21 -0
- package/dist/cli/menu/apps.js +122 -0
- package/dist/cli/menu/command-form.d.ts +28 -0
- package/dist/cli/menu/command-form.js +219 -0
- package/dist/cli/menu/index.d.ts +26 -0
- package/dist/cli/menu/index.js +180 -0
- package/dist/cli/menu/settings/categories.d.ts +9 -0
- package/dist/cli/menu/settings/categories.js +28 -0
- package/dist/cli/menu/settings/config-file.d.ts +40 -0
- package/dist/cli/menu/settings/config-file.js +71 -0
- package/dist/cli/menu/settings/editors.d.ts +30 -0
- package/dist/cli/menu/settings/editors.js +305 -0
- package/dist/cli/menu/settings/index.d.ts +32 -0
- package/dist/cli/menu/settings/index.js +136 -0
- package/dist/cli/menu/settings/schema-node.d.ts +62 -0
- package/dist/cli/menu/settings/schema-node.js +104 -0
- package/dist/cli/{menu-multi.d.ts → menu/several.d.ts} +10 -2
- package/dist/cli/menu/several.js +102 -0
- package/dist/cli/tui/app.d.ts +39 -0
- package/dist/cli/tui/app.js +52 -0
- package/dist/cli/tui/dialogs.d.ts +57 -0
- package/dist/cli/tui/dialogs.js +162 -0
- package/dist/cli/tui/frame.d.ts +18 -0
- package/dist/cli/tui/frame.js +27 -0
- package/dist/cli/tui/index.d.ts +7 -0
- package/dist/cli/tui/index.js +8 -0
- package/dist/cli/tui/list-screen.d.ts +101 -0
- package/dist/cli/tui/list-screen.js +299 -0
- package/dist/cli/tui/terminal.d.ts +37 -0
- package/dist/cli/tui/terminal.js +89 -0
- package/dist/cli/tui/text.d.ts +20 -0
- package/dist/cli/tui/text.js +105 -0
- package/dist/cli/tui/theme.d.ts +45 -0
- package/dist/cli/tui/theme.js +30 -0
- package/dist/core/config.js +9 -6
- package/dist/core/local-config.d.ts +15 -0
- package/dist/core/local-config.js +63 -4
- package/dist/core/schema/apps.d.ts +40 -0
- package/dist/core/schema/apps.js +27 -0
- package/dist/core/schema/config.d.ts +66 -1
- package/dist/core/schema/config.js +18 -6
- package/dist/core/schema/profile.d.ts +5 -0
- package/dist/core/schema/profile.js +4 -2
- package/dist/core/schema/sandbox.d.ts +28 -0
- package/dist/core/schema/sandbox.js +28 -0
- package/dist/core/schema/toolchains.d.ts +3 -0
- package/dist/core/schema/toolchains.js +6 -1
- package/dist/core/types.d.ts +6 -0
- package/dist/core/util.d.ts +2 -0
- package/dist/core/util.js +2 -0
- package/dist/core/workspace.js +22 -5
- package/dist/guest/apps/index.d.ts +10 -0
- package/dist/guest/apps/index.js +206 -0
- package/dist/guest/apps/resolvers.d.ts +8 -0
- package/dist/guest/apps/resolvers.js +90 -0
- package/dist/guest/audit-parse.d.ts +7 -0
- package/dist/guest/audit-parse.js +24 -0
- package/dist/guest/audit.d.ts +4 -0
- package/dist/guest/audit.js +47 -0
- package/dist/guest/daemon/relays.d.ts +1 -0
- package/dist/guest/daemon/relays.js +18 -0
- package/dist/guest/daemon.js +4 -0
- package/dist/guest/env.d.ts +1 -0
- package/dist/guest/env.js +3 -1
- package/dist/guest/helpers.d.ts +1 -1
- package/dist/guest/main.js +15 -2
- package/dist/guest/netproxy.d.ts +1 -0
- package/dist/guest/netproxy.js +100 -0
- package/dist/guest/ptyhost.js +14 -4
- package/dist/guest/setup/claude.d.ts +0 -4
- package/dist/guest/setup/claude.js +48 -2
- package/dist/guest/setup/network.d.ts +9 -0
- package/dist/guest/setup/network.js +97 -4
- package/dist/guest/setup/user.js +4 -0
- package/dist/guest/toolchains/native/mesa.js +13 -3
- package/dist/guest/toolchains/plan.d.ts +3 -1
- package/dist/guest/toolchains/plan.js +53 -9
- package/dist/guest/toolchains/python.js +26 -2
- package/dist/guest/toolchains/vscode-profile.d.ts +30 -0
- package/dist/guest/toolchains/vscode-profile.js +69 -0
- package/dist/guest/toolchains/vscode.d.ts +17 -0
- package/dist/guest/toolchains/vscode.js +87 -1
- package/dist/host/attach.js +2 -1
- package/dist/host/audit/collect.d.ts +34 -0
- package/dist/host/audit/collect.js +126 -0
- package/dist/host/audit/log.d.ts +46 -0
- package/dist/host/audit/log.js +143 -0
- package/dist/host/generate.d.ts +6 -0
- package/dist/host/generate.js +10 -0
- package/dist/host/launch/mappings.js +5 -1
- package/dist/host/launch/stage.js +1 -1
- package/dist/host/motw.d.ts +17 -0
- package/dist/host/motw.js +74 -0
- package/dist/host/platform.js +3 -0
- package/dist/host/port-forward.d.ts +8 -0
- package/dist/host/port-forward.js +37 -0
- package/dist/host/repo-guard.d.ts +10 -3
- package/dist/host/repo-guard.js +142 -14
- package/dist/host/stops.d.ts +67 -0
- package/dist/host/stops.js +126 -0
- package/dist/host/terminal.d.ts +8 -0
- package/dist/host/terminal.js +45 -1
- package/dist/host/workspace-view/index.d.ts +20 -0
- package/dist/host/workspace-view/index.js +53 -0
- package/dist/host/workspace-view/rules.d.ts +26 -0
- package/dist/host/workspace-view/rules.js +93 -0
- package/dist/host/workspace-view/view.d.ts +50 -0
- package/dist/host/workspace-view/view.js +281 -0
- package/dist/runtime/daemon.js +1319 -552
- package/dist/runtime/db.js +168 -13
- package/dist/runtime/main.js +2898 -608
- package/dist/runtime/netproxy.js +25936 -0
- package/dist/runtime/view.js +272 -23
- package/dist/selftest/audit.test.d.ts +2 -0
- package/dist/selftest/audit.test.js +57 -0
- package/dist/selftest/channel.test.js +7 -0
- package/dist/selftest/cli.test.js +47 -17
- package/dist/selftest/fixtures.d.ts +45 -0
- package/dist/selftest/fixtures.js +3 -1
- package/dist/selftest/generate.test.js +7 -2
- package/dist/selftest/index.js +5 -2
- package/dist/selftest/live-network.test.d.ts +2 -0
- package/dist/selftest/live-network.test.js +63 -0
- package/dist/selftest/local-config.test.js +16 -3
- package/dist/selftest/network.test.js +12 -0
- package/dist/selftest/projects.test.js +1 -1
- package/dist/selftest/repo-guard.test.js +59 -1
- package/dist/selftest/sync.test.js +50 -10
- package/dist/selftest/toolchains.test.js +30 -0
- package/dist/selftest/workspace-view.test.d.ts +2 -0
- package/dist/selftest/workspace-view.test.js +89 -0
- package/dist/shared/apps.d.ts +49 -0
- package/dist/shared/apps.js +114 -0
- package/dist/shared/netpolicy.d.ts +27 -0
- package/dist/shared/netpolicy.js +51 -0
- package/dist/shared/pty-client.d.ts +9 -4
- package/dist/shared/pty-client.js +139 -17
- package/dist/shared/rundir.d.ts +2 -0
- package/dist/shared/rundir.js +2 -0
- package/dist/shared/schema.d.ts +10 -0
- package/dist/shared/schema.js +2 -0
- package/dist/shared/system.d.ts +1 -0
- package/dist/shared/system.js +1 -0
- package/dist/sync/claude-mem.js +3 -2
- package/dist/sync/history.d.ts +6 -0
- package/dist/sync/history.js +26 -11
- package/dist/sync/watcher.js +78 -6
- package/package.json +2 -1
- package/sandbox/cargoshim.cs +56 -0
- package/sandbox/cpucap.cs +33 -0
- package/sandbox/cpucap.ps1 +4 -35
- package/sandbox/logon.cmd +6 -0
- package/sandbox/memlog.ps1 +34 -0
- package/sandbox/pagefile.cs +31 -0
- package/sandbox/pagefile.ps1 +21 -0
- package/sandbox/ptyhost.cs +105 -7
- package/schemas/config.schema.json +303 -2
- package/schemas/sandbox-config.schema.json +126 -12
- package/dist/cli/menu-multi.js +0 -82
- package/dist/cli/menu.d.ts +0 -14
- package/dist/cli/menu.js +0 -290
- package/dist/cli/settings/edit.d.ts +0 -13
- package/dist/cli/settings/edit.js +0 -19
- package/dist/cli/settings/index.d.ts +0 -8
- package/dist/cli/settings/index.js +0 -131
- package/dist/cli/settings/prompts.d.ts +0 -7
- package/dist/cli/settings/prompts.js +0 -48
- package/dist/cli/settings/schema.d.ts +0 -33
- package/dist/cli/settings/schema.js +0 -29
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export declare class Theme {
|
|
2
|
+
/** Colour is decided once per run: the menu only runs in a terminal. */
|
|
3
|
+
static readonly colour: boolean;
|
|
4
|
+
static readonly bold: (text: string) => string;
|
|
5
|
+
static readonly dim: (text: string) => string;
|
|
6
|
+
static readonly italic: (text: string) => string;
|
|
7
|
+
static readonly accent: (text: string) => string;
|
|
8
|
+
static readonly accentBold: (t: string) => string;
|
|
9
|
+
static readonly ok: (text: string) => string;
|
|
10
|
+
static readonly warn: (text: string) => string;
|
|
11
|
+
static readonly error: (text: string) => string;
|
|
12
|
+
static readonly muted: (text: string) => string;
|
|
13
|
+
static readonly changed: (text: string) => string;
|
|
14
|
+
/** The highlighted row: a soft background bar across the whole width. */
|
|
15
|
+
static readonly selected: (t: string) => string;
|
|
16
|
+
/** The header bar. */
|
|
17
|
+
static readonly bar: (t: string) => string;
|
|
18
|
+
/** The two settings scopes: your own (blue) and the project's (purple). */
|
|
19
|
+
static readonly scopeGlobal: (t: string) => string;
|
|
20
|
+
static readonly scopeProject: (t: string) => string;
|
|
21
|
+
static readonly project: (text: string) => string;
|
|
22
|
+
static readonly key: (t: string) => string;
|
|
23
|
+
static readonly glyph: {
|
|
24
|
+
pointer: string;
|
|
25
|
+
bullet: string;
|
|
26
|
+
on: string;
|
|
27
|
+
off: string;
|
|
28
|
+
checked: string;
|
|
29
|
+
unchecked: string;
|
|
30
|
+
dot: string;
|
|
31
|
+
arrow: string;
|
|
32
|
+
back: string;
|
|
33
|
+
more: string;
|
|
34
|
+
folder: string;
|
|
35
|
+
open: string;
|
|
36
|
+
line: string;
|
|
37
|
+
vline: string;
|
|
38
|
+
search: string;
|
|
39
|
+
changed: string;
|
|
40
|
+
lock: string;
|
|
41
|
+
ok: string;
|
|
42
|
+
cross: string;
|
|
43
|
+
star: string;
|
|
44
|
+
};
|
|
45
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// The look of every screen: colours (off with NO_COLOR, like the rest of the CLI) and the line-drawing characters.
|
|
2
|
+
import { colorEnabled } from '../colors.js';
|
|
3
|
+
const sgr = (open, close = '0') => (text) => (Theme.colour ? '\x1b[' + open + 'm' + text + '\x1b[' + close + 'm' : text);
|
|
4
|
+
export class Theme {
|
|
5
|
+
/** Colour is decided once per run: the menu only runs in a terminal. */
|
|
6
|
+
static colour = colorEnabled(process.env, Boolean(process.stdout.isTTY));
|
|
7
|
+
static bold = sgr('1', '22');
|
|
8
|
+
static dim = sgr('2', '22');
|
|
9
|
+
static italic = sgr('3', '23');
|
|
10
|
+
static accent = sgr('38;5;75', '39');
|
|
11
|
+
static accentBold = (t) => Theme.bold(Theme.accent(t));
|
|
12
|
+
static ok = sgr('38;5;78', '39');
|
|
13
|
+
static warn = sgr('38;5;214', '39');
|
|
14
|
+
static error = sgr('38;5;203', '39');
|
|
15
|
+
static muted = sgr('38;5;245', '39');
|
|
16
|
+
static changed = sgr('38;5;221', '39');
|
|
17
|
+
/** The highlighted row: a soft background bar across the whole width. */
|
|
18
|
+
static selected = (t) => (Theme.colour ? '\x1b[48;5;24m\x1b[97m' + t.replace(/\x1b\[0m/g, '\x1b[0m\x1b[48;5;24m\x1b[97m') + '\x1b[0m' : t);
|
|
19
|
+
/** The header bar. */
|
|
20
|
+
static bar = (t) => (Theme.colour ? '\x1b[48;5;236m\x1b[97m' + t.replace(/\x1b\[0m/g, '\x1b[0m\x1b[48;5;236m\x1b[97m') + '\x1b[0m' : t);
|
|
21
|
+
/** The two settings scopes: your own (blue) and the project's (purple). */
|
|
22
|
+
static scopeGlobal = (t) => (Theme.colour ? '\x1b[48;5;25m\x1b[97m\x1b[1m' + t + '\x1b[0m' : '[' + t + ']');
|
|
23
|
+
static scopeProject = (t) => (Theme.colour ? '\x1b[48;5;91m\x1b[97m\x1b[1m' + t + '\x1b[0m' : '[' + t + ']');
|
|
24
|
+
static project = sgr('38;5;177', '39');
|
|
25
|
+
static key = (t) => (Theme.colour ? '\x1b[48;5;238m\x1b[97m ' + t + ' \x1b[0m' : '[' + t + ']');
|
|
26
|
+
static glyph = {
|
|
27
|
+
pointer: '›', bullet: '•', on: '●', off: '○', checked: '◉', unchecked: '◯', dot: '·', arrow: '→', back: '←', more: '…',
|
|
28
|
+
folder: '▸', open: '▾', line: '─', vline: '│', search: '⌕', changed: '✎', lock: '⚿', ok: '✔', cross: '✘', star: '★',
|
|
29
|
+
};
|
|
30
|
+
}
|
package/dist/core/config.js
CHANGED
|
@@ -8,11 +8,13 @@ export const DEFAULTS = {
|
|
|
8
8
|
defaultHarness: 'claude',
|
|
9
9
|
guestWorkspace: 'auto',
|
|
10
10
|
readOnly: false,
|
|
11
|
+
workspaceFilter: { enabled: false, exclude: [], include: [] },
|
|
12
|
+
audit: { enabled: false, days: 14, maxMb: 100, processes: true, files: true },
|
|
11
13
|
mappings: [],
|
|
12
|
-
sandbox: { memoryMb: 'auto', maxMemoryMb: 32768, maxMemoryPercent: 80, cpuLimit: 80, reserveHostMb: 8192, vGpu: false, networking: true, clipboard: true, audioInput: false, videoInput: false, printerRedirection: false, protectedClient: true, headless: false },
|
|
13
|
-
storage: { root: '%LOCALAPPDATA%\\AgentSandbox', persistAgentState: true, dataName: 'default' },
|
|
14
|
-
network: { blockPrivateRanges: true },
|
|
15
|
-
provisioning: { copyFromHost: true, fallbackToInstall: true, refresh: false, git: { enabled: true, access: 'full' }, terminal: { enabled: true } },
|
|
14
|
+
sandbox: { memoryMb: 'auto', maxMemoryMb: 32768, maxMemoryPercent: 80, cpuLimit: 80, pageFileMb: 16384, reserveHostMb: 8192, vGpu: false, networking: true, clipboard: true, audioInput: false, videoInput: false, printerRedirection: false, protectedClient: true, headless: false, keepAwake: true },
|
|
15
|
+
storage: { root: '%LOCALAPPDATA%\\AgentSandbox', persistAgentState: true, dataName: 'default', markSandboxFiles: true, warnDataGb: 50 },
|
|
16
|
+
network: { blockPrivateRanges: true, allowOnly: [], blockHosts: [], forwardPorts: [], dns: [], proxy: '' },
|
|
17
|
+
provisioning: { copyFromHost: true, fallbackToInstall: true, refresh: false, git: { enabled: true, access: 'full' }, terminal: { enabled: true }, conpty: { enabled: true } },
|
|
16
18
|
downloads: { requireChecksum: false },
|
|
17
19
|
toolchains: {
|
|
18
20
|
node: { version: 'host', extraVersions: [], corepack: true, volta: 'auto', globalPackages: [] },
|
|
@@ -33,7 +35,7 @@ export const DEFAULTS = {
|
|
|
33
35
|
uv: { enabled: true },
|
|
34
36
|
vcRuntime: { enabled: false },
|
|
35
37
|
mesa: { enabled: false, vulkan: false },
|
|
36
|
-
vscode: { enabled: true },
|
|
38
|
+
vscode: { enabled: true, setup: true, recommended: true, extensions: [] },
|
|
37
39
|
environment: {},
|
|
38
40
|
},
|
|
39
41
|
databases: {
|
|
@@ -48,7 +50,8 @@ export const DEFAULTS = {
|
|
|
48
50
|
agentCommands: { enabled: false, agents: 'auto' },
|
|
49
51
|
secrets: { strip: true, allow: [], patterns: [], warnFiles: true },
|
|
50
52
|
personalization: { enabled: true },
|
|
51
|
-
|
|
53
|
+
apps: { install: [], custom: {} },
|
|
54
|
+
claudeProfile: { enabled: true, hostDir: 'auto', include: ['settings.json', 'CLAUDE.md', 'commands', 'agents', 'skills', 'plugins'], globalStateFile: 'auto', copyCredentials: false, overwrite: true, syncHistory: true, historyScope: 'project', syncClaudeMem: true, syncClaudeMemBack: false },
|
|
52
55
|
harnesses: {
|
|
53
56
|
claude: { hostExecutable: 'claude.exe', hostDir: 'auto', npmPackage: '@anthropic-ai/claude-code@latest', npmBin: 'claude', arguments: [], environment: { DISABLE_AUTOUPDATER: '1' }, resumeArguments: ['--continue'] },
|
|
54
57
|
codex: { npmPackage: '@openai/codex@latest', npmBin: 'codex', arguments: [], environment: {}, resumeArguments: ['resume', '--last'] },
|
|
@@ -10,6 +10,12 @@ export declare const ON_IS_SAFER: string[];
|
|
|
10
10
|
export declare const OFF_IS_SAFER: string[];
|
|
11
11
|
export declare const GIT_STRICTNESS: Record<string, number>;
|
|
12
12
|
export declare const NEVER_LOOSER: string[];
|
|
13
|
+
/** What a project file asks for that would loosen one of your protections (so it is not used). */
|
|
14
|
+
export declare const loosenedRequests: (config: Config, ov: Overrides) => {
|
|
15
|
+
key: string;
|
|
16
|
+
wanted: unknown;
|
|
17
|
+
yours: unknown;
|
|
18
|
+
}[];
|
|
13
19
|
/** Applies validated overrides. Mappings with the same guest folder replace the configured ones. */
|
|
14
20
|
export declare const applyOverrides: (config: Config, ov: Overrides) => Config;
|
|
15
21
|
/** Human-readable list of what a local config changes, with risky items flagged. */
|
|
@@ -18,3 +24,12 @@ export declare const readLocalConfig: (dir: string) => LocalConfigFile | null;
|
|
|
18
24
|
export declare const trustStorePath: (storageRoot: string) => string;
|
|
19
25
|
export declare const isTrusted: (store: string, file: string, hash: string) => boolean;
|
|
20
26
|
export declare const saveTrust: (store: string, file: string, hash: string) => void;
|
|
27
|
+
export declare const allowStorePath: (storageRoot: string) => string;
|
|
28
|
+
/** true = you allowed it, false = you said no (not asked again), null = not asked yet. */
|
|
29
|
+
export declare const allowedAnswer: (store: string, file: string, setting: string, value: unknown) => boolean | null;
|
|
30
|
+
export declare const saveAllowed: (store: string, file: string, setting: string, value: unknown, allow: boolean) => void;
|
|
31
|
+
/** Your config with these requests let through, so applyOverrides' "strictest wins" keeps the project's value. */
|
|
32
|
+
export declare const allowRequests: (config: Config, allowed: {
|
|
33
|
+
key: string;
|
|
34
|
+
wanted: unknown;
|
|
35
|
+
}[]) => Config;
|
|
@@ -4,9 +4,10 @@ import fs from 'node:fs';
|
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
import crypto from 'node:crypto';
|
|
6
6
|
import _ from 'lodash';
|
|
7
|
+
import { z } from 'zod';
|
|
7
8
|
import { mergeConfig } from './config.js';
|
|
8
9
|
import { describeIssues, LocalOverrides } from './schema.js';
|
|
9
|
-
import { expandPath, fail, fileExists, parseJsonc, tryReadJson, writeJson } from './util.js';
|
|
10
|
+
import { expandPath, fail, fileExists, isInsideOrEqual, parseJsonc, tryReadJson, writeJson } from './util.js';
|
|
10
11
|
export const LOCAL_CONFIG_NAME = '.sandbox-config.json';
|
|
11
12
|
/** Validates a folder's settings and resolves its relative paths against that folder. */
|
|
12
13
|
export const normalizeOverrides = (data, dir, source) => {
|
|
@@ -18,14 +19,33 @@ export const normalizeOverrides = (data, dir, source) => {
|
|
|
18
19
|
const missing = scripts?.filter((s) => !fileExists(s)) ?? [];
|
|
19
20
|
if (missing.length)
|
|
20
21
|
fail(source + ' is not valid:\n - scripts: file not found: ' + missing.join(', '));
|
|
21
|
-
|
|
22
|
+
const mappings = overrides.mappings?.map((m) => ({ ...m, host: expandPath(m.host, dir) }));
|
|
23
|
+
// A repo ships this file, so it must not reach the rest of this PC: its extra folders stay inside the project.
|
|
24
|
+
const outside = mappings?.filter((m) => !insideProject(dir, m.host)) ?? [];
|
|
25
|
+
if (outside.length)
|
|
26
|
+
fail(source + ' is not valid:\n - mappings: a project file may only map folders inside the project, not ' + outside.map((m) => m.host).join(', '), 'Map other folders in your own settings (sandbox config path shows where they are).');
|
|
27
|
+
return { ...overrides, scripts, mappings };
|
|
28
|
+
};
|
|
29
|
+
/** `folder` (also where it really is, through links) is inside the project folder `dir`. */
|
|
30
|
+
const insideProject = (dir, folder) => {
|
|
31
|
+
// Where a path really is: the nearest existing folder resolved (links, 8.3 short names), plus the part not created yet.
|
|
32
|
+
const real = (p) => {
|
|
33
|
+
const full = path.resolve(p);
|
|
34
|
+
try {
|
|
35
|
+
return fs.realpathSync.native(full);
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return path.dirname(full) === full ? full : path.join(real(path.dirname(full)), path.basename(full));
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
return isInsideOrEqual(dir, folder) && isInsideOrEqual(real(dir), real(folder));
|
|
22
42
|
};
|
|
23
43
|
/**
|
|
24
44
|
* Protections a project file may only make stricter, never looser. A repo can ship the file, so it must not be able to
|
|
25
45
|
* switch off what you turned on (or turn on what you turned off). The strictest of the two values always wins.
|
|
26
46
|
*/
|
|
27
|
-
export const ON_IS_SAFER = ['readOnly', 'network.blockPrivateRanges', 'sandbox.protectedClient', 'downloads.requireChecksum'];
|
|
28
|
-
export const OFF_IS_SAFER = ['sandbox.networking', 'sandbox.clipboard', 'sandbox.audioInput', 'sandbox.videoInput', 'sandbox.printerRedirection', 'sandbox.vGpu', 'claudeProfile.copyCredentials'];
|
|
47
|
+
export const ON_IS_SAFER = ['readOnly', 'workspaceFilter.enabled', 'network.blockPrivateRanges', 'sandbox.protectedClient', 'downloads.requireChecksum'];
|
|
48
|
+
export const OFF_IS_SAFER = ['sandbox.networking', 'sandbox.clipboard', 'sandbox.audioInput', 'sandbox.videoInput', 'sandbox.printerRedirection', 'sandbox.vGpu', 'claudeProfile.copyCredentials', 'claudeProfile.enabled', 'claudeProfile.syncHistory', 'claudeProfile.syncClaudeMem'];
|
|
29
49
|
export const GIT_STRICTNESS = { full: 0, readonly: 1, none: 2 };
|
|
30
50
|
export const NEVER_LOOSER = [...ON_IS_SAFER, ...OFF_IS_SAFER, 'provisioning.git.access'];
|
|
31
51
|
const keepStrictest = (yours, merged) => {
|
|
@@ -34,6 +54,12 @@ const keepStrictest = (yours, merged) => {
|
|
|
34
54
|
if (GIT_STRICTNESS[yours.provisioning.git.access] > GIT_STRICTNESS[merged.provisioning.git.access])
|
|
35
55
|
merged.provisioning.git.access = yours.provisioning.git.access;
|
|
36
56
|
};
|
|
57
|
+
/** What a project file asks for that would loosen one of your protections (so it is not used). */
|
|
58
|
+
export const loosenedRequests = (config, ov) => [
|
|
59
|
+
...ON_IS_SAFER.filter((p) => _.has(ov, p) && !_.get(ov, p) && _.get(config, p)),
|
|
60
|
+
...OFF_IS_SAFER.filter((p) => _.has(ov, p) && _.get(ov, p) && !_.get(config, p)),
|
|
61
|
+
...(_.has(ov, 'provisioning.git.access') && GIT_STRICTNESS[_.get(ov, 'provisioning.git.access')] < GIT_STRICTNESS[config.provisioning.git.access] ? ['provisioning.git.access'] : []),
|
|
62
|
+
].map((key) => ({ key, wanted: _.get(ov, key), yours: _.get(config, key) }));
|
|
37
63
|
/** Applies validated overrides. Mappings with the same guest folder replace the configured ones. */
|
|
38
64
|
export const applyOverrides = (config, ov) => {
|
|
39
65
|
const { harness, mappings, scripts: _scripts, $schema: _schema, ...rest } = ov;
|
|
@@ -41,6 +67,14 @@ export const applyOverrides = (config, ov) => {
|
|
|
41
67
|
keepStrictest(config, next);
|
|
42
68
|
if (harness)
|
|
43
69
|
next.defaultHarness = harness;
|
|
70
|
+
// A project adds catalogue apps to yours; it never removes the ones you chose.
|
|
71
|
+
if (rest.apps?.install)
|
|
72
|
+
next.apps.install = _.union(config.apps.install, rest.apps.install);
|
|
73
|
+
// Its patterns keep more out on top of yours; yours stay.
|
|
74
|
+
if (rest.network?.blockHosts)
|
|
75
|
+
next.network.blockHosts = _.union(config.network.blockHosts, rest.network.blockHosts);
|
|
76
|
+
if (rest.workspaceFilter?.exclude)
|
|
77
|
+
next.workspaceFilter.exclude = _.union(config.workspaceFilter.exclude, rest.workspaceFilter.exclude);
|
|
44
78
|
if (mappings)
|
|
45
79
|
next.mappings = [..._.reject(config.mappings, (m) => mappings.some((o) => o.guest.toLowerCase() === m.guest.toLowerCase())), ...mappings];
|
|
46
80
|
return next;
|
|
@@ -75,3 +109,28 @@ const loadTrust = (store) => tryReadJson(store) ?? {};
|
|
|
75
109
|
const key = (file) => path.resolve(file).toLowerCase();
|
|
76
110
|
export const isTrusted = (store, file, hash) => loadTrust(store)[key(file)] === hash;
|
|
77
111
|
export const saveTrust = (store, file, hash) => writeJson(store, { ...loadTrust(store), [key(file)]: hash });
|
|
112
|
+
// ---- looser settings you allowed for one project (host-only: <storage>\allowed-requests.json) ----------------------
|
|
113
|
+
// A project file can never loosen a protection by itself. When it asks, you are asked once (interactively); a "yes"
|
|
114
|
+
// is kept here, per project file and per "setting = value", so only that value is ever let through for that project.
|
|
115
|
+
export const allowStorePath = (storageRoot) => path.join(storageRoot, 'allowed-requests.json');
|
|
116
|
+
const loadAllowed = (store) => {
|
|
117
|
+
const parsed = z.record(z.string(), z.array(z.string().max(300)).max(50)).safeParse(tryReadJson(store) ?? {});
|
|
118
|
+
return parsed.success ? parsed.data : {};
|
|
119
|
+
};
|
|
120
|
+
const allowKey = (setting, value) => setting + ' = ' + JSON.stringify(value);
|
|
121
|
+
/** true = you allowed it, false = you said no (not asked again), null = not asked yet. */
|
|
122
|
+
export const allowedAnswer = (store, file, setting, value) => {
|
|
123
|
+
const answers = loadAllowed(store)[key(file)] ?? [];
|
|
124
|
+
return answers.includes(allowKey(setting, value)) ? true : answers.includes('no: ' + allowKey(setting, value)) ? false : null;
|
|
125
|
+
};
|
|
126
|
+
export const saveAllowed = (store, file, setting, value, allow) => {
|
|
127
|
+
const all = loadAllowed(store);
|
|
128
|
+
const entry = allowKey(setting, value);
|
|
129
|
+
writeJson(store, { ...all, [key(file)]: _.union(_.without(all[key(file)] ?? [], entry, 'no: ' + entry), [(allow ? '' : 'no: ') + entry]) });
|
|
130
|
+
};
|
|
131
|
+
/** Your config with these requests let through, so applyOverrides' "strictest wins" keeps the project's value. */
|
|
132
|
+
export const allowRequests = (config, allowed) => {
|
|
133
|
+
const next = _.cloneDeep(config);
|
|
134
|
+
allowed.forEach((a) => _.set(next, a.key, a.wanted));
|
|
135
|
+
return next;
|
|
136
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const AppId: z.ZodEnum<{
|
|
3
|
+
[x: string]: string;
|
|
4
|
+
}>;
|
|
5
|
+
export declare const CustomAppId: z.ZodString;
|
|
6
|
+
export declare const CustomApp: z.ZodObject<{
|
|
7
|
+
name: z.ZodString;
|
|
8
|
+
github: z.ZodOptional<z.ZodString>;
|
|
9
|
+
asset: z.ZodOptional<z.ZodString>;
|
|
10
|
+
url: z.ZodOptional<z.ZodString>;
|
|
11
|
+
sha256: z.ZodOptional<z.ZodString>;
|
|
12
|
+
unpack: z.ZodOptional<z.ZodEnum<{
|
|
13
|
+
file: "file";
|
|
14
|
+
archive: "archive";
|
|
15
|
+
nsis: "nsis";
|
|
16
|
+
msi: "msi";
|
|
17
|
+
}>>;
|
|
18
|
+
exe: z.ZodString;
|
|
19
|
+
cli: z.ZodOptional<z.ZodBoolean>;
|
|
20
|
+
}, z.core.$strict>;
|
|
21
|
+
export declare const Apps: z.ZodObject<{
|
|
22
|
+
install: z.ZodArray<z.ZodEnum<{
|
|
23
|
+
[x: string]: string;
|
|
24
|
+
}>>;
|
|
25
|
+
custom: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
26
|
+
name: z.ZodString;
|
|
27
|
+
github: z.ZodOptional<z.ZodString>;
|
|
28
|
+
asset: z.ZodOptional<z.ZodString>;
|
|
29
|
+
url: z.ZodOptional<z.ZodString>;
|
|
30
|
+
sha256: z.ZodOptional<z.ZodString>;
|
|
31
|
+
unpack: z.ZodOptional<z.ZodEnum<{
|
|
32
|
+
file: "file";
|
|
33
|
+
archive: "archive";
|
|
34
|
+
nsis: "nsis";
|
|
35
|
+
msi: "msi";
|
|
36
|
+
}>>;
|
|
37
|
+
exe: z.ZodString;
|
|
38
|
+
cli: z.ZodOptional<z.ZodBoolean>;
|
|
39
|
+
}, z.core.$strict>>;
|
|
40
|
+
}, z.core.$strict>;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// Apps installed in the sandbox: entries from the catalogue (shared/apps.ts) and programs of your own.
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { APPS } from '../../shared/apps.js';
|
|
4
|
+
const CATALOGUE_IDS = APPS.filter((a) => !a.setting).map((a) => a.id);
|
|
5
|
+
export const AppId = z.enum(CATALOGUE_IDS);
|
|
6
|
+
export const CustomAppId = z.string().regex(/^[a-z0-9][a-z0-9-]{0,40}$/, 'lower-case letters, digits and - only');
|
|
7
|
+
const compiles = (pattern) => { try {
|
|
8
|
+
new RegExp(pattern);
|
|
9
|
+
return true;
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return false;
|
|
13
|
+
} };
|
|
14
|
+
export const CustomApp = z.strictObject({
|
|
15
|
+
name: z.string().min(1).max(80).describe('The name on the desktop shortcut, e.g. "My Tool".'),
|
|
16
|
+
github: z.string().regex(/^[\w.-]+\/[\w.-]+$/, 'owner/repo, e.g. sharkdp/bat').optional().describe('GitHub repository (owner/repo): the file comes from its latest release.'),
|
|
17
|
+
asset: z.string().max(200).refine(compiles, 'not a valid pattern').optional().describe('With github: a pattern (regular expression) matching the release file, e.g. windows.*x64\\.zip$'),
|
|
18
|
+
url: z.string().url().startsWith('https://', 'https:// only').optional().describe('Or a direct https:// download link (zip, 7z, exe or msi).'),
|
|
19
|
+
sha256: z.string().regex(/^[0-9a-fA-F]{64}$/, '64 hex characters').optional().describe('SHA-256 of the download (GitHub releases usually publish one; then this is not needed).'),
|
|
20
|
+
unpack: z.enum(['archive', 'file', 'nsis', 'msi']).optional().describe('archive (zip, 7z, tar: the default), file (a single .exe), nsis (an installer 7-Zip can open) or msi.'),
|
|
21
|
+
exe: z.string().min(1).max(200).describe('The program to start (found anywhere in the download), e.g. mytool.exe.'),
|
|
22
|
+
cli: z.boolean().optional().describe('A command-line tool: its folder goes on PATH instead of getting a desktop shortcut.'),
|
|
23
|
+
}).refine((a) => (a.github && a.asset ? !a.url : Boolean(a.url) && !a.github && !a.asset), { message: 'give github + asset, or url (not both)' });
|
|
24
|
+
export const Apps = z.strictObject({
|
|
25
|
+
install: z.array(AppId).describe('Apps from the catalogue to install in every sandbox (the Apps screen in the menu lists them by category). Each is downloaded once and kept.'),
|
|
26
|
+
custom: z.record(CustomAppId, CustomApp).describe('Programs of your own: a GitHub release or a download link, and the program to start. Installed like the catalogue apps.'),
|
|
27
|
+
});
|
|
@@ -4,6 +4,18 @@ export declare const Config: z.ZodObject<{
|
|
|
4
4
|
defaultHarness: z.ZodString;
|
|
5
5
|
guestWorkspace: z.ZodUnion<readonly [z.ZodLiteral<"auto">, z.ZodLiteral<"mirror">, z.ZodString]>;
|
|
6
6
|
readOnly: z.ZodBoolean;
|
|
7
|
+
workspaceFilter: z.ZodObject<{
|
|
8
|
+
enabled: z.ZodBoolean;
|
|
9
|
+
exclude: z.ZodArray<z.ZodString>;
|
|
10
|
+
include: z.ZodArray<z.ZodString>;
|
|
11
|
+
}, z.core.$strict>;
|
|
12
|
+
audit: z.ZodObject<{
|
|
13
|
+
enabled: z.ZodBoolean;
|
|
14
|
+
days: z.ZodNumber;
|
|
15
|
+
maxMb: z.ZodNumber;
|
|
16
|
+
processes: z.ZodBoolean;
|
|
17
|
+
files: z.ZodBoolean;
|
|
18
|
+
}, z.core.$strict>;
|
|
7
19
|
mappings: z.ZodArray<z.ZodObject<{
|
|
8
20
|
host: z.ZodString;
|
|
9
21
|
guest: z.ZodString;
|
|
@@ -15,6 +27,7 @@ export declare const Config: z.ZodObject<{
|
|
|
15
27
|
maxMemoryMb: z.ZodNumber;
|
|
16
28
|
maxMemoryPercent: z.ZodNumber;
|
|
17
29
|
cpuLimit: z.ZodUnion<readonly [z.ZodLiteral<"none">, z.ZodNumber]>;
|
|
30
|
+
pageFileMb: z.ZodNumber;
|
|
18
31
|
reserveHostMb: z.ZodNumber;
|
|
19
32
|
vGpu: z.ZodBoolean;
|
|
20
33
|
networking: z.ZodBoolean;
|
|
@@ -24,14 +37,25 @@ export declare const Config: z.ZodObject<{
|
|
|
24
37
|
printerRedirection: z.ZodBoolean;
|
|
25
38
|
protectedClient: z.ZodBoolean;
|
|
26
39
|
headless: z.ZodBoolean;
|
|
40
|
+
keepAwake: z.ZodBoolean;
|
|
27
41
|
}, z.core.$strict>;
|
|
28
42
|
storage: z.ZodObject<{
|
|
29
43
|
root: z.ZodString;
|
|
30
44
|
persistAgentState: z.ZodBoolean;
|
|
31
45
|
dataName: z.ZodString;
|
|
46
|
+
markSandboxFiles: z.ZodBoolean;
|
|
47
|
+
warnDataGb: z.ZodNumber;
|
|
32
48
|
}, z.core.$strict>;
|
|
33
49
|
network: z.ZodObject<{
|
|
34
50
|
blockPrivateRanges: z.ZodBoolean;
|
|
51
|
+
allowOnly: z.ZodArray<z.ZodString>;
|
|
52
|
+
blockHosts: z.ZodArray<z.ZodString>;
|
|
53
|
+
forwardPorts: z.ZodArray<z.ZodObject<{
|
|
54
|
+
pc: z.ZodNumber;
|
|
55
|
+
sandbox: z.ZodNumber;
|
|
56
|
+
}, z.core.$strict>>;
|
|
57
|
+
dns: z.ZodArray<z.ZodString>;
|
|
58
|
+
proxy: z.ZodString;
|
|
35
59
|
}, z.core.$strict>;
|
|
36
60
|
provisioning: z.ZodObject<{
|
|
37
61
|
copyFromHost: z.ZodBoolean;
|
|
@@ -48,6 +72,9 @@ export declare const Config: z.ZodObject<{
|
|
|
48
72
|
terminal: z.ZodObject<{
|
|
49
73
|
enabled: z.ZodBoolean;
|
|
50
74
|
}, z.core.$strict>;
|
|
75
|
+
conpty: z.ZodObject<{
|
|
76
|
+
enabled: z.ZodBoolean;
|
|
77
|
+
}, z.core.$strict>;
|
|
51
78
|
}, z.core.$strict>;
|
|
52
79
|
downloads: z.ZodObject<{
|
|
53
80
|
requireChecksum: z.ZodBoolean;
|
|
@@ -141,6 +168,9 @@ export declare const Config: z.ZodObject<{
|
|
|
141
168
|
}, z.core.$strict>;
|
|
142
169
|
vscode: z.ZodObject<{
|
|
143
170
|
enabled: z.ZodBoolean;
|
|
171
|
+
setup: z.ZodBoolean;
|
|
172
|
+
recommended: z.ZodBoolean;
|
|
173
|
+
extensions: z.ZodArray<z.ZodString>;
|
|
144
174
|
}, z.core.$strict>;
|
|
145
175
|
environment: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
146
176
|
}, z.core.$strict>;
|
|
@@ -193,6 +223,26 @@ export declare const Config: z.ZodObject<{
|
|
|
193
223
|
personalization: z.ZodObject<{
|
|
194
224
|
enabled: z.ZodBoolean;
|
|
195
225
|
}, z.core.$strict>;
|
|
226
|
+
apps: z.ZodObject<{
|
|
227
|
+
install: z.ZodArray<z.ZodEnum<{
|
|
228
|
+
[x: string]: string;
|
|
229
|
+
}>>;
|
|
230
|
+
custom: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
231
|
+
name: z.ZodString;
|
|
232
|
+
github: z.ZodOptional<z.ZodString>;
|
|
233
|
+
asset: z.ZodOptional<z.ZodString>;
|
|
234
|
+
url: z.ZodOptional<z.ZodString>;
|
|
235
|
+
sha256: z.ZodOptional<z.ZodString>;
|
|
236
|
+
unpack: z.ZodOptional<z.ZodEnum<{
|
|
237
|
+
file: "file";
|
|
238
|
+
archive: "archive";
|
|
239
|
+
nsis: "nsis";
|
|
240
|
+
msi: "msi";
|
|
241
|
+
}>>;
|
|
242
|
+
exe: z.ZodString;
|
|
243
|
+
cli: z.ZodOptional<z.ZodBoolean>;
|
|
244
|
+
}, z.core.$strict>>;
|
|
245
|
+
}, z.core.$strict>;
|
|
196
246
|
claudeProfile: z.ZodObject<{
|
|
197
247
|
enabled: z.ZodBoolean;
|
|
198
248
|
hostDir: z.ZodString;
|
|
@@ -201,7 +251,12 @@ export declare const Config: z.ZodObject<{
|
|
|
201
251
|
copyCredentials: z.ZodBoolean;
|
|
202
252
|
overwrite: z.ZodBoolean;
|
|
203
253
|
syncHistory: z.ZodBoolean;
|
|
254
|
+
historyScope: z.ZodEnum<{
|
|
255
|
+
project: "project";
|
|
256
|
+
all: "all";
|
|
257
|
+
}>;
|
|
204
258
|
syncClaudeMem: z.ZodBoolean;
|
|
259
|
+
syncClaudeMemBack: z.ZodBoolean;
|
|
205
260
|
}, z.core.$strict>;
|
|
206
261
|
harnesses: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
207
262
|
hostExecutable: z.ZodOptional<z.ZodString>;
|
|
@@ -235,6 +290,10 @@ export declare const LocalOverrides: z.ZodObject<{
|
|
|
235
290
|
harness: z.ZodOptional<z.ZodString>;
|
|
236
291
|
guestWorkspace: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<"auto">, z.ZodLiteral<"mirror">, z.ZodString]>>;
|
|
237
292
|
readOnly: z.ZodOptional<z.ZodBoolean>;
|
|
293
|
+
workspaceFilter: z.ZodOptional<z.ZodObject<{
|
|
294
|
+
enabled: z.ZodOptional<z.ZodBoolean>;
|
|
295
|
+
exclude: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
296
|
+
}, z.core.$strict>>;
|
|
238
297
|
mappings: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
239
298
|
host: z.ZodString;
|
|
240
299
|
guest: z.ZodString;
|
|
@@ -255,9 +314,11 @@ export declare const LocalOverrides: z.ZodObject<{
|
|
|
255
314
|
printerRedirection: z.ZodOptional<z.ZodBoolean>;
|
|
256
315
|
protectedClient: z.ZodOptional<z.ZodBoolean>;
|
|
257
316
|
headless: z.ZodOptional<z.ZodBoolean>;
|
|
317
|
+
keepAwake: z.ZodOptional<z.ZodBoolean>;
|
|
258
318
|
}, z.core.$strict>>;
|
|
259
319
|
network: z.ZodOptional<z.ZodObject<{
|
|
260
320
|
blockPrivateRanges: z.ZodOptional<z.ZodBoolean>;
|
|
321
|
+
blockHosts: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
261
322
|
}, z.core.$strict>>;
|
|
262
323
|
provisioning: z.ZodOptional<z.ZodObject<{
|
|
263
324
|
copyFromHost: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -580,9 +641,13 @@ export declare const LocalOverrides: z.ZodObject<{
|
|
|
580
641
|
personalization: z.ZodOptional<z.ZodObject<{
|
|
581
642
|
enabled: z.ZodOptional<z.ZodBoolean>;
|
|
582
643
|
}, z.core.$strict>>;
|
|
644
|
+
apps: z.ZodOptional<z.ZodObject<{
|
|
645
|
+
install: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
646
|
+
[x: string]: string;
|
|
647
|
+
}>>>;
|
|
648
|
+
}, z.core.$strict>>;
|
|
583
649
|
claudeProfile: z.ZodOptional<z.ZodObject<{
|
|
584
650
|
enabled: z.ZodOptional<z.ZodBoolean>;
|
|
585
|
-
include: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
586
651
|
copyCredentials: z.ZodOptional<z.ZodBoolean>;
|
|
587
652
|
overwrite: z.ZodOptional<z.ZodBoolean>;
|
|
588
653
|
syncHistory: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -6,22 +6,27 @@ import { Name, SchemaRef, Strings, toggle } from './common.js';
|
|
|
6
6
|
import { Databases } from './databases.js';
|
|
7
7
|
import { Harness } from './harness.js';
|
|
8
8
|
import { AgentCommands, Attach, ClaudeProfile, Secrets } from './profile.js';
|
|
9
|
-
import { Downloads, Git, GuestWorkspace, Mapping, Provisioning, Sandbox } from './sandbox.js';
|
|
9
|
+
import { Audit, Downloads, Git, GuestWorkspace, Mapping, Network, Provisioning, Sandbox, WorkspaceFilter } from './sandbox.js';
|
|
10
10
|
import { Toolchains } from './toolchains.js';
|
|
11
|
+
import { Apps } from './apps.js';
|
|
11
12
|
import { Projects } from './projects.js';
|
|
12
13
|
export const Config = z.strictObject({
|
|
13
14
|
$schema: SchemaRef.optional(),
|
|
14
15
|
defaultHarness: Name.describe('Agent started by `sandbox launch` (sandbox claude always runs claude).'),
|
|
15
16
|
guestWorkspace: GuestWorkspace,
|
|
16
17
|
readOnly: z.boolean().describe('Map the workspace read-only.'),
|
|
18
|
+
workspaceFilter: WorkspaceFilter,
|
|
19
|
+
audit: Audit,
|
|
17
20
|
mappings: z.array(Mapping).describe('Extra folders from this PC.'),
|
|
18
21
|
sandbox: Sandbox,
|
|
19
22
|
storage: z.strictObject({
|
|
20
23
|
root: z.string().min(1).describe('Where everything is kept on this PC (tools, databases, logins, launches).'),
|
|
21
24
|
persistAgentState: z.boolean().describe('Keep agent logins and history between sandboxes.'),
|
|
22
25
|
dataName: Name.describe('Name of the persistent data set (C:\\AgentData) shared by all workspaces.'),
|
|
26
|
+
markSandboxFiles: z.boolean().describe('Programs and scripts (.exe, .ps1, .bat, .cmd, .msi, .lnk ...) that appear or change in your project while a sandbox runs get Windows\' "downloaded from the internet" mark, so SmartScreen and PowerShell ask before they run on this PC.'),
|
|
27
|
+
warnDataGb: z.number().min(0).describe('Say at launch when the persisted data (tools, databases, logins) is bigger than this many GB; 0 = never. sandbox data shows what takes the space.'),
|
|
23
28
|
}),
|
|
24
|
-
network:
|
|
29
|
+
network: Network,
|
|
25
30
|
provisioning: Provisioning,
|
|
26
31
|
downloads: Downloads,
|
|
27
32
|
toolchains: Toolchains,
|
|
@@ -30,6 +35,7 @@ export const Config = z.strictObject({
|
|
|
30
35
|
agentCommands: AgentCommands,
|
|
31
36
|
secrets: Secrets,
|
|
32
37
|
personalization: toggle('Copy your desktop look (theme, accent, taskbar, wallpaper, ...) into the sandbox.'),
|
|
38
|
+
apps: Apps,
|
|
33
39
|
claudeProfile: ClaudeProfile,
|
|
34
40
|
harnesses: z.record(Name, Harness).describe('Agents that can run in the sandbox.'),
|
|
35
41
|
hooks: z.strictObject({ failFast: z.boolean().describe('Stop when a hook script fails.') }),
|
|
@@ -44,10 +50,14 @@ export const LocalOverrides = z.strictObject({
|
|
|
44
50
|
harness: Name.describe('Agent for this project.'),
|
|
45
51
|
guestWorkspace: GuestWorkspace,
|
|
46
52
|
readOnly: z.boolean().describe('Map this project read-only.'),
|
|
47
|
-
|
|
53
|
+
// A project may turn the filter on and keep more out, never let ignored files in.
|
|
54
|
+
workspaceFilter: WorkspaceFilter.pick({ enabled: true, exclude: true }).partial(),
|
|
55
|
+
mappings: z.array(Mapping).describe('Extra folders inside this project (relative to this file), e.g. a read-only data folder at another place in the sandbox.'),
|
|
48
56
|
scripts: Strings.describe('Scripts (.ps1 / .mjs / .cmd, relative to this file) run inside the sandbox before the agent.'),
|
|
49
|
-
|
|
50
|
-
|
|
57
|
+
// This PC's resources are yours to share: CPU and paging file (this PC's disk) are not a project's to set.
|
|
58
|
+
sandbox: Sandbox.omit({ cpuLimit: true, pageFileMb: true }).partial(),
|
|
59
|
+
// A project may block more (the LAN, hosts); it never opens hosts, ports, DNS or a proxy.
|
|
60
|
+
network: Network.pick({ blockPrivateRanges: true, blockHosts: true }).partial(),
|
|
51
61
|
provisioning: Provisioning.pick({ copyFromHost: true, fallbackToInstall: true, refresh: true }).extend({ git: Git.partial(), terminal: toggle('Windows Terminal as the sandbox window.').partial() }).partial(),
|
|
52
62
|
downloads: Downloads.partial(),
|
|
53
63
|
// Every toolchain setting, each one optional (a project usually only switches languages on or pins a version).
|
|
@@ -58,6 +68,8 @@ export const LocalOverrides = z.strictObject({
|
|
|
58
68
|
databases: z.strictObject(_.mapValues(Databases.shape, (shape) => shape.partial())).partial(),
|
|
59
69
|
attach: Attach.partial(),
|
|
60
70
|
personalization: toggle('Copy your desktop look into the sandbox.').partial(),
|
|
61
|
-
|
|
71
|
+
// Catalogue apps only (a vetted list): your own programs (apps.custom) download anything, so a repo cannot add them.
|
|
72
|
+
apps: Apps.pick({ install: true }).partial(),
|
|
73
|
+
claudeProfile: ClaudeProfile.pick({ enabled: true, overwrite: true, copyCredentials: true, syncHistory: true, syncClaudeMem: true }).partial(),
|
|
62
74
|
hooks: z.strictObject({ failFast: z.boolean() }).partial(),
|
|
63
75
|
}).partial();
|
|
@@ -23,5 +23,10 @@ export declare const ClaudeProfile: z.ZodObject<{
|
|
|
23
23
|
copyCredentials: z.ZodBoolean;
|
|
24
24
|
overwrite: z.ZodBoolean;
|
|
25
25
|
syncHistory: z.ZodBoolean;
|
|
26
|
+
historyScope: z.ZodEnum<{
|
|
27
|
+
project: "project";
|
|
28
|
+
all: "all";
|
|
29
|
+
}>;
|
|
26
30
|
syncClaudeMem: z.ZodBoolean;
|
|
31
|
+
syncClaudeMemBack: z.ZodBoolean;
|
|
27
32
|
}, z.core.$strict>;
|
|
@@ -24,6 +24,8 @@ export const ClaudeProfile = z.strictObject({
|
|
|
24
24
|
globalStateFile: z.string().describe('Your .claude.json, or "auto". Empty string = do not copy.'),
|
|
25
25
|
copyCredentials: z.boolean().describe('Copy your Claude LOGIN into the sandbox. Prefer sandbox account instead.'),
|
|
26
26
|
overwrite: z.boolean().describe('Refresh these settings from this PC on every launch.'),
|
|
27
|
-
syncHistory: z.boolean().describe('Bring
|
|
28
|
-
|
|
27
|
+
syncHistory: z.boolean().describe('Bring conversation history in, and sync new history back (path-translated).'),
|
|
28
|
+
historyScope: z.enum(['project', 'all']).describe('Which history comes in: "project" = only the projects in this sandbox (other projects\' conversations, file backups and prompts stay on this PC), "all" = everything. Only this sandbox\'s projects ever sync back.'),
|
|
29
|
+
syncClaudeMem: z.boolean().describe('Bring claude-mem data into the sandbox (its settings and backups stay on this PC).'),
|
|
30
|
+
syncClaudeMemBack: z.boolean().describe('Also copy claude-mem\'s database back to this PC when the sandbox closes. Off by default: what is in it is loaded into every Claude session on this PC, so a compromised agent could plant instructions there.'),
|
|
29
31
|
});
|
|
@@ -11,6 +11,7 @@ export declare const Sandbox: z.ZodObject<{
|
|
|
11
11
|
maxMemoryMb: z.ZodNumber;
|
|
12
12
|
maxMemoryPercent: z.ZodNumber;
|
|
13
13
|
cpuLimit: z.ZodUnion<readonly [z.ZodLiteral<"none">, z.ZodNumber]>;
|
|
14
|
+
pageFileMb: z.ZodNumber;
|
|
14
15
|
reserveHostMb: z.ZodNumber;
|
|
15
16
|
vGpu: z.ZodBoolean;
|
|
16
17
|
networking: z.ZodBoolean;
|
|
@@ -20,6 +21,30 @@ export declare const Sandbox: z.ZodObject<{
|
|
|
20
21
|
printerRedirection: z.ZodBoolean;
|
|
21
22
|
protectedClient: z.ZodBoolean;
|
|
22
23
|
headless: z.ZodBoolean;
|
|
24
|
+
keepAwake: z.ZodBoolean;
|
|
25
|
+
}, z.core.$strict>;
|
|
26
|
+
export declare const Network: z.ZodObject<{
|
|
27
|
+
blockPrivateRanges: z.ZodBoolean;
|
|
28
|
+
allowOnly: z.ZodArray<z.ZodString>;
|
|
29
|
+
blockHosts: z.ZodArray<z.ZodString>;
|
|
30
|
+
forwardPorts: z.ZodArray<z.ZodObject<{
|
|
31
|
+
pc: z.ZodNumber;
|
|
32
|
+
sandbox: z.ZodNumber;
|
|
33
|
+
}, z.core.$strict>>;
|
|
34
|
+
dns: z.ZodArray<z.ZodString>;
|
|
35
|
+
proxy: z.ZodString;
|
|
36
|
+
}, z.core.$strict>;
|
|
37
|
+
export declare const Audit: z.ZodObject<{
|
|
38
|
+
enabled: z.ZodBoolean;
|
|
39
|
+
days: z.ZodNumber;
|
|
40
|
+
maxMb: z.ZodNumber;
|
|
41
|
+
processes: z.ZodBoolean;
|
|
42
|
+
files: z.ZodBoolean;
|
|
43
|
+
}, z.core.$strict>;
|
|
44
|
+
export declare const WorkspaceFilter: z.ZodObject<{
|
|
45
|
+
enabled: z.ZodBoolean;
|
|
46
|
+
exclude: z.ZodArray<z.ZodString>;
|
|
47
|
+
include: z.ZodArray<z.ZodString>;
|
|
23
48
|
}, z.core.$strict>;
|
|
24
49
|
export declare const Git: z.ZodObject<{
|
|
25
50
|
enabled: z.ZodBoolean;
|
|
@@ -44,6 +69,9 @@ export declare const Provisioning: z.ZodObject<{
|
|
|
44
69
|
terminal: z.ZodObject<{
|
|
45
70
|
enabled: z.ZodBoolean;
|
|
46
71
|
}, z.core.$strict>;
|
|
72
|
+
conpty: z.ZodObject<{
|
|
73
|
+
enabled: z.ZodBoolean;
|
|
74
|
+
}, z.core.$strict>;
|
|
47
75
|
}, z.core.$strict>;
|
|
48
76
|
export declare const Downloads: z.ZodObject<{
|
|
49
77
|
requireChecksum: z.ZodBoolean;
|