windows-development-sandbox 1.23.1 → 1.25.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 (85) hide show
  1. package/config.example.json +0 -1
  2. package/dist/bin/agent-sandbox.js +2 -2
  3. package/dist/bin/{chunk-USTRSHIE.js → chunk-IZRGTSQ5.js} +158 -131
  4. package/dist/bin/{chunk-QFSJX2KW.js → chunk-KH4MI2U5.js} +1 -1
  5. package/dist/bin/{chunk-7YPEDAJD.js → chunk-SUYYVIDW.js} +1 -1
  6. package/dist/bin/claude-host.js +2 -2
  7. package/dist/bin/claude-sbx.js +2 -2
  8. package/dist/bin/codex-sbx.js +2 -2
  9. package/dist/bin/gemini-sbx.js +2 -2
  10. package/dist/bin/sandbox.js +2 -2
  11. package/dist/bin/sbx-hide.js +2 -2
  12. package/dist/bin/sbx-show.js +2 -2
  13. package/dist/core/config/defaults.js +0 -1
  14. package/dist/core/detect/toolchains.js +0 -6
  15. package/dist/core/schema/config.d.ts +1 -2
  16. package/dist/core/schema/toolchains.d.ts +1 -2
  17. package/dist/core/schema/toolchains.js +3 -6
  18. package/dist/guest/agents/agent-files.d.ts +11 -7
  19. package/dist/guest/agents/agent-files.js +37 -11
  20. package/dist/guest/agents/local-state.d.ts +2 -1
  21. package/dist/guest/agents/local-state.js +2 -1
  22. package/dist/guest/boot/boot-console.d.ts +28 -0
  23. package/dist/guest/boot/boot-console.js +98 -0
  24. package/dist/guest/boot/boot.d.ts +8 -1
  25. package/dist/guest/boot/boot.js +66 -42
  26. package/dist/guest/boot/graph.d.ts +9 -5
  27. package/dist/guest/boot/graph.js +8 -2
  28. package/dist/guest/boot/progress-view.d.ts +116 -0
  29. package/dist/guest/boot/progress-view.js +456 -0
  30. package/dist/guest/boot/runner.d.ts +20 -10
  31. package/dist/guest/boot/runner.js +39 -47
  32. package/dist/guest/runtime/log-line.d.ts +7 -0
  33. package/dist/guest/runtime/log-line.js +18 -0
  34. package/dist/guest/runtime/log.d.ts +17 -4
  35. package/dist/guest/runtime/log.js +40 -17
  36. package/dist/guest/runtime/process.d.ts +9 -0
  37. package/dist/guest/runtime/process.js +69 -8
  38. package/dist/guest/setup/environment.js +9 -4
  39. package/dist/guest/setup/hooks.d.ts +2 -0
  40. package/dist/guest/setup/hooks.js +4 -0
  41. package/dist/guest/setup/path-plan.d.ts +12 -0
  42. package/dist/guest/setup/path-plan.js +18 -0
  43. package/dist/guest/toolchains/native/unreal.js +4 -6
  44. package/dist/guest/toolchains/native/visual-studio.d.ts +14 -14
  45. package/dist/guest/toolchains/native/visual-studio.js +145 -174
  46. package/dist/guest/toolchains/native/vs-persist.d.ts +189 -0
  47. package/dist/guest/toolchains/native/vs-persist.js +256 -0
  48. package/dist/guest/toolchains/native/vs-request.d.ts +6 -22
  49. package/dist/guest/toolchains/native/vs-request.js +16 -46
  50. package/dist/guest/toolchains/plan.js +11 -14
  51. package/dist/host/migrate/migrations/drop-vs-install-files.d.ts +5 -0
  52. package/dist/host/migrate/migrations/drop-vs-install-files.js +34 -0
  53. package/dist/host/migrate/registry.js +7 -1
  54. package/dist/runtime/agent.js +67 -26
  55. package/dist/runtime/daemon.js +958 -716
  56. package/dist/runtime/db.js +67 -26
  57. package/dist/runtime/main.js +1792 -1038
  58. package/dist/runtime/netproxy.js +3 -6
  59. package/dist/runtime/tool.js +44 -24
  60. package/dist/runtime/view.js +4 -7
  61. package/dist/selftest/agent-paths.test.js +17 -1
  62. package/dist/selftest/agent-settings.test.js +36 -5
  63. package/dist/selftest/boot-console.test.d.ts +2 -0
  64. package/dist/selftest/boot-console.test.js +291 -0
  65. package/dist/selftest/boot-runner.test.js +10 -12
  66. package/dist/selftest/guest-boot.test.js +7 -1
  67. package/dist/selftest/index.js +2 -0
  68. package/dist/selftest/layout.test.js +4 -0
  69. package/dist/selftest/migrate.test.js +21 -0
  70. package/dist/selftest/persisted-installs.test.js +169 -32
  71. package/dist/selftest/support/engine-golden.js +2 -0
  72. package/dist/selftest/support/fixtures.d.ts +1 -2
  73. package/dist/shared/channel/schema.d.ts +3 -6
  74. package/dist/shared/pty/terminal.js +2 -1
  75. package/dist/sync/watcher.js +5 -1
  76. package/native/csharp/HcsHost/AssemblyInfo.cs +3 -3
  77. package/native/csharp/HostTools/AssemblyInfo.cs +3 -3
  78. package/native/csharp/StorageHelper/AssemblyInfo.cs +3 -3
  79. package/package.json +1 -1
  80. package/sandbox/scripts/restore-visual-studio.ps1 +78 -0
  81. package/sandbox/scripts/save-visual-studio.ps1 +106 -0
  82. package/schemas/config.schema.json +3 -7
  83. package/schemas/sandbox-config.schema.json +3 -7
  84. package/dist/guest/toolchains/native/autosdk.d.ts +0 -3
  85. package/dist/guest/toolchains/native/autosdk.js +0 -50
