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.
Files changed (246) hide show
  1. package/README.md +13 -6
  2. package/config.example.json +15 -4
  3. package/dist/app/features/access/access.def.d.ts +190 -0
  4. package/dist/app/features/access/access.def.js +273 -0
  5. package/dist/app/features/access/access.run.d.ts +79 -0
  6. package/dist/app/features/access/access.run.js +129 -0
  7. package/dist/app/features/access/index.d.ts +2 -0
  8. package/dist/app/features/access/index.js +3 -0
  9. package/dist/app/features/agents/agent-entry.js +5 -0
  10. package/dist/app/features/agents/agents.def.js +1 -0
  11. package/dist/app/features/apps/apps-rows.d.ts +4 -1
  12. package/dist/app/features/apps/apps-rows.js +7 -2
  13. package/dist/app/features/apps/apps.def.d.ts +19 -0
  14. package/dist/app/features/apps/apps.def.js +50 -12
  15. package/dist/app/features/apps/apps.run.d.ts +2 -0
  16. package/dist/app/features/apps/apps.run.js +1 -1
  17. package/dist/app/features/audit/audit.def.d.ts +350 -0
  18. package/dist/app/features/audit/audit.def.js +457 -0
  19. package/dist/app/features/audit/audit.run.d.ts +110 -0
  20. package/dist/app/features/audit/audit.run.js +133 -0
  21. package/dist/app/features/audit/index.d.ts +2 -0
  22. package/dist/app/features/audit/index.js +3 -0
  23. package/dist/app/features/defs.d.ts +349 -4
  24. package/dist/app/features/defs.js +16 -1
  25. package/dist/app/features/files/files.def.js +1 -0
  26. package/dist/app/features/health/health.def.js +1 -0
  27. package/dist/app/features/sandbox/sandbox.def.js +3 -0
  28. package/dist/app/features/sessions/sessions.def.d.ts +36 -0
  29. package/dist/app/features/sessions/sessions.def.js +77 -9
  30. package/dist/app/features/sessions/sessions.run.d.ts +25 -2
  31. package/dist/app/features/sessions/sessions.run.js +71 -3
  32. package/dist/app/features/settings/settings.def.d.ts +8 -0
  33. package/dist/app/features/settings/settings.def.js +17 -0
  34. package/dist/app/features/settings/settings.run.d.ts +3 -0
  35. package/dist/app/features/settings/settings.run.js +16 -2
  36. package/dist/app/features/web/web.def.d.ts +9 -0
  37. package/dist/app/features/web/web.def.js +26 -1
  38. package/dist/app/features/web/web.run.d.ts +3 -0
  39. package/dist/app/features/web/web.run.js +11 -3
  40. package/dist/app/kernel/access.d.ts +33 -0
  41. package/dist/app/kernel/access.js +44 -0
  42. package/dist/app/kernel/define.d.ts +26 -2
  43. package/dist/app/kernel/index.d.ts +1 -0
  44. package/dist/app/kernel/index.js +1 -0
  45. package/dist/app/kernel/rpc.d.ts +5 -2
  46. package/dist/app/registry.d.ts +4 -1
  47. package/dist/app/registry.js +26 -4
  48. package/dist/cli/args.d.ts +0 -3
  49. package/dist/cli/args.js +3 -4
  50. package/dist/cli/commands/index.d.ts +1 -1
  51. package/dist/cli/commands/index.js +1 -1
  52. package/dist/cli/commands/launch/prepare.js +1 -1
  53. package/dist/cli/commands/maintenance/index.d.ts +0 -1
  54. package/dist/cli/commands/maintenance/index.js +0 -1
  55. package/dist/cli/commands/registry.js +3 -2
  56. package/dist/cli/commands/remote.d.ts +2 -0
  57. package/dist/cli/commands/remote.js +49 -0
  58. package/dist/cli/commands/sessions/reopen.js +3 -0
  59. package/dist/cli/commands/web.js +66 -0
  60. package/dist/cli/manifest.d.ts +63 -45
  61. package/dist/cli/manifest.js +33 -18
  62. package/dist/cli/menu/io-menu.d.ts +5 -3
  63. package/dist/cli/menu/io-menu.js +12 -5
  64. package/dist/cli/menu/pages/apps-page.js +2 -15
  65. package/dist/cli/menu/pages/maintenance-page.d.ts +4 -0
  66. package/dist/cli/menu/pages/maintenance-page.js +51 -8
  67. package/dist/cli/menu/remote-menu.d.ts +19 -0
  68. package/dist/cli/menu/remote-menu.js +108 -0
  69. package/dist/cli/menu/views.js +4 -0
  70. package/dist/cli/operations/handler.js +15 -1
  71. package/dist/cli/services/remote-client.d.ts +16 -0
  72. package/dist/cli/services/remote-client.js +141 -0
  73. package/dist/cli/services/sessions-service.d.ts +5 -0
  74. package/dist/cli/services/sessions-service.js +17 -1
  75. package/dist/core/config/defaults.js +12 -2
  76. package/dist/core/local-config/file.js +16 -2
  77. package/dist/core/local-config/overrides.js +7 -0
  78. package/dist/core/run/versions.js +9 -8
  79. package/dist/core/schema/apps.d.ts +9 -0
  80. package/dist/core/schema/apps.js +9 -0
  81. package/dist/core/schema/config.d.ts +23 -3
  82. package/dist/core/schema/config.js +42 -5
  83. package/dist/core/schema/sandbox.d.ts +1 -0
  84. package/dist/core/schema/sandbox.js +11 -3
  85. package/dist/core/schema/web.d.ts +51 -0
  86. package/dist/core/schema/web.js +46 -1
  87. package/dist/core/util/fs.d.ts +5 -0
  88. package/dist/core/util/fs.js +23 -0
  89. package/dist/core/util/index.d.ts +1 -1
  90. package/dist/core/util/index.js +1 -1
  91. package/dist/guest/apps/app-installer.js +50 -4
  92. package/dist/guest/apps/index.d.ts +1 -1
  93. package/dist/guest/apps/index.js +1 -1
  94. package/dist/guest/apps/sources.js +1 -1
  95. package/dist/guest/audit/parse.d.ts +32 -2
  96. package/dist/guest/audit/parse.js +43 -5
  97. package/dist/guest/audit/process-audit.d.ts +3 -1
  98. package/dist/guest/audit/process-audit.js +9 -6
  99. package/dist/guest/boot/boot.js +12 -3
  100. package/dist/guest/boot/guide.d.ts +1 -1
  101. package/dist/guest/boot/guide.js +27 -3
  102. package/dist/guest/daemon/daemon.js +2 -0
  103. package/dist/guest/daemon/idle-shutdown.js +2 -1
  104. package/dist/guest/daemon/sessions.d.ts +23 -2
  105. package/dist/guest/daemon/sessions.js +151 -20
  106. package/dist/guest/entries/daemon.js +2 -1
  107. package/dist/guest/git/decision.js +7 -0
  108. package/dist/guest/sessions/cmdline.d.ts +14 -0
  109. package/dist/guest/sessions/cmdline.js +10 -0
  110. package/dist/guest/setup/addon-mcp.d.ts +2 -0
  111. package/dist/guest/setup/addon-mcp.js +49 -0
  112. package/dist/guest/setup/agent-addons.d.ts +9 -0
  113. package/dist/guest/setup/agent-addons.js +22 -0
  114. package/dist/guest/setup/headroom.d.ts +7 -0
  115. package/dist/guest/setup/headroom.js +60 -0
  116. package/dist/guest/setup/managed-policy.d.ts +2 -1
  117. package/dist/guest/setup/managed-policy.js +15 -6
  118. package/dist/guest/toolchains/plan.js +8 -3
  119. package/dist/guest/toolchains/setup.js +3 -0
  120. package/dist/host/audit/attribute.d.ts +46 -0
  121. package/dist/host/audit/attribute.js +76 -0
  122. package/dist/host/audit/collect.d.ts +29 -3
  123. package/dist/host/audit/collect.js +256 -61
  124. package/dist/host/audit/git-commits.d.ts +29 -0
  125. package/dist/host/audit/git-commits.js +157 -0
  126. package/dist/host/audit/index.d.ts +5 -0
  127. package/dist/host/audit/index.js +6 -0
  128. package/dist/host/audit/log.d.ts +45 -7
  129. package/dist/host/audit/log.js +148 -45
  130. package/dist/host/audit/redact.d.ts +2 -0
  131. package/dist/host/audit/redact.js +67 -0
  132. package/dist/host/channel/channel.d.ts +5 -1
  133. package/dist/host/channel/channel.js +12 -0
  134. package/dist/host/guard/agent-files.js +7 -1
  135. package/dist/host/guard/commands.js +13 -1
  136. package/dist/host/guard/git-config.js +27 -7
  137. package/dist/host/guard/git-dirs.js +78 -3
  138. package/dist/host/guard/mark-of-the-web.js +3 -1
  139. package/dist/host/guard/nested-repos.js +7 -2
  140. package/dist/host/guard/store.js +1 -0
  141. package/dist/host/guard/types.d.ts +2 -0
  142. package/dist/host/launch/mappings.js +11 -1
  143. package/dist/host/launch/project-scan.js +2 -21
  144. package/dist/host/launch/stage.js +4 -0
  145. package/dist/host/life/session-ledger.js +2 -1
  146. package/dist/host/remote/access-store.d.ts +156 -0
  147. package/dist/host/remote/access-store.js +308 -0
  148. package/dist/host/remote/cert.d.ts +20 -0
  149. package/dist/host/remote/cert.js +103 -0
  150. package/dist/host/remote/door-client.d.ts +13 -0
  151. package/dist/host/remote/door-client.js +54 -0
  152. package/dist/host/remote/firewall.d.ts +44 -0
  153. package/dist/host/remote/firewall.js +118 -0
  154. package/dist/host/remote/gate.d.ts +125 -0
  155. package/dist/host/remote/gate.js +391 -0
  156. package/dist/host/remote/index.d.ts +8 -0
  157. package/dist/host/remote/index.js +10 -0
  158. package/dist/host/remote/lan.d.ts +24 -0
  159. package/dist/host/remote/lan.js +88 -0
  160. package/dist/host/remote/tls-store.d.ts +22 -0
  161. package/dist/host/remote/tls-store.js +63 -0
  162. package/dist/host/remote/windows-helper.d.ts +67 -0
  163. package/dist/host/remote/windows-helper.js +129 -0
  164. package/dist/host/web/bridge.d.ts +5 -1
  165. package/dist/host/web/bridge.js +6 -2
  166. package/dist/host/web/browser.js +1 -1
  167. package/dist/host/web/control.d.ts +4 -1
  168. package/dist/host/web/control.js +28 -4
  169. package/dist/host/web/rpc.d.ts +16 -2
  170. package/dist/host/web/rpc.js +37 -4
  171. package/dist/host/web/runner.d.ts +4 -0
  172. package/dist/host/web/runner.js +12 -0
  173. package/dist/host/web/server.d.ts +18 -2
  174. package/dist/host/web/server.js +97 -24
  175. package/dist/host/web/state.d.ts +5 -0
  176. package/dist/host/web/state.js +8 -0
  177. package/dist/runtime/agent.js +184 -18
  178. package/dist/runtime/daemon.js +772 -338
  179. package/dist/runtime/db.js +184 -18
  180. package/dist/runtime/gitguard.js +6 -0
  181. package/dist/runtime/main.js +966 -553
  182. package/dist/runtime/netproxy.js +184 -18
  183. package/dist/runtime/view.js +186 -18
  184. package/dist/selftest/app.test.js +66 -7
  185. package/dist/selftest/audit.test.js +354 -47
  186. package/dist/selftest/channel.test.js +16 -0
  187. package/dist/selftest/cmdline.test.js +16 -0
  188. package/dist/selftest/git-access.test.js +8 -0
  189. package/dist/selftest/hcs.test.js +35 -0
  190. package/dist/selftest/hostile-input.test.js +12 -1
  191. package/dist/selftest/index.js +2 -0
  192. package/dist/selftest/live-daemon.test.js +25 -0
  193. package/dist/selftest/local-config.test.js +30 -0
  194. package/dist/selftest/menu-screens.js +1 -1
  195. package/dist/selftest/menu.test.js +3 -3
  196. package/dist/selftest/remote.test.d.ts +2 -0
  197. package/dist/selftest/remote.test.js +536 -0
  198. package/dist/selftest/repo-guard.test.js +56 -2
  199. package/dist/selftest/resume.test.js +2 -1
  200. package/dist/selftest/sharing.test.js +9 -2
  201. package/dist/selftest/support/engine-golden.js +9 -0
  202. package/dist/selftest/support/fixtures.d.ts +14 -0
  203. package/dist/selftest/support/web-preview.js +157 -0
  204. package/dist/selftest/toolchains.test.js +147 -2
  205. package/dist/selftest/web.test.js +2 -0
  206. package/dist/shared/apps/agent-addons.d.ts +64 -0
  207. package/dist/shared/apps/agent-addons.js +95 -0
  208. package/dist/shared/apps/catalogue.d.ts +33 -2
  209. package/dist/shared/apps/catalogue.js +102 -11
  210. package/dist/shared/apps/index.d.ts +3 -2
  211. package/dist/shared/apps/index.js +3 -2
  212. package/dist/shared/apps/selection.d.ts +2 -0
  213. package/dist/shared/apps/selection.js +7 -0
  214. package/dist/shared/channel/schema.d.ts +23 -0
  215. package/dist/shared/channel/schema.js +8 -0
  216. package/dist/shared/pty/protocol.d.ts +2 -0
  217. package/dist/shared/pty/protocol.js +2 -0
  218. package/dist/sync/watcher.js +9 -1
  219. package/dist/web/index.html +2 -2
  220. package/dist/web/{main-RQFN4WBY.css → main-UAQPI5XD.css} +1 -1
  221. package/dist/web/main-Z2WYAE2Z.js +221 -0
  222. package/native/csharp/HcsHost/AssemblyInfo.cs +5 -3
  223. package/native/csharp/HcsHost/Hcs.cs +9 -0
  224. package/native/csharp/HcsHost/Program.cs +26 -1
  225. package/native/csharp/HcsHost/Share.cs +173 -0
  226. package/native/csharp/HostTools/AssemblyInfo.cs +3 -3
  227. package/native/csharp/HostTools/Program.cs +17 -1
  228. package/native/csharp/HostTools/WebHelper.cs +499 -0
  229. package/native/scripts/get-network-profile.ps1 +19 -0
  230. package/native/scripts/set-folder-access.ps1 +11 -0
  231. package/native/scripts/set-web-firewall.ps1 +103 -0
  232. package/package.json +1 -1
  233. package/sandbox/addons/ccusage/package-lock.json +111 -0
  234. package/sandbox/addons/ccusage/package.json +7 -0
  235. package/sandbox/addons/context7/package-lock.json +1313 -0
  236. package/sandbox/addons/context7/package.json +7 -0
  237. package/sandbox/addons/headroom/requirements.in +1 -0
  238. package/sandbox/addons/headroom/requirements.txt +2922 -0
  239. package/sandbox/addons/playwright-mcp/package-lock.json +56 -0
  240. package/sandbox/addons/playwright-mcp/package.json +7 -0
  241. package/sandbox/csharp/PtyHost.cs +20 -0
  242. package/schemas/config.schema.json +78 -4
  243. package/schemas/sandbox-config.schema.json +2 -20
  244. package/dist/cli/commands/maintenance/audit.d.ts +0 -3
  245. package/dist/cli/commands/maintenance/audit.js +0 -58
  246. 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
- // Agent sessions of the running sandbox: list, pick one to attach to, restart. Returns data, never prints.
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: false, days: 14, maxMb: 100, processes: true, files: true },
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: { enabled: false, port: 47800, openOnLaunch: false },
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
- const raw = fs.readFileSync(file);
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
- if (!fileExists(file))
7
+ const text = fileExists(file) ? readSmallFile(file, 64 * 1024) : null;
8
+ if (text === null)
7
9
  return null;
8
- const line = fs
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 ?? null;
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(fs.readFileSync(file, 'utf8').replace(/^\uFEFF/, ''))).data?.volta?.node ??
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;
@@ -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 and paging file (this PC's disk) are not a project's to set.
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: AppsSchema.pick({ install: true }).partial(),
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,
@@ -49,6 +49,7 @@ export declare const AuditSchema: z.ZodObject<{
49
49
  maxMb: z.ZodNumber;
50
50
  processes: z.ZodBoolean;
51
51
  files: z.ZodBoolean;
52
+ commits: z.ZodBoolean;
52
53
  }, z.core.$strict>;
53
54
  export declare const WorkspaceFilterSchema: z.ZodObject<{
54
55
  enabled: z.ZodBoolean;
@@ -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 happened in your sandboxes: programs started (with their command lines), files changed in the project, sessions, and what the repo guard caught. The sandbox cannot change it. Read it with sandbox audit.'),
112
- days: z.number().int().min(1).max(365).describe('Days the audit log is kept.'),
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 project while the sandbox runs.'),
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>;