windows-development-sandbox 1.12.0 → 1.13.2
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 +1 -0
- package/config.example.json +11 -1
- 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/accounts/accounts.def.d.ts +1 -0
- package/dist/app/features/accounts/accounts.def.js +12 -3
- package/dist/app/features/accounts/accounts.run.d.ts +1 -0
- package/dist/app/features/accounts/accounts.run.js +20 -11
- 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.def.js +1 -0
- package/dist/app/features/audit/audit.def.d.ts +11 -2
- package/dist/app/features/audit/audit.def.js +7 -4
- package/dist/app/features/audit/audit.run.d.ts +3 -0
- package/dist/app/features/defs.d.ts +156 -3
- package/dist/app/features/defs.js +12 -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-file.d.ts +7 -0
- package/dist/app/features/settings/settings-file.js +19 -2
- package/dist/app/features/settings/settings-rows.js +9 -1
- 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/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 +14 -1
- 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 +24 -4
- package/dist/bin/agent-sandbox.js +7 -1
- package/dist/bin/chunk-IL7BXJF2.js +7 -0
- package/dist/bin/chunk-K4SU3MHJ.js +10432 -0
- package/dist/bin/chunk-VENWSGX2.js +78437 -0
- package/dist/bin/claude-host.js +10 -4
- package/dist/bin/claude-sbx.js +8 -2
- package/dist/bin/codex-sbx.js +8 -2
- package/dist/bin/gemini-sbx.js +8 -2
- package/dist/bin/sandbox.js +7 -2
- package/dist/bin/sbx-hide.js +8 -2
- package/dist/bin/sbx-show.js +8 -2
- package/dist/cli/commands/launch/prepare.js +24 -7
- package/dist/cli/commands/launch/running.js +4 -0
- package/dist/cli/commands/registry.js +2 -0
- 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/storage/offer.js +1 -1
- package/dist/cli/commands/web.js +66 -0
- package/dist/cli/manifest.d.ts +63 -4
- package/dist/cli/manifest.js +33 -4
- package/dist/cli/menu/io-menu.d.ts +5 -3
- package/dist/cli/menu/io-menu.js +12 -5
- package/dist/cli/menu/live.d.ts +29 -0
- package/dist/cli/menu/live.js +92 -21
- package/dist/cli/menu/menu.d.ts +2 -1
- package/dist/cli/menu/menu.js +16 -2
- package/dist/cli/menu/pages/list-page.d.ts +5 -0
- package/dist/cli/menu/pages/list-page.js +19 -0
- package/dist/cli/menu/remote-menu.d.ts +19 -0
- package/dist/cli/menu/remote-menu.js +108 -0
- package/dist/cli/menu/shell.d.ts +2 -0
- package/dist/cli/menu/shell.js +1 -1
- package/dist/cli/menu/views.js +2 -0
- package/dist/cli/operations/handler.js +15 -1
- package/dist/cli/services/accounts-service.d.ts +7 -0
- package/dist/cli/services/accounts-service.js +20 -0
- 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/cli/tui/app.d.ts +4 -0
- package/dist/cli/tui/app.js +5 -0
- package/dist/cli/tui/terminal.d.ts +6 -0
- package/dist/cli/tui/terminal.js +8 -1
- package/dist/cli/validate.js +8 -4
- package/dist/core/config/defaults.js +11 -1
- package/dist/core/local-config/file.js +16 -2
- package/dist/core/local-config/overrides.js +7 -0
- package/dist/core/package-info.js +1 -1
- package/dist/core/run/versions.js +9 -8
- package/dist/core/schema/config.d.ts +22 -3
- package/dist/core/schema/config.js +39 -2
- 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/daemon/daemon.js +2 -0
- package/dist/guest/daemon/idle-shutdown.js +2 -1
- package/dist/guest/daemon/sessions.d.ts +16 -2
- package/dist/guest/daemon/sessions.js +126 -20
- 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/toolchains/setup.js +3 -0
- package/dist/host/audit/log.d.ts +3 -2
- package/dist/host/audit/log.js +3 -3
- 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/sandbox/platform.d.ts +2 -0
- package/dist/host/sandbox/platform.js +19 -4
- package/dist/host/storage/contained/state.d.ts +3 -2
- package/dist/host/storage/contained/state.js +5 -2
- 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 +74 -3
- package/dist/runtime/daemon.js +205 -24
- package/dist/runtime/db.js +74 -3
- package/dist/runtime/gitguard.js +6 -0
- package/dist/runtime/main.js +77 -3
- package/dist/runtime/netproxy.js +74 -3
- package/dist/runtime/view.js +76 -3
- package/dist/selftest/app.test.js +41 -6
- 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/hostile-input.test.js +12 -1
- package/dist/selftest/index.js +2 -0
- package/dist/selftest/layout.test.js +10 -1
- package/dist/selftest/live-daemon.test.js +25 -0
- package/dist/selftest/local-config.test.js +30 -0
- package/dist/selftest/menu-perf.test.js +198 -12
- package/dist/selftest/menu.test.js +4 -0
- 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/fixtures.d.ts +13 -0
- package/dist/selftest/support/web-preview.js +63 -0
- package/dist/selftest/web.test.js +2 -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/web/index.html +1 -1
- package/dist/web/main-NBQ3LF4E.js +221 -0
- package/native/csharp/HcsHost/AssemblyInfo.cs +3 -3
- 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/csharp/PtyHost.cs +20 -0
- package/schemas/config.schema.json +65 -0
- package/schemas/sandbox-config.schema.json +0 -18
- package/dist/web/main-UD4QCXZR.js +0 -219
|
@@ -1,13 +1,17 @@
|
|
|
1
|
-
// The sessions feature's work: the list (as this user may see it under the sharing rules)
|
|
2
|
-
|
|
1
|
+
// The sessions feature's work: the list (as this user may see it under the sharing rules), restarting the agents,
|
|
2
|
+
// opening a shell in a project of the running sandbox and closing a session.
|
|
3
|
+
import { fail, safeText } from '../../../core/util/index.js';
|
|
4
|
+
import { RunDir } from '../../../shared/channel/index.js';
|
|
3
5
|
import { hostEndpoint } from '../../../host/channel/index.js';
|
|
4
6
|
import { isSandboxRunning } from '../../../host/sandbox/index.js';
|
|
5
7
|
import { requireOnPc, requireRunning } from '../../../cli/commands/context.js';
|
|
8
|
+
import { locateHere } from '../../../cli/commands/projects/here.js';
|
|
6
9
|
import { lookAtSandbox, useSharedSandbox } from '../../../cli/commands/shared-use.js';
|
|
7
10
|
import { SessionsService } from '../../../cli/services/index.js';
|
|
11
|
+
import { runningProjects } from '../../../cli/services/web-service.js';
|
|
8
12
|
import { implement, typedConfirm } from '../../context.js';
|
|
9
13
|
import { cancelled } from '../../kernel/index.js';
|
|
10
|
-
import { listSessions, restartSessions } from './sessions.def.js';
|
|
14
|
+
import { closeSession, listSessions, restartSessions, startShell } from './sessions.def.js';
|
|
11
15
|
/** A listed session as the surfaces see it: no attach address or token, text from the sandbox made safe. */
|
|
12
16
|
function sessionRow(s) {
|
|
13
17
|
const { attach: _attach, ...rest } = s;
|
|
@@ -56,3 +60,67 @@ export const restartSessionsRun = implement(restartSessions, async (ctx, input,
|
|
|
56
60
|
io.progress('Restarting the agents' + (scope.guest ? ' of ' + scope.projectName : ''));
|
|
57
61
|
return { message: safeText(await service.restart(input.harness ?? null, scope.guest)) };
|
|
58
62
|
});
|
|
63
|
+
/** Where a new shell opens: the project named, else the one of the current folder, else the only one, else asked. */
|
|
64
|
+
async function shellPlace(ctx, project, io) {
|
|
65
|
+
const projects = runningProjects({ ctx, line: null, limited: false, owner: null });
|
|
66
|
+
if (project !== undefined) {
|
|
67
|
+
const named = projects.find((p) => p.name.toLowerCase() === project.toLowerCase());
|
|
68
|
+
if (!named)
|
|
69
|
+
fail('The running sandbox has no project "' + project + '".', 'sandbox status lists its projects.');
|
|
70
|
+
return { name: named.name, guest: named.guest };
|
|
71
|
+
}
|
|
72
|
+
const here = locateHere(ctx, new RunDir(ctx.dataPath).readSandboxStatus());
|
|
73
|
+
if (here.guestDir)
|
|
74
|
+
return { name: here.project?.name, guest: here.guestDir };
|
|
75
|
+
if (!projects.length)
|
|
76
|
+
fail('The running sandbox has no projects.');
|
|
77
|
+
if (projects.length === 1)
|
|
78
|
+
return projects[0];
|
|
79
|
+
const chosen = await io.choose({
|
|
80
|
+
title: 'Which project?',
|
|
81
|
+
choices: projects.map((p) => ({ value: p.name, label: p.name, description: p.guest })),
|
|
82
|
+
});
|
|
83
|
+
const picked = projects.find((p) => p.name === chosen);
|
|
84
|
+
if (!picked)
|
|
85
|
+
cancelled();
|
|
86
|
+
return picked;
|
|
87
|
+
}
|
|
88
|
+
export const startShellRun = implement(startShell, async (asked, input, io) => {
|
|
89
|
+
requireOnPc('shell');
|
|
90
|
+
requireRunning();
|
|
91
|
+
const ctx = await useSharedSandbox(asked, { kind: 'use' }, typedConfirm(io));
|
|
92
|
+
const place = await shellPlace(ctx, input.project, io);
|
|
93
|
+
io.progress('Opening ' + (input.shell === 'auto' ? 'a shell' : input.shell) + ' in ' + (place.name ?? place.guest));
|
|
94
|
+
const session = await new SessionsService(ctx).openShell(input.shell, place.guest);
|
|
95
|
+
const shell = safeText(session.harness);
|
|
96
|
+
return {
|
|
97
|
+
session: safeText(session.id),
|
|
98
|
+
shell,
|
|
99
|
+
message: 'Opened ' + shell + ' in ' + safeText(place.name ?? place.guest) + ' (session ' + safeText(session.id) + ').',
|
|
100
|
+
};
|
|
101
|
+
});
|
|
102
|
+
export const closeSessionRun = implement(closeSession, async (asked, input, io) => {
|
|
103
|
+
requireOnPc('close');
|
|
104
|
+
requireRunning();
|
|
105
|
+
const find = (ctx) => new SessionsService(ctx).running(null).find((s) => s.id === input.session);
|
|
106
|
+
// Anyone the sandbox is shared with may close a shell; an agent session is its owner's to end.
|
|
107
|
+
const ctx = await useSharedSandbox(asked, (shared) => find(shared)?.kind === 'shell' ? { kind: 'use' } : { kind: 'owner-only', what: 'close an agent session' }, typedConfirm(io));
|
|
108
|
+
const session = find(ctx);
|
|
109
|
+
if (!session)
|
|
110
|
+
fail('No running session ' + input.session + '.', 'sandbox sessions lists the running ones.');
|
|
111
|
+
const what = safeText(session.harness) + (session.kind === 'shell' ? ' (shell)' : '');
|
|
112
|
+
const yes = await io.confirm({
|
|
113
|
+
title: 'Close ' + what + ' session ' + input.session + '?',
|
|
114
|
+
lines: [
|
|
115
|
+
'It ends, with every program it started.' +
|
|
116
|
+
(session.kind === 'shell' ? '' : ' The agent can pick its conversation up again in a new session.'),
|
|
117
|
+
],
|
|
118
|
+
yes: 'Close',
|
|
119
|
+
danger: true,
|
|
120
|
+
implied: true,
|
|
121
|
+
});
|
|
122
|
+
if (!yes)
|
|
123
|
+
cancelled();
|
|
124
|
+
io.progress('Closing session ' + input.session);
|
|
125
|
+
return { message: safeText(await new SessionsService(ctx).close(input.session)) };
|
|
126
|
+
});
|
|
@@ -22,6 +22,8 @@ export declare class ConfigFile {
|
|
|
22
22
|
readonly target: SettingsTarget;
|
|
23
23
|
readonly file: string;
|
|
24
24
|
readonly schema: SchemaNode;
|
|
25
|
+
/** The file as read in this synchronous run (see read). */
|
|
26
|
+
private snapshot;
|
|
25
27
|
constructor(target: SettingsTarget, file: string);
|
|
26
28
|
static user(configPath: string | null): ConfigFile;
|
|
27
29
|
static project(dir: string): ConfigFile;
|
|
@@ -30,6 +32,11 @@ export declare class ConfigFile {
|
|
|
30
32
|
text(): string;
|
|
31
33
|
/** The value written in this file (undefined = not set here). */
|
|
32
34
|
valueInFile(key: string[]): unknown;
|
|
35
|
+
/**
|
|
36
|
+
* The file's text and what it parses to, read once per synchronous run: the settings list asks for every setting's
|
|
37
|
+
* value (hundreds of reads), and the next run (after a key, a save or a change outside) reads the file again.
|
|
38
|
+
*/
|
|
39
|
+
private read;
|
|
33
40
|
/** Your own settings with the built-in defaults filled in (what a sandbox uses without a project file). */
|
|
34
41
|
userConfig(): Config;
|
|
35
42
|
/** What is in effect: for your file the merged config, for a project file only what the file sets. */
|
|
@@ -34,6 +34,8 @@ export class ConfigFile {
|
|
|
34
34
|
target;
|
|
35
35
|
file;
|
|
36
36
|
schema;
|
|
37
|
+
/** The file as read in this synchronous run (see read). */
|
|
38
|
+
snapshot;
|
|
37
39
|
constructor(target, file) {
|
|
38
40
|
this.target = target;
|
|
39
41
|
this.file = file;
|
|
@@ -52,11 +54,25 @@ export class ConfigFile {
|
|
|
52
54
|
return fs.existsSync(this.file);
|
|
53
55
|
}
|
|
54
56
|
text() {
|
|
55
|
-
return this.
|
|
57
|
+
return this.read().text;
|
|
56
58
|
}
|
|
57
59
|
/** The value written in this file (undefined = not set here). */
|
|
58
60
|
valueInFile(key) {
|
|
59
|
-
|
|
61
|
+
const value = _.get(this.read().parsed, key);
|
|
62
|
+
// A copy: the parsed file is shared by every read in this run.
|
|
63
|
+
return value !== null && typeof value === 'object' ? structuredClone(value) : value;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* The file's text and what it parses to, read once per synchronous run: the settings list asks for every setting's
|
|
67
|
+
* value (hundreds of reads), and the next run (after a key, a save or a change outside) reads the file again.
|
|
68
|
+
*/
|
|
69
|
+
read() {
|
|
70
|
+
if (!this.snapshot) {
|
|
71
|
+
const text = this.exists ? fs.readFileSync(this.file, 'utf8') : '';
|
|
72
|
+
this.snapshot = { text, parsed: parse(text) ?? {} };
|
|
73
|
+
queueMicrotask(() => (this.snapshot = undefined));
|
|
74
|
+
}
|
|
75
|
+
return this.snapshot;
|
|
60
76
|
}
|
|
61
77
|
/** Your own settings with the built-in defaults filled in (what a sandbox uses without a project file). */
|
|
62
78
|
userConfig() {
|
|
@@ -100,6 +116,7 @@ export class ConfigFile {
|
|
|
100
116
|
return 'unchanged';
|
|
101
117
|
fs.mkdirSync(path.dirname(this.file), { recursive: true });
|
|
102
118
|
fs.writeFileSync(this.file, text, 'utf8');
|
|
119
|
+
this.snapshot = undefined;
|
|
103
120
|
return 'saved';
|
|
104
121
|
}
|
|
105
122
|
}
|
|
@@ -46,9 +46,17 @@ export function masked(key, value) {
|
|
|
46
46
|
return undefined;
|
|
47
47
|
return _.get(redactSecrets(_.set({}, key, structuredClone(value))), key);
|
|
48
48
|
}
|
|
49
|
+
/** The keys a project file's schema can set, worked out once per schema (a list asks for it on every row). */
|
|
50
|
+
const projectKeysOf = new WeakMap();
|
|
49
51
|
/** The keys a project file can set. */
|
|
50
52
|
function projectKeys(files) {
|
|
51
|
-
|
|
53
|
+
const schema = files.project.schema;
|
|
54
|
+
let keys = projectKeysOf.get(schema);
|
|
55
|
+
if (!keys) {
|
|
56
|
+
keys = new Set(schema.leaves().map((l) => l.key.join('.')));
|
|
57
|
+
projectKeysOf.set(schema, keys);
|
|
58
|
+
}
|
|
59
|
+
return keys;
|
|
52
60
|
}
|
|
53
61
|
/** One setting's row in a scope (`config` and `inProject` can be passed when making many rows). */
|
|
54
62
|
export function settingRow(s, files, scope, source = SAVED, config = files.user.userConfig(), inProject = projectKeys(files)) {
|
|
@@ -94,6 +94,14 @@ export declare const getSetting: import("../../kernel/define.js").QueryDef<z.Zod
|
|
|
94
94
|
key: z.ZodString;
|
|
95
95
|
value: z.ZodUnknown;
|
|
96
96
|
}, z.core.$strip>, "settings.get">;
|
|
97
|
+
/**
|
|
98
|
+
* The settings someone with control (not the owner) may change: how attaching and reopening sessions behave, in your
|
|
99
|
+
* own settings file. Everything else (folders, agents, secrets, network, storage, sharing, the audit log, remote
|
|
100
|
+
* access) changes what the sandbox can reach or who can come in, so it is the owner's (docs/design/remote-access.md).
|
|
101
|
+
*/
|
|
102
|
+
export declare const CONTROL_SETTINGS: readonly string[];
|
|
103
|
+
/** settings.set / settings.reset: control for CONTROL_SETTINGS in the user file, owner for everything else. */
|
|
104
|
+
export declare function controlSettingAccess(input: unknown): 'control' | 'owner';
|
|
97
105
|
export declare const setSetting: import("../../kernel/define.js").OperationDef<z.ZodObject<{
|
|
98
106
|
scope: z.ZodDefault<z.ZodEnum<{
|
|
99
107
|
project: "project";
|
|
@@ -65,8 +65,24 @@ export const getSetting = defineQuery({
|
|
|
65
65
|
surfaces: { except: { menu: 'Settings shows every value', web: 'the Settings page shows every value' } },
|
|
66
66
|
cli: { name: 'config', action: 'get', positional: ['key'] },
|
|
67
67
|
});
|
|
68
|
+
/**
|
|
69
|
+
* The settings someone with control (not the owner) may change: how attaching and reopening sessions behave, in your
|
|
70
|
+
* own settings file. Everything else (folders, agents, secrets, network, storage, sharing, the audit log, remote
|
|
71
|
+
* access) changes what the sandbox can reach or who can come in, so it is the owner's (docs/design/remote-access.md).
|
|
72
|
+
*/
|
|
73
|
+
export const CONTROL_SETTINGS = ['attach.', 'resume.'];
|
|
74
|
+
/** settings.set / settings.reset: control for CONTROL_SETTINGS in the user file, owner for everything else. */
|
|
75
|
+
export function controlSettingAccess(input) {
|
|
76
|
+
const o = (input ?? {});
|
|
77
|
+
const key = typeof o.key === 'string' ? o.key : '';
|
|
78
|
+
const userFile = o.scope === undefined || o.scope === 'user';
|
|
79
|
+
return userFile && CONTROL_SETTINGS.some((prefix) => key.startsWith(prefix) && key.length > prefix.length)
|
|
80
|
+
? 'control'
|
|
81
|
+
: 'owner';
|
|
82
|
+
}
|
|
68
83
|
export const setSetting = defineOperation({
|
|
69
84
|
id: 'settings.set',
|
|
85
|
+
access: controlSettingAccess,
|
|
70
86
|
title: 'Change a setting',
|
|
71
87
|
summary: 'change one setting (text is read like the command line: true/on, numbers, JSON)',
|
|
72
88
|
input: z.object({
|
|
@@ -80,6 +96,7 @@ export const setSetting = defineOperation({
|
|
|
80
96
|
});
|
|
81
97
|
export const resetSetting = defineOperation({
|
|
82
98
|
id: 'settings.reset',
|
|
99
|
+
access: controlSettingAccess,
|
|
83
100
|
title: 'Reset a setting',
|
|
84
101
|
summary: 'put one setting back to its default (in a project file: remove it)',
|
|
85
102
|
input: z.object({ scope: ScopeSchema.default('user'), key: KeySchema }),
|
|
@@ -4,12 +4,18 @@ export declare const WebStatusSchema: z.ZodObject<{
|
|
|
4
4
|
running: z.ZodBoolean;
|
|
5
5
|
port: z.ZodNullable<z.ZodNumber>;
|
|
6
6
|
address: z.ZodNullable<z.ZodString>;
|
|
7
|
+
lanAddress: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
8
|
+
lanFingerprint: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
9
|
+
lanProblem: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
7
10
|
}, z.core.$strip>;
|
|
8
11
|
export type WebStatus = z.output<typeof WebStatusSchema>;
|
|
9
12
|
export declare const webStatus: import("../../kernel/define.js").QueryDef<z.ZodObject<{}, z.core.$strip>, z.ZodObject<{
|
|
10
13
|
running: z.ZodBoolean;
|
|
11
14
|
port: z.ZodNullable<z.ZodNumber>;
|
|
12
15
|
address: z.ZodNullable<z.ZodString>;
|
|
16
|
+
lanAddress: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
17
|
+
lanFingerprint: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
18
|
+
lanProblem: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
13
19
|
}, z.core.$strip>, "web.status">;
|
|
14
20
|
export declare const startWeb: import("../../kernel/define.js").OperationDef<z.ZodObject<{}, z.core.$strip>, z.ZodObject<{
|
|
15
21
|
port: z.ZodNumber;
|
|
@@ -29,5 +35,8 @@ export declare const webView: import("../../kernel/define.js").ViewDef<{
|
|
|
29
35
|
running: z.ZodBoolean;
|
|
30
36
|
port: z.ZodNullable<z.ZodNumber>;
|
|
31
37
|
address: z.ZodNullable<z.ZodString>;
|
|
38
|
+
lanAddress: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
39
|
+
lanFingerprint: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
40
|
+
lanProblem: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
32
41
|
}, z.core.$strip>, "web.status">;
|
|
33
42
|
}, Record<string, never>, "web">;
|
|
@@ -8,6 +8,13 @@ export const WebStatusSchema = z.object({
|
|
|
8
8
|
running: z.boolean().describe('The web UI server answers'),
|
|
9
9
|
port: z.number().nullable().describe('Its port on 127.0.0.1, when it runs'),
|
|
10
10
|
address: z.string().nullable().describe('Its address (without the login token), when it runs'),
|
|
11
|
+
lanAddress: z
|
|
12
|
+
.string()
|
|
13
|
+
.nullable()
|
|
14
|
+
.optional()
|
|
15
|
+
.describe('Its address on your network for paired devices (LAN mode), when it listens there'),
|
|
16
|
+
lanFingerprint: z.string().nullable().optional().describe("The LAN certificate's SHA-256 fingerprint"),
|
|
17
|
+
lanProblem: z.string().nullable().optional().describe('Why LAN mode is on but not listening'),
|
|
11
18
|
});
|
|
12
19
|
const MessageSchema = z.object({ message: z.string().describe('What happened') });
|
|
13
20
|
export const webStatus = defineQuery({
|
|
@@ -18,7 +25,11 @@ export const webStatus = defineQuery({
|
|
|
18
25
|
output: WebStatusSchema,
|
|
19
26
|
surfaces: NOT_ON_WEB,
|
|
20
27
|
text: (o) => o.running && o.port
|
|
21
|
-
? 'The web UI is running at http://127.0.0.1:' +
|
|
28
|
+
? 'The web UI is running at http://127.0.0.1:' +
|
|
29
|
+
o.port +
|
|
30
|
+
'/ (sandbox web open signs you in).' +
|
|
31
|
+
(o.lanAddress ? '\nOn your network: ' + o.lanAddress + ' (certificate ' + o.lanFingerprint + ')' : '') +
|
|
32
|
+
(o.lanProblem ? '\nLAN mode is on but not listening: ' + o.lanProblem : '')
|
|
22
33
|
: 'The web UI is not running (sandbox web start).',
|
|
23
34
|
cli: { name: 'web', action: 'status', json: true },
|
|
24
35
|
});
|
|
@@ -73,6 +84,20 @@ export function webPage(o) {
|
|
|
73
84
|
tone: o.running ? 'ok' : 'muted',
|
|
74
85
|
},
|
|
75
86
|
...(o.address ? [{ id: 'address', label: 'Address', value: o.address + ' (this PC only)' }] : []),
|
|
87
|
+
...(o.lanAddress
|
|
88
|
+
? [
|
|
89
|
+
{
|
|
90
|
+
id: 'lan',
|
|
91
|
+
label: 'On your network',
|
|
92
|
+
value: o.lanAddress,
|
|
93
|
+
description: 'Type this into the phone, then enter the code from Remote access > Allow a new device.',
|
|
94
|
+
details: o.lanFingerprint ? ['Certificate ' + o.lanFingerprint] : undefined,
|
|
95
|
+
},
|
|
96
|
+
]
|
|
97
|
+
: []),
|
|
98
|
+
...(o.lanProblem
|
|
99
|
+
? [{ id: 'lan-problem', label: 'LAN mode', value: o.lanProblem, tone: 'warn' }]
|
|
100
|
+
: []),
|
|
76
101
|
],
|
|
77
102
|
actions: o.running ? [open, stop] : [start, open],
|
|
78
103
|
notes: [{ text: 'Loopback only, behind a token. Open signs you in.', tone: 'muted' }],
|
|
@@ -10,6 +10,9 @@ export declare const webStatusRun: import("../../kernel/define.js").Implementati
|
|
|
10
10
|
running: import("zod").ZodBoolean;
|
|
11
11
|
port: import("zod").ZodNullable<import("zod").ZodNumber>;
|
|
12
12
|
address: import("zod").ZodNullable<import("zod").ZodString>;
|
|
13
|
+
lanAddress: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>;
|
|
14
|
+
lanFingerprint: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>;
|
|
15
|
+
lanProblem: import("zod").ZodOptional<import("zod").ZodNullable<import("zod").ZodString>>;
|
|
13
16
|
}, import("zod/v4/core").$strip>, "web.status">, import("../../../cli/commands/context.js").Context>;
|
|
14
17
|
export declare const startWebRun: import("../../kernel/define.js").Implementation<import("../../kernel/define.js").OperationDef<import("zod").ZodObject<{}, import("zod/v4/core").$strip>, import("zod").ZodObject<{
|
|
15
18
|
port: import("zod").ZodNumber;
|
|
@@ -60,7 +60,15 @@ export const webStatusRun = implement(webStatus, async (ctx) => {
|
|
|
60
60
|
requireOnPc('web');
|
|
61
61
|
const { running, state } = await controlOf(ctx).status();
|
|
62
62
|
const port = running && state ? state.port : null;
|
|
63
|
-
|
|
63
|
+
const lan = running ? (state?.lan ?? null) : null;
|
|
64
|
+
return {
|
|
65
|
+
running,
|
|
66
|
+
port,
|
|
67
|
+
address: port ? 'http://127.0.0.1:' + port + '/' : null,
|
|
68
|
+
lanAddress: lan?.url ?? null,
|
|
69
|
+
lanFingerprint: lan?.fingerprint ?? null,
|
|
70
|
+
lanProblem: lan?.problem ?? null,
|
|
71
|
+
};
|
|
64
72
|
});
|
|
65
73
|
export const startWebRun = implement(startWeb, async (ctx, _input, io) => {
|
|
66
74
|
requireOnPc('web');
|
|
@@ -73,8 +81,8 @@ export const openWebRun = implement(openWeb, async (ctx, _input, io) => {
|
|
|
73
81
|
const started = await startServer(ctx, io, control);
|
|
74
82
|
if (started.wasRunning)
|
|
75
83
|
io.log(started.message);
|
|
76
|
-
const { state } = await control.status();
|
|
77
|
-
if (!state)
|
|
84
|
+
const { running, state } = await control.status();
|
|
85
|
+
if (!running || !state)
|
|
78
86
|
return fail('The web UI stopped right away.', 'Run sandbox web start again.');
|
|
79
87
|
openBrowser(control.url(state));
|
|
80
88
|
return { message: 'Opened in your browser.' };
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/** What a definition needs. Default (fails closed): a query needs view, an operation owner. */
|
|
2
|
+
export type Access = 'view' | 'control' | 'owner';
|
|
3
|
+
/** A role the owner can give: view only, or full control (everything but the owner's own things). */
|
|
4
|
+
export type RemoteRole = 'view' | 'control';
|
|
5
|
+
/** Who runs a definition: the owner, or someone the owner let in. */
|
|
6
|
+
export type Caller = {
|
|
7
|
+
readonly kind: 'owner';
|
|
8
|
+
} | {
|
|
9
|
+
readonly kind: 'windows';
|
|
10
|
+
readonly user: string;
|
|
11
|
+
readonly role: RemoteRole;
|
|
12
|
+
} | {
|
|
13
|
+
readonly kind: 'device';
|
|
14
|
+
readonly id: string;
|
|
15
|
+
readonly name: string;
|
|
16
|
+
readonly role: RemoteRole;
|
|
17
|
+
};
|
|
18
|
+
export declare const OWNER: Caller;
|
|
19
|
+
/** A definition's need, as written: fixed, or from its input (settings.set on a web.* key needs owner). */
|
|
20
|
+
export type AccessRule = Access | ((input: unknown) => Access);
|
|
21
|
+
/** The most a caller may do. */
|
|
22
|
+
export declare function callerAccess(caller: Caller): Access;
|
|
23
|
+
/** The caller may run something that needs `need`. */
|
|
24
|
+
export declare function allows(caller: Caller, need: Access): boolean;
|
|
25
|
+
/** What a definition needs for this input. A rule that throws on a strange input needs owner. */
|
|
26
|
+
export declare function accessNeeded(def: {
|
|
27
|
+
readonly kind: 'operation' | 'query';
|
|
28
|
+
readonly access?: AccessRule;
|
|
29
|
+
}, input: unknown): Access;
|
|
30
|
+
/** Who the caller is, in a few words (the audit log, the page's header). */
|
|
31
|
+
export declare function callerLabel(caller: Caller): string;
|
|
32
|
+
/** The refusal text for a caller that may not. */
|
|
33
|
+
export declare function refusalText(caller: Caller, need: Access): string;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// Who may run what (docs/design/remote-access.md): every definition needs view, control or owner; the caller of a run
|
|
2
|
+
// has a role. The owner (at the PC, or through the token link) may do everything; another Windows user of this PC or a
|
|
3
|
+
// paired device has the role the owner gave them, never owner. Pure (the web bundle shows what the role allows).
|
|
4
|
+
export const OWNER = { kind: 'owner' };
|
|
5
|
+
const RANK = { view: 0, control: 1, owner: 2 };
|
|
6
|
+
/** The most a caller may do. */
|
|
7
|
+
export function callerAccess(caller) {
|
|
8
|
+
return caller.kind === 'owner' ? 'owner' : caller.role;
|
|
9
|
+
}
|
|
10
|
+
/** The caller may run something that needs `need`. */
|
|
11
|
+
export function allows(caller, need) {
|
|
12
|
+
return RANK[callerAccess(caller)] >= RANK[need];
|
|
13
|
+
}
|
|
14
|
+
/** What a definition needs for this input. A rule that throws on a strange input needs owner. */
|
|
15
|
+
export function accessNeeded(def, input) {
|
|
16
|
+
const rule = def.access;
|
|
17
|
+
if (rule === undefined)
|
|
18
|
+
return def.kind === 'query' ? 'view' : 'owner';
|
|
19
|
+
if (typeof rule === 'string')
|
|
20
|
+
return rule;
|
|
21
|
+
try {
|
|
22
|
+
return rule(input);
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return 'owner';
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/** Who the caller is, in a few words (the audit log, the page's header). */
|
|
29
|
+
export function callerLabel(caller) {
|
|
30
|
+
switch (caller.kind) {
|
|
31
|
+
case 'owner':
|
|
32
|
+
return 'you';
|
|
33
|
+
case 'windows':
|
|
34
|
+
return 'Windows user ' + caller.user;
|
|
35
|
+
case 'device':
|
|
36
|
+
return 'device ' + caller.name;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/** The refusal text for a caller that may not. */
|
|
40
|
+
export function refusalText(caller, need) {
|
|
41
|
+
if (need === 'owner')
|
|
42
|
+
return 'Only the owner of this PC can do that, at the PC.';
|
|
43
|
+
return callerAccess(caller) === 'view' ? 'Your access is view only.' : 'You may not do that.';
|
|
44
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { z } from 'zod';
|
|
2
|
+
import type { AccessRule } from './access.js';
|
|
2
3
|
import type { OperationIO } from './io.js';
|
|
3
|
-
import type { Page } from './view.js';
|
|
4
|
+
import type { Page, Target } from './view.js';
|
|
4
5
|
export type SurfaceName = 'cli' | 'menu' | 'web';
|
|
5
6
|
export declare const SURFACE_NAMES: readonly SurfaceName[];
|
|
6
7
|
/** Where a definition is offered: everywhere, or everywhere but these surfaces (each with the reason). */
|
|
@@ -63,6 +64,11 @@ interface Definition<Id extends string, I extends z.ZodType, O extends z.ZodType
|
|
|
63
64
|
readonly output: O;
|
|
64
65
|
/** Default: every surface. */
|
|
65
66
|
readonly surfaces?: Surfaces;
|
|
67
|
+
/**
|
|
68
|
+
* What a caller other than the owner needs (docs/design/remote-access.md). Default (fails closed): view for a query,
|
|
69
|
+
* owner for an operation; an operation says 'control' to be open to someone the owner gave control.
|
|
70
|
+
*/
|
|
71
|
+
readonly access?: AccessRule;
|
|
66
72
|
readonly cli?: CliHint;
|
|
67
73
|
/** The output in one text: what an operation did, or a value a query read (CLI output, menu notice, web note). */
|
|
68
74
|
text?(output: z.output<O>): string;
|
|
@@ -77,6 +83,13 @@ export interface OperationDef<I extends z.ZodType = z.ZodType, O extends z.ZodTy
|
|
|
77
83
|
readonly kind: 'operation';
|
|
78
84
|
/** Shown as dangerous (red); it asks for its own confirmation through io.confirm. */
|
|
79
85
|
readonly danger?: boolean;
|
|
86
|
+
/** Its result must stay on screen until dismissed (a one-time code to type elsewhere): the web keeps it open. */
|
|
87
|
+
readonly keepResult?: boolean;
|
|
88
|
+
/**
|
|
89
|
+
* Where every surface goes once it succeeded: a new session's terminal is attached on the command line and in the
|
|
90
|
+
* menu, and opened in the web UI.
|
|
91
|
+
*/
|
|
92
|
+
opens?(output: z.output<O>): Target | undefined;
|
|
80
93
|
}
|
|
81
94
|
export interface QueryDef<I extends z.ZodType = z.ZodType, O extends z.ZodType = z.ZodType, Id extends string = string> extends Definition<Id, I, O> {
|
|
82
95
|
readonly kind: 'query';
|
package/dist/app/kernel/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// The kernel of the shared core: pure (zod and nothing of Node, the terminal or the DOM), so the CLI, the menu, the
|
|
2
2
|
// web server and the browser all import the same definitions, view model, form fields, questions and RPC messages.
|
|
3
|
+
export * from './access.js';
|
|
3
4
|
export * from './define.js';
|
|
4
5
|
export * from './errors.js';
|
|
5
6
|
export * from './fields.js';
|
package/dist/app/kernel/rpc.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import type { Access, Caller } from './access.js';
|
|
2
3
|
import { type OperationIO } from './io.js';
|
|
3
4
|
/** One call or subscription on the socket (chosen by the browser, unique while it runs). */
|
|
4
5
|
export declare const RpcIdSchema: z.ZodString;
|
|
@@ -168,6 +169,8 @@ export interface RpcHost {
|
|
|
168
169
|
kind(id: string): 'operation' | 'query' | undefined;
|
|
169
170
|
/** How often a query is read again while subscribed (undefined: once). */
|
|
170
171
|
liveMs(id: string): number | undefined;
|
|
171
|
-
/**
|
|
172
|
-
|
|
172
|
+
/** What a caller other than the owner needs to run it with this input (undefined: not here). */
|
|
173
|
+
need(id: string, input: unknown): Access | undefined;
|
|
174
|
+
/** Runs an allowed definition (input and output checked); refused when `caller` (default: the owner) may not. */
|
|
175
|
+
run(id: string, input: unknown, io: OperationIO, caller?: Caller): Promise<unknown>;
|
|
173
176
|
}
|
package/dist/app/registry.d.ts
CHANGED
|
@@ -6,5 +6,8 @@ export declare function implementationOf(id: string): Implementation<AnyDefiniti
|
|
|
6
6
|
export declare function runDefinition(id: string, ctx: AppContext, input: unknown, io: OperationIO): Promise<unknown>;
|
|
7
7
|
/** Reads a query in-process (the menu): typed output, and no questions. */
|
|
8
8
|
export declare function readQuery<D extends QueryDef>(def: D, ctx: AppContext, input?: unknown): Promise<OutputOf<D>>;
|
|
9
|
-
/**
|
|
9
|
+
/**
|
|
10
|
+
* What the web server may run: the registered definitions whose surfaces include the web, each refused to a caller
|
|
11
|
+
* whose role does not reach what it needs (docs/design/remote-access.md).
|
|
12
|
+
*/
|
|
10
13
|
export declare function createRpcHost(ctx: AppContext): RpcHost;
|
package/dist/app/registry.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { fail } from '../core/util/errors.js';
|
|
2
|
+
import { accessNeeded, allows, invoke, OWNER, refusalText, NO_QUESTIONS, onSurface, } from './kernel/index.js';
|
|
2
3
|
import { addAgentRun, listAgentsRun, makeDefaultAgentRun, removeAgentRun, startAgentRun, } from './features/agents/index.js';
|
|
3
4
|
import { defaultAccountRun, listAccountsRun, removeAccountRun, renameAccountRun, saveAccountRun, useAccountRun, } from './features/accounts/index.js';
|
|
4
5
|
import { listAppsRun, requestAppRun, setCustomAppsRun, toggleAppRun } from './features/apps/index.js';
|
|
@@ -10,13 +11,14 @@ import { checkHealthRun, enableSandboxRun, lastHealthRun, repairHealthRun } from
|
|
|
10
11
|
import { hideWindowRun, sandboxStatusRun, showWindowRun, stopSandboxRun } from './features/sandbox/index.js';
|
|
11
12
|
import { listLogsRun, tailLogRun } from './features/logs/index.js';
|
|
12
13
|
import { listAuditRun } from './features/audit/index.js';
|
|
13
|
-
import { listSessionsRun, restartSessionsRun } from './features/sessions/index.js';
|
|
14
|
+
import { closeSessionRun, listSessionsRun, restartSessionsRun, startShellRun } from './features/sessions/index.js';
|
|
14
15
|
import { exportConfigRun, getSettingRun, importConfigRun, listSettingsRun, resetSettingRun, setSettingRun, } from './features/settings/index.js';
|
|
15
16
|
import { migrateSettingsRun, runUpdateRun, updatesStatusRun } from './features/updates/index.js';
|
|
16
17
|
import { checkEngineRun, cleanEngineRun, engineLeftoversRun, engineStatusRun } from './features/engine/index.js';
|
|
17
18
|
import { openWebRun, startWebRun, stopWebRun, webStatusRun } from './features/web/index.js';
|
|
18
19
|
import { vpnLoginRun, vpnLogoutRun, vpnStatusRun } from './features/vpn/index.js';
|
|
19
20
|
import { githubLoginRun, githubLogoutRun, githubStatusRun } from './features/github/index.js';
|
|
21
|
+
import { accessStatusRun, allowDeviceRun, lanOffRun, lanOnRun, revokeAccessRun, revokeAllAccessRun, } from './features/access/index.js';
|
|
20
22
|
export const IMPLEMENTATIONS = [
|
|
21
23
|
sandboxStatusRun,
|
|
22
24
|
showWindowRun,
|
|
@@ -24,6 +26,8 @@ export const IMPLEMENTATIONS = [
|
|
|
24
26
|
stopSandboxRun,
|
|
25
27
|
listSessionsRun,
|
|
26
28
|
restartSessionsRun,
|
|
29
|
+
startShellRun,
|
|
30
|
+
closeSessionRun,
|
|
27
31
|
startAgentRun,
|
|
28
32
|
addAgentRun,
|
|
29
33
|
makeDefaultAgentRun,
|
|
@@ -88,6 +92,12 @@ export const IMPLEMENTATIONS = [
|
|
|
88
92
|
githubLoginRun,
|
|
89
93
|
githubLogoutRun,
|
|
90
94
|
webStatusRun,
|
|
95
|
+
accessStatusRun,
|
|
96
|
+
allowDeviceRun,
|
|
97
|
+
revokeAccessRun,
|
|
98
|
+
revokeAllAccessRun,
|
|
99
|
+
lanOnRun,
|
|
100
|
+
lanOffRun,
|
|
91
101
|
];
|
|
92
102
|
export function implementationOf(id) {
|
|
93
103
|
return IMPLEMENTATIONS.find((i) => i.def.id === id);
|
|
@@ -103,7 +113,10 @@ export function runDefinition(id, ctx, input, io) {
|
|
|
103
113
|
export async function readQuery(def, ctx, input = {}) {
|
|
104
114
|
return (await runDefinition(def.id, ctx, input, NO_QUESTIONS));
|
|
105
115
|
}
|
|
106
|
-
/**
|
|
116
|
+
/**
|
|
117
|
+
* What the web server may run: the registered definitions whose surfaces include the web, each refused to a caller
|
|
118
|
+
* whose role does not reach what it needs (docs/design/remote-access.md).
|
|
119
|
+
*/
|
|
107
120
|
export function createRpcHost(ctx) {
|
|
108
121
|
const allowed = (id) => {
|
|
109
122
|
const impl = implementationOf(id);
|
|
@@ -115,10 +128,17 @@ export function createRpcHost(ctx) {
|
|
|
115
128
|
const def = allowed(id)?.def;
|
|
116
129
|
return def?.kind === 'query' ? def.live?.everyMs : undefined;
|
|
117
130
|
},
|
|
118
|
-
|
|
131
|
+
need: (id, input) => {
|
|
132
|
+
const impl = allowed(id);
|
|
133
|
+
return impl ? accessNeeded(impl.def, input) : undefined;
|
|
134
|
+
},
|
|
135
|
+
run: async (id, input, io, caller = OWNER) => {
|
|
119
136
|
const impl = allowed(id);
|
|
120
137
|
if (!impl)
|
|
121
138
|
throw new Error('Not allowed on the web: ' + id);
|
|
139
|
+
const need = accessNeeded(impl.def, input);
|
|
140
|
+
if (!allows(caller, need))
|
|
141
|
+
fail(refusalText(caller, need));
|
|
122
142
|
return invoke(impl, ctx, input, io);
|
|
123
143
|
},
|
|
124
144
|
};
|
|
@@ -1,3 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { createRequire as __createRequire } from 'node:module'; const require = __createRequire(import.meta.url);
|
|
3
|
+
import {
|
|
4
|
+
run
|
|
5
|
+
} from "./chunk-K4SU3MHJ.js";
|
|
6
|
+
import "./chunk-VENWSGX2.js";
|
|
7
|
+
|
|
8
|
+
// src/bin/agent-sandbox.ts
|
|
3
9
|
run(process.argv.slice(2));
|