@@ -1,7 +1,7 @@
1
1
  import { createRequire as __createRequire } from 'node:module'; const require = __createRequire(import.meta.url);
2
2
  import {
3
3
  attach
4
- } from "./chunk-USTRSHIE.js";
4
+ } from "./chunk-IZRGTSQ5.js";
5
5
  export {
6
6
  attach
7
7
  };
@@ -251,7 +251,7 @@ import {
251
251
  withOperationCommands,
252
252
  working,
253
253
  writeJson
254
- } from "./chunk-USTRSHIE.js";
254
+ } from "./chunk-IZRGTSQ5.js";
255
255
 
256
256
  // node_modules/commander/lib/error.js
257
257
  var require_error = __commonJS({
@@ -2,8 +2,8 @@
2
2
  import { createRequire as __createRequire } from 'node:module'; const require = __createRequire(import.meta.url);
3
3
  import {
4
4
  run
5
- } from "./chunk-7YPEDAJD.js";
6
- import "./chunk-USTRSHIE.js";
5
+ } from "./chunk-SUYYVIDW.js";
6
+ import "./chunk-IZRGTSQ5.js";
7
7
 
8
8
  // src/bin/claude-host.ts
9
9
  var args = process.argv.slice(2);
@@ -2,8 +2,8 @@
2
2
  import { createRequire as __createRequire } from 'node:module'; const require = __createRequire(import.meta.url);
3
3
  import {
4
4
  run
5
- } from "./chunk-7YPEDAJD.js";
6
- import "./chunk-USTRSHIE.js";
5
+ } from "./chunk-SUYYVIDW.js";
6
+ import "./chunk-IZRGTSQ5.js";
7
7
 
8
8
  // src/bin/claude-sbx.ts
9
9
  run(process.argv.slice(2), { harness: "claude" });
@@ -2,8 +2,8 @@
2
2
  import { createRequire as __createRequire } from 'node:module'; const require = __createRequire(import.meta.url);
3
3
  import {
4
4
  run
5
- } from "./chunk-7YPEDAJD.js";
6
- import "./chunk-USTRSHIE.js";
5
+ } from "./chunk-SUYYVIDW.js";
6
+ import "./chunk-IZRGTSQ5.js";
7
7
 
8
8
  // src/bin/codex-sbx.ts
9
9
  run(process.argv.slice(2), { harness: "codex" });
@@ -2,8 +2,8 @@
2
2
  import { createRequire as __createRequire } from 'node:module'; const require = __createRequire(import.meta.url);
3
3
  import {
4
4
  run
5
- } from "./chunk-7YPEDAJD.js";
6
- import "./chunk-USTRSHIE.js";
5
+ } from "./chunk-SUYYVIDW.js";
6
+ import "./chunk-IZRGTSQ5.js";
7
7
 
8
8
  // src/bin/gemini-sbx.ts
9
9
  run(process.argv.slice(2), { harness: "gemini" });
@@ -2,8 +2,8 @@
2
2
  import { createRequire as __createRequire } from 'node:module'; const require = __createRequire(import.meta.url);
3
3
  import {
4
4
  run
5
- } from "./chunk-7YPEDAJD.js";
6
- import "./chunk-USTRSHIE.js";
5
+ } from "./chunk-SUYYVIDW.js";
6
+ import "./chunk-IZRGTSQ5.js";
7
7
 
8
8
  // src/bin/sandbox.ts
9
9
  run(process.argv.slice(2));
@@ -2,8 +2,8 @@
2
2
  import { createRequire as __createRequire } from 'node:module'; const require = __createRequire(import.meta.url);
3
3
  import {
4
4
  run
5
- } from "./chunk-7YPEDAJD.js";
6
- import "./chunk-USTRSHIE.js";
5
+ } from "./chunk-SUYYVIDW.js";
6
+ import "./chunk-IZRGTSQ5.js";
7
7
 
8
8
  // src/bin/sbx-hide.ts
9
9
  run(process.argv.slice(2), { command: "hide" });
@@ -2,8 +2,8 @@
2
2
  import { createRequire as __createRequire } from 'node:module'; const require = __createRequire(import.meta.url);
3
3
  import {
4
4
  run
5
- } from "./chunk-7YPEDAJD.js";
6
- import "./chunk-USTRSHIE.js";
5
+ } from "./chunk-SUYYVIDW.js";
6
+ import "./chunk-IZRGTSQ5.js";
7
7
 
8
8
  // src/bin/sbx-show.ts
9
9
  run(process.argv.slice(2), { command: "show" });
@@ -87,7 +87,6 @@ export const DEFAULTS = {
87
87
  enabled: 'auto',
88
88
  acceptLicense: false,
89
89
  components: [],
90
- keepInstallFiles: true,
91
90
  visualStudio: 'full',
92
91
  persistEngines: true,
93
92
  derivedDataCache: 'persisted',
@@ -127,15 +127,9 @@ function applyImplications(settings, result) {
127
127
  imply(settings, result, 'android', 'React Native builds for Android');
128
128
  if (result.godot.on && settings.godot.dotnet === true)
129
129
  imply(settings, result, 'dotnet', "Godot's .NET build");
130
- if (result.unreal.on && settings.unreal.visualStudio === 'autosdk')
131
- imply(settings, result, 'msvc', 'Unreal builds from the portable MSVC (AutoSDK)');
132
- // Unreal's licence acceptance covers the MSVC it builds from (the same Visual Studio terms).
133
- const autosdkLicence = settings.unreal.visualStudio === 'autosdk' && settings.unreal.acceptLicense;
134
130
  for (const name of LICENSED_TOOLCHAINS) {
135
131
  if (settings[name].enabled !== 'auto' || !result[name].on || settings[name].acceptLicense)
136
132
  continue;
137
- if (name === 'msvc' && autosdkLicence)
138
- continue;
139
133
  result[name] = {
140
134
  on: false,
141
135
  reason: 'would be used (' + result[name].reason + '); set toolchains.' + name + '.acceptLicense to install it',
@@ -195,10 +195,9 @@ export declare const ConfigSchema: z.ZodObject<{
195
195
  enabled: z.ZodUnion<readonly [z.ZodBoolean, z.ZodLiteral<"auto">]>;
196
196
  acceptLicense: z.ZodBoolean;
197
197
  components: z.ZodArray<z.ZodString>;
198
- keepInstallFiles: z.ZodBoolean;
199
198
  visualStudio: z.ZodEnum<{
200
199
  full: "full";
201
- autosdk: "autosdk";
200
+ buildtools: "buildtools";
202
201
  }>;
203
202
  persistEngines: z.ZodBoolean;
204
203
  derivedDataCache: z.ZodEnum<{
@@ -38,10 +38,9 @@ export declare const ToolchainsSchema: z.ZodObject<{
38
38
  enabled: z.ZodUnion<readonly [z.ZodBoolean, z.ZodLiteral<"auto">]>;
39
39
  acceptLicense: z.ZodBoolean;
40
40
  components: z.ZodArray<z.ZodString>;
41
- keepInstallFiles: z.ZodBoolean;
42
41
  visualStudio: z.ZodEnum<{
43
42
  full: "full";
44
- autosdk: "autosdk";
43
+ buildtools: "buildtools";
45
44
  }>;
46
45
  persistEngines: z.ZodBoolean;
47
46
  derivedDataCache: z.ZodEnum<{
@@ -54,17 +54,14 @@ export const ToolchainsSchema = z.strictObject({
54
54
  sdkVersion: z.string().min(1).describe('"latest" or a Windows SDK build, e.g. "26100" or "10.0.22621".'),
55
55
  }),
56
56
  unreal: z.strictObject({
57
- enabled: enabled('Unreal Engine development: Visual Studio Community 2022 with the C++ game workload (MSVC, a Windows 11 SDK, .NET Framework targeting packs, Unreal integration and the Epic Games Launcher) and the DirectX End-User Runtime. A real installer: it runs every boot (~10 GB on the sandbox disk). The engine itself comes from the Epic Games Launcher or its source.', 'a .uproject or .uplugin'),
57
+ enabled: enabled('Unreal Engine development: Visual Studio Community 2022 with the C++ game workload (MSVC, a Windows 11 SDK, .NET Framework targeting packs, Unreal integration and the Epic Games Launcher) and the DirectX End-User Runtime. Visual Studio is installed once and kept on the data disk (~6.5 GB); later boots put it back in seconds. The engine itself comes from the Epic Games Launcher or its source.', 'a .uproject or .uplugin'),
58
58
  acceptLicense: z
59
59
  .boolean()
60
60
  .describe('Required to install: you accept the Visual Studio Community licence terms (https://visualstudio.microsoft.com/license-terms/) and the DirectX End-User Runtime licence.'),
61
61
  components: StringsSchema.describe('More Visual Studio workload or component IDs to add, e.g. ["Microsoft.VisualStudio.Component.VC.Llvm.Clang"].'),
62
- keepInstallFiles: z
63
- .boolean()
64
- .describe('Keep an offline copy of the Visual Studio install files in tools\\visualstudio (~10 GB, made once), so later boots install without downloading them again. Skipped while that disk has too little room.'),
65
62
  visualStudio: z
66
- .enum(['full', 'autosdk'])
67
- .describe('"full": install Visual Studio Community (the engine finds it through the Setup API; ~10 GB, redone every boot). "autosdk" (experimental): skip Visual Studio and build an Unreal AutoSDK tree from the portable MSVC in tools\\unreal\\autosdk (kept between sandboxes); UnrealBuildTool finds MSVC through AutoSDK, but the editor\'s Visual Studio integration is not available. Needs toolchains.msvc (turned on for you).'),
63
+ .enum(['full', 'buildtools'])
64
+ .describe('"full": Visual Studio Community with the C++ game workload (the IDE, its debugger and Unreal integration). "buildtools": only the Visual Studio Build Tools (MSVC, the Windows SDK and the .NET Framework parts UnrealBuildTool needs; no IDE). Either is installed once and kept on the data disk (tools\\visualstudio\\persist, ~6.5 GB for Community); later boots put it back in seconds.'),
68
65
  persistEngines: z
69
66
  .boolean()
70
67
  .describe('Engines the Epic Games Launcher installs go to tools\\unreal\\engines (kept between sandboxes, 40-60 GB each) and are registered with the launcher again every boot. Off: they go on the sandbox disk and are lost when it closes. Engines installed on this PC can be mapped instead (pcInstalls.unrealEngines).'),
@@ -52,9 +52,10 @@ export declare function bootResetPaths(agents: Readonly<Record<string, Harness>>
52
52
  }[];
53
53
  type CodexSettings = Config['codex'];
54
54
  /**
55
- * The flags every Codex start gets. Flags beat config.toml and a resumed thread's saved permissions, which is why the
56
- * sandbox does not rely on the file. Full access with no questions is the one flag that skips Codex's Windows sandbox
57
- * setup for certain. Pure.
55
+ * The flags every Codex start through the sandbox gets (the codex.cmd shim, the daemon's sessions). Flags beat
56
+ * config.toml and a resumed thread's saved permissions; config.toml carries the same settings for a start without
57
+ * them (codexConfigOwned). Full access with no questions is the one flag that skips Codex's Windows sandbox setup for
58
+ * certain. Pure.
58
59
  */
59
60
  export declare function codexArguments(c: CodexSettings): string[];
60
61
  /** The arguments an agent always starts with: its own, and for codex the sandbox's settings. */
@@ -62,10 +63,13 @@ export declare function agentArguments(name: string, harness: Harness, codex: Co
62
63
  /** The largest config.toml the sandbox edits. */
63
64
  export declare const MAX_CODEX_CONFIG_BYTES: number;
64
65
  /**
65
- * Codex's config.toml as the sandbox wants it, every other line byte for byte: the lines an older version wrote above
66
- * the first table (sandbox_mode, approval_policy, under the marker comment) are gone, `[windows] sandbox` follows
67
- * windowsSandbox ("off" removes the line, so Codex does not set up its own sandbox users), and
68
- * mcp_oauth_credentials_store = "file" is added when it is not set. Running it again changes nothing. Pure.
66
+ * Codex's config.toml as the sandbox wants it, every other line byte for byte. Above the first table, under the
67
+ * marker comment: sandbox_mode, approval_policy (and model, model_reasoning_effort when set), so ANY start of Codex
68
+ * obeys the codex settings, also one without the shim's flags (a copy installed with npm -g). The marked lines are
69
+ * replaced each time; a key the user set on a line of their own above the first table is left to them.
70
+ * `[windows] sandbox` follows windowsSandbox ("off" removes the line, which Codex adds back after its own setup, so it
71
+ * does not set up its own sandbox users), and mcp_oauth_credentials_store = "file" is added when it is not set.
72
+ * Keeps the file's line endings; running it again changes nothing. Pure.
69
73
  */
70
74
  export declare function codexConfigOwned(toml: string, c: CodexSettings): string;
71
75
  /**
@@ -63,10 +63,17 @@ export function bootResetPaths(agents, stateDir) {
63
63
  }
64
64
  /** The marker the sandbox puts above the lines it writes into config.toml. */
65
65
  const CODEX_MARKER = '# Set by the sandbox';
66
+ /** The full marker line: what it is and where to change it. */
67
+ const CODEX_MARKER_LINE = CODEX_MARKER + ' from its codex settings (`sandbox config`); a line of your own without it wins.';
68
+ /** Markers whose lines count as the sandbox's: CODEX_MARKER, and a hand-made fix's (`# agent-sandbox:`). */
69
+ const CODEX_MARKERS = [CODEX_MARKER, '# agent-sandbox:'];
70
+ /** The top-level keys the sandbox writes under its marker. */
71
+ const CODEX_OWNED_KEYS = ['sandbox_mode', 'approval_policy', 'model', 'model_reasoning_effort'];
66
72
  /**
67
- * The flags every Codex start gets. Flags beat config.toml and a resumed thread's saved permissions, which is why the
68
- * sandbox does not rely on the file. Full access with no questions is the one flag that skips Codex's Windows sandbox
69
- * setup for certain. Pure.
73
+ * The flags every Codex start through the sandbox gets (the codex.cmd shim, the daemon's sessions). Flags beat
74
+ * config.toml and a resumed thread's saved permissions; config.toml carries the same settings for a start without
75
+ * them (codexConfigOwned). Full access with no questions is the one flag that skips Codex's Windows sandbox setup for
76
+ * certain. Pure.
70
77
  */
71
78
  export function codexArguments(c) {
72
79
  const out = c.sandboxMode === 'danger-full-access' && c.approvalPolicy === 'never'
@@ -85,10 +92,13 @@ export function agentArguments(name, harness, codex) {
85
92
  /** The largest config.toml the sandbox edits. */
86
93
  export const MAX_CODEX_CONFIG_BYTES = 1024 * 1024;
87
94
  /**
88
- * Codex's config.toml as the sandbox wants it, every other line byte for byte: the lines an older version wrote above
89
- * the first table (sandbox_mode, approval_policy, under the marker comment) are gone, `[windows] sandbox` follows
90
- * windowsSandbox ("off" removes the line, so Codex does not set up its own sandbox users), and
91
- * mcp_oauth_credentials_store = "file" is added when it is not set. Running it again changes nothing. Pure.
95
+ * Codex's config.toml as the sandbox wants it, every other line byte for byte. Above the first table, under the
96
+ * marker comment: sandbox_mode, approval_policy (and model, model_reasoning_effort when set), so ANY start of Codex
97
+ * obeys the codex settings, also one without the shim's flags (a copy installed with npm -g). The marked lines are
98
+ * replaced each time; a key the user set on a line of their own above the first table is left to them.
99
+ * `[windows] sandbox` follows windowsSandbox ("off" removes the line, which Codex adds back after its own setup, so it
100
+ * does not set up its own sandbox users), and mcp_oauth_credentials_store = "file" is added when it is not set.
101
+ * Keeps the file's line endings; running it again changes nothing. Pure.
92
102
  */
93
103
  export function codexConfigOwned(toml, c) {
94
104
  const eol = toml.includes('\r\n') ? '\r\n' : '\n';
@@ -96,15 +106,31 @@ export function codexConfigOwned(toml, c) {
96
106
  let top = firstTable < 0 ? toml : toml.slice(0, firstTable);
97
107
  const tables = firstTable < 0 ? '' : toml.slice(firstTable);
98
108
  top = top.replace(MARKED_LINES, '');
99
- if (c.mcpLoginsInFile && !/^[ \t]*mcp_oauth_credentials_store[ \t]*=/m.test(top)) {
109
+ if (c.mcpLoginsInFile && !hasKey(top, 'mcp_oauth_credentials_store')) {
100
110
  if (top && !top.endsWith('\n'))
101
111
  top += eol;
102
112
  top += 'mcp_oauth_credentials_store = "file"' + eol + (tables ? eol : '');
103
113
  }
104
- return top + codexWindowsSandbox(tables, c.windowsSandbox, eol);
114
+ const values = {
115
+ sandbox_mode: c.sandboxMode,
116
+ approval_policy: c.approvalPolicy,
117
+ model: c.model,
118
+ model_reasoning_effort: c.reasoningEffort === 'default' ? '' : c.reasoningEffort,
119
+ };
120
+ const lines = CODEX_OWNED_KEYS.filter((k) => values[k] && !hasKey(top, k)).map((k) => k + ' = ' + JSON.stringify(values[k]));
121
+ const marked = lines.length ? [CODEX_MARKER_LINE, ...lines].join(eol) + eol + (top || tables ? eol : '') : '';
122
+ return marked + top + codexWindowsSandbox(tables, c.windowsSandbox, eol);
123
+ }
124
+ /** True when the top of config.toml sets the key on a line of its own. */
125
+ function hasKey(top, key) {
126
+ return new RegExp('^[ \t]*' + key + '[ \t]*=', 'm').test(top);
105
127
  }
106
- /** The lines an older version wrote above the first table: its marker comment, then sandbox_mode / approval_policy. */
107
- const MARKED_LINES = new RegExp('^' + CODEX_MARKER + '[^\r\n]*\r?\n(?:(?:sandbox_mode|approval_policy)[ \t]*=[^\r\n]*\r?\n)*(?:[ \t]*\r?\n)?', 'm');
128
+ /** The lines the sandbox wrote above the first table: a marker comment, the keys it owns, then one blank line. */
129
+ const MARKED_LINES = new RegExp('^(?:' +
130
+ CODEX_MARKERS.join('|') +
131
+ ')[^\r\n]*\r?\n(?:(?:' +
132
+ CODEX_OWNED_KEYS.join('|') +
133
+ ')[ \t]*=[^\r\n]*(?:\r?\n|$))*(?:[ \t]*\r?\n)?', 'm');
108
134
  /** The tables of config.toml (from the first one on) with `[windows] sandbox` set to the mode, or removed for "off". */
109
135
  function codexWindowsSandbox(tables, mode, eol) {
110
136
  const setting = 'sandbox = "' + mode + '"';
@@ -7,7 +7,8 @@ export declare function localStateVars(): Record<string, string>;
7
7
  export declare function resetMachineBound(): void;
8
8
  /**
9
9
  * Boot: Codex's config.toml follows the codex settings (codexConfigOwned): no Windows sandbox setup of its own, its MCP
10
- * logins in a file. The sandbox mode itself travels as launch flags (codexArguments), which beat this file.
10
+ * logins in a file, the sandbox mode and approval policy for a start without the launch flags (codexArguments, which
11
+ * beat this file and still go on every start through the sandbox).
11
12
  */
12
13
  export declare function codexBootConfig(): void;
13
14
  /** Boot: the saved databases of every configured agent are copied to the sandbox's disk, where the agent runs them. */
@@ -104,7 +104,8 @@ export function resetMachineBound() {
104
104
  }
105
105
  /**
106
106
  * Boot: Codex's config.toml follows the codex settings (codexConfigOwned): no Windows sandbox setup of its own, its MCP
107
- * logins in a file. The sandbox mode itself travels as launch flags (codexArguments), which beat this file.
107
+ * logins in a file, the sandbox mode and approval policy for a start without the launch flags (codexArguments, which
108
+ * beat this file and still go on every start through the sandbox).
108
109
  */
109
110
  export function codexBootConfig() {
110
111
  if (dryRun || !session.agents?.codex || !session.codex)
@@ -0,0 +1,28 @@
1
+ import { type ReadyInfo, type ViewStage, type ViewTask } from './progress-view.js';
2
+ import type { BootTaskDef, RunnerObserver, TaskOutcome } from './runner.js';
3
+ export declare class BootConsole implements RunnerObserver {
4
+ private readonly view;
5
+ private readonly live;
6
+ private timer;
7
+ private open;
8
+ private constructor();
9
+ /** `plain`: ordered lines even on a terminal (a dry run). */
10
+ static create(stages: readonly ViewStage[], tasks: readonly ViewTask[], o: {
11
+ plain: boolean;
12
+ }): BootConsole;
13
+ /** Prints the header and takes the console. */
14
+ start(header: string): void;
15
+ started(task: BootTaskDef): void;
16
+ finished(task: BootTaskDef, outcome: TaskOutcome): void;
17
+ /** What a running task's place on its row says (null: its title). */
18
+ detail(taskId: string, text: string | null): void;
19
+ /** The tool summary: to the file now, to the console once the boot is ready. */
20
+ summary(lines: readonly string[]): void;
21
+ /** Runs `work` that writes to the terminal itself (the hooks). */
22
+ aside<T>(work: () => T): T;
23
+ /**
24
+ * The final summary (`ready`, or null when the boot failed); the console is the log's own again. Programs' output
25
+ * keeps going to the log (the background runs quiet: file only).
26
+ */
27
+ release(ready: ReadyInfo | null): void;
28
+ }
@@ -0,0 +1,98 @@
1
+ // The boot's console on the sandbox's terminal: the live progress block when it is a terminal, ordered plain lines
2
+ // otherwise (and in a dry run). While it is open it takes the log's console lines and the output of the programs the
3
+ // boot starts; it is released, with the final summary, right before the first session takes the window.
4
+ import { log, record, routeConsole } from '../runtime/log.js';
5
+ import { routeOutput } from '../runtime/process.js';
6
+ import { CONSOLE_GLYPHS, ProgressView, readyLines, RICH_GLYPHS, } from './progress-view.js';
7
+ /** The spinner's pace (the elapsed times change every tenth of a second too). */
8
+ const FRAME_MS = 100;
9
+ const COLUMNS = 80;
10
+ const SHOW_CURSOR = '\x1b[?25h';
11
+ export class BootConsole {
12
+ view;
13
+ live;
14
+ timer = null;
15
+ open = false;
16
+ constructor(view, live) {
17
+ this.view = view;
18
+ this.live = live;
19
+ }
20
+ /** `plain`: ordered lines even on a terminal (a dry run). */
21
+ static create(stages, tasks, o) {
22
+ const live = !o.plain && process.stdout.isTTY === true;
23
+ // Windows Terminal sets WT_SESSION; the classic console's font has no braille or check marks.
24
+ const rich = live && Boolean(process.env.WT_SESSION);
25
+ const view = new ProgressView(stages, tasks, {
26
+ live,
27
+ glyphs: rich ? RICH_GLYPHS : CONSOLE_GLYPHS,
28
+ now: () => Date.now(),
29
+ screen: {
30
+ write: (text) => void process.stdout.write(text),
31
+ columns: () => process.stdout.columns ?? COLUMNS,
32
+ },
33
+ });
34
+ return new BootConsole(view, live);
35
+ }
36
+ /** Prints the header and takes the console. */
37
+ start(header) {
38
+ record(header);
39
+ this.open = true;
40
+ this.view.start(header);
41
+ routeConsole({
42
+ line: (message, stage) => this.view.line(message, stage),
43
+ warning: (message) => this.view.warning(message),
44
+ notice: (message) => this.view.notice(message),
45
+ });
46
+ // Programs the boot starts would draw over the block (or between the plain stages): their output goes to the log;
47
+ // the hooks keep the terminal. After the release too: the background steps must not write into the session.
48
+ routeOutput({ capturing: () => !this.open || this.view.capturing, line: (text) => log(text) });
49
+ if (!this.live)
50
+ return;
51
+ this.timer = setInterval(() => this.view.tick(), FRAME_MS);
52
+ this.timer.unref();
53
+ // A crash must not leave the terminal without its cursor.
54
+ process.once('exit', () => {
55
+ if (this.open)
56
+ process.stdout.write(SHOW_CURSOR);
57
+ });
58
+ }
59
+ started(task) {
60
+ this.view.started(task);
61
+ this.view.tick();
62
+ }
63
+ finished(task, outcome) {
64
+ this.view.finished(task, outcome);
65
+ this.view.tick();
66
+ }
67
+ /** What a running task's place on its row says (null: its title). */
68
+ detail(taskId, text) {
69
+ this.view.detail(taskId, text);
70
+ }
71
+ /** The tool summary: to the file now, to the console once the boot is ready. */
72
+ summary(lines) {
73
+ for (const line of lines)
74
+ record(' ' + line);
75
+ this.view.summary(lines);
76
+ }
77
+ /** Runs `work` that writes to the terminal itself (the hooks). */
78
+ aside(work) {
79
+ return this.view.aside(work);
80
+ }
81
+ /**
82
+ * The final summary (`ready`, or null when the boot failed); the console is the log's own again. Programs' output
83
+ * keeps going to the log (the background runs quiet: file only).
84
+ */
85
+ release(ready) {
86
+ if (!this.open)
87
+ return;
88
+ this.open = false;
89
+ if (this.timer)
90
+ clearInterval(this.timer);
91
+ this.timer = null;
92
+ routeConsole(null);
93
+ if (ready)
94
+ for (const line of readyLines(ready))
95
+ record(line);
96
+ this.view.release(ready);
97
+ }
98
+ }
@@ -15,6 +15,9 @@ export declare class Boot {
15
15
  private secrets;
16
16
  private basePath;
17
17
  private readonly startedAt;
18
+ private readonly who;
19
+ /** The console while the boot runs: the stage rows, warnings and summary (released before the first session). */
20
+ private readonly progress;
18
21
  private openGate;
19
22
  private readonly gate;
20
23
  /** The boot is ready: the runner no longer writes the status (the session owns it). */
@@ -24,9 +27,13 @@ export declare class Boot {
24
27
  private stepsDone;
25
28
  private stepsTotal;
26
29
  run(): Promise<void>;
30
+ /** The prelude, then every foreground task (the console shows them). */
31
+ private foreground;
27
32
  private stageTitle;
28
- /** The names shown for work that goes on after the boot is ready. */
33
+ /** The toolchain steps that go on after the boot is ready (Visual Studio and what needs it), in plan order. */
29
34
  private backgroundNames;
35
+ /** Runs a stage's hooks; they write to the terminal themselves, so the progress block steps aside for them. */
36
+ private hooks;
30
37
  /**
31
38
  * The host's one-shot file, read once. Your saved service sign-ins (accounts.<kind>.share) go to their tools before
32
39
  * anything opens them, and leave memory at once; what they give the agent (EXPO_TOKEN ...) joins its environment