windows-development-sandbox 1.18.2 → 1.18.4

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 (98) hide show
  1. package/config.example.json +1 -1
  2. package/dist/app/features/defs.d.ts +26 -0
  3. package/dist/app/features/defs.js +2 -1
  4. package/dist/app/features/sandbox/sandbox.def.d.ts +34 -0
  5. package/dist/app/features/sandbox/sandbox.def.js +52 -10
  6. package/dist/app/features/sandbox/sandbox.run.d.ts +24 -0
  7. package/dist/app/features/sandbox/sandbox.run.js +45 -1
  8. package/dist/app/registry.js +2 -1
  9. package/dist/bin/agent-sandbox.js +2 -2
  10. package/dist/bin/{chunk-MQIA7GNB.js → chunk-FRPLQLSY.js} +3188 -2765
  11. package/dist/bin/{chunk-M4OM632I.js → chunk-MY7NLQQY.js} +1 -1
  12. package/dist/bin/{chunk-IIHL7W6B.js → chunk-N4GXYTLV.js} +1 -1
  13. package/dist/bin/claude-host.js +2 -2
  14. package/dist/bin/claude-sbx.js +2 -2
  15. package/dist/bin/codex-sbx.js +2 -2
  16. package/dist/bin/gemini-sbx.js +2 -2
  17. package/dist/bin/sandbox.js +2 -2
  18. package/dist/bin/sbx-hide.js +2 -2
  19. package/dist/bin/sbx-show.js +2 -2
  20. package/dist/cli/commands/launch/launch.js +4 -1
  21. package/dist/cli/commands/launch/leftover-offer.d.ts +3 -0
  22. package/dist/cli/commands/launch/leftover-offer.js +26 -0
  23. package/dist/cli/commands/repair/checks/sandbox.d.ts +3 -1
  24. package/dist/cli/commands/repair/checks/sandbox.js +53 -2
  25. package/dist/core/config/defaults.js +1 -1
  26. package/dist/core/schema/sandbox.js +1 -1
  27. package/dist/core/schema/toolchains.js +2 -2
  28. package/dist/guest/daemon/daemon.js +1 -1
  29. package/dist/guest/daemon/heartbeat.d.ts +11 -0
  30. package/dist/guest/daemon/heartbeat.js +59 -0
  31. package/dist/guest/daemon/idle-shutdown.js +2 -0
  32. package/dist/guest/entries/daemon.js +2 -0
  33. package/dist/guest/governor/core.d.ts +19 -1
  34. package/dist/guest/governor/core.js +23 -1
  35. package/dist/guest/governor/decide.d.ts +14 -0
  36. package/dist/guest/governor/decide.js +52 -7
  37. package/dist/guest/governor/governor.d.ts +4 -1
  38. package/dist/guest/governor/governor.js +9 -2
  39. package/dist/guest/toolchains/native/rust-extras.d.ts +7 -0
  40. package/dist/guest/toolchains/native/rust-extras.js +26 -3
  41. package/dist/guest/toolchains/plan.js +6 -4
  42. package/dist/host/channel/channel.d.ts +1 -1
  43. package/dist/host/channel/channel.js +10 -2
  44. package/dist/host/health/index.d.ts +1 -0
  45. package/dist/host/health/index.js +1 -0
  46. package/dist/host/health/leftovers.d.ts +3 -0
  47. package/dist/host/health/leftovers.js +40 -0
  48. package/dist/host/health/stale.d.ts +48 -0
  49. package/dist/host/health/stale.js +40 -0
  50. package/dist/host/launch/wsb-file.d.ts +6 -1
  51. package/dist/host/launch/wsb-file.js +9 -5
  52. package/dist/host/life/crash-report.d.ts +3 -1
  53. package/dist/host/life/crash-report.js +9 -3
  54. package/dist/host/life/index.d.ts +1 -0
  55. package/dist/host/life/index.js +1 -0
  56. package/dist/host/life/last-end.d.ts +28 -0
  57. package/dist/host/life/last-end.js +91 -0
  58. package/dist/host/life/life-record.d.ts +3 -0
  59. package/dist/host/life/life-record.js +2 -2
  60. package/dist/host/life/stop-events.d.ts +3 -1
  61. package/dist/host/life/stop-events.js +59 -0
  62. package/dist/host/life/stop-warning.js +3 -0
  63. package/dist/host/life/watchdog.d.ts +2 -0
  64. package/dist/host/life/watchdog.js +8 -2
  65. package/dist/host/sandbox/cleanup.d.ts +37 -0
  66. package/dist/host/sandbox/cleanup.js +82 -0
  67. package/dist/host/sandbox/index.d.ts +1 -0
  68. package/dist/host/sandbox/index.js +1 -0
  69. package/dist/host/sandbox/platform.js +1 -1
  70. package/dist/runtime/agent.js +28 -3
  71. package/dist/runtime/daemon.js +629 -479
  72. package/dist/runtime/db.js +28 -3
  73. package/dist/runtime/main.js +59 -11
  74. package/dist/runtime/netproxy.js +28 -3
  75. package/dist/runtime/tool.js +28 -3
  76. package/dist/runtime/view.js +28 -3
  77. package/dist/selftest/heavy-load.test.js +212 -14
  78. package/dist/selftest/live-terminal.test.js +4 -0
  79. package/dist/shared/channel/run-dir.d.ts +4 -1
  80. package/dist/shared/channel/run-dir.js +7 -1
  81. package/dist/shared/channel/schema.d.ts +24 -0
  82. package/dist/shared/channel/schema.js +22 -0
  83. package/dist/shared/memory/policy.d.ts +10 -0
  84. package/dist/shared/memory/policy.js +17 -1
  85. package/dist/shared/system.d.ts +1 -0
  86. package/dist/shared/system.js +1 -0
  87. package/dist/sync/watcher.js +10 -5
  88. package/dist/web/index.html +1 -1
  89. package/dist/web/{main-CWIF4VQI.js → main-GVZWWP4I.js} +63 -63
  90. package/native/csharp/HcsHost/AssemblyInfo.cs +3 -3
  91. package/native/csharp/HostTools/AssemblyInfo.cs +3 -3
  92. package/native/csharp/HostTools/Program.cs +5 -1
  93. package/native/csharp/StorageHelper/AssemblyInfo.cs +3 -3
  94. package/native/scripts/stop-events.ps1 +7 -3
  95. package/package.json +1 -1
  96. package/sandbox/csharp/MemGov.cs +58 -0
  97. package/schemas/config.schema.json +3 -3
  98. package/schemas/sandbox-config.schema.json +3 -3
@@ -140,7 +140,7 @@
140
140
  "components": [],
141
141
  "targets": [],
142
142
  "crates": [],
143
- "targetDir": "separate",
143
+ "targetDir": "auto",
144
144
  "linker": "lld",
145
145
  "linkSlots": "auto"
146
146
  },
@@ -26,6 +26,18 @@ export declare const OPERATIONS: readonly [OperationDef<import("zod").ZodObject<
26
26
  stopped: import("zod").ZodBoolean;
27
27
  message: import("zod").ZodString;
28
28
  }, import("zod/v4/core").$strip>, "sandbox.stop">, OperationDef<import("zod").ZodObject<{
29
+ service: import("zod").ZodOptional<import("zod").ZodBoolean>;
30
+ }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
31
+ outcome: import("zod").ZodEnum<{
32
+ failed: "failed";
33
+ nothing: "nothing";
34
+ alive: "alive";
35
+ declined: "declined";
36
+ cleaned: "cleaned";
37
+ "service-restarted": "service-restarted";
38
+ }>;
39
+ message: import("zod").ZodString;
40
+ }, import("zod/v4/core").$strip>, "sandbox.cleanUp">, OperationDef<import("zod").ZodObject<{
29
41
  harness: import("zod").ZodOptional<import("zod").ZodString>;
30
42
  all: import("zod").ZodDefault<import("zod").ZodBoolean>;
31
43
  }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
@@ -374,6 +386,13 @@ export declare const QUERIES: readonly [QueryDef<import("zod").ZodObject<{}, imp
374
386
  guarded: import("zod").ZodNumber;
375
387
  findings: import("zod").ZodArray<import("zod").ZodString>;
376
388
  }, import("zod/v4/core").$strip>;
389
+ lastEnd: import("zod").ZodOptional<import("zod").ZodObject<{
390
+ text: import("zod").ZodString;
391
+ at: import("zod").ZodString;
392
+ }, import("zod/v4/core").$strip>>;
393
+ leftover: import("zod").ZodOptional<import("zod").ZodObject<{
394
+ text: import("zod").ZodString;
395
+ }, import("zod/v4/core").$strip>>;
377
396
  }, import("zod/v4/core").$strip>, "sandbox.status">, QueryDef<import("zod").ZodObject<{
378
397
  wide: import("zod").ZodDefault<import("zod").ZodBoolean>;
379
398
  }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
@@ -1064,6 +1083,13 @@ export declare const VIEWS: readonly [import("../kernel/define.js").ViewDef<{
1064
1083
  guarded: import("zod").ZodNumber;
1065
1084
  findings: import("zod").ZodArray<import("zod").ZodString>;
1066
1085
  }, import("zod/v4/core").$strip>;
1086
+ lastEnd: import("zod").ZodOptional<import("zod").ZodObject<{
1087
+ text: import("zod").ZodString;
1088
+ at: import("zod").ZodString;
1089
+ }, import("zod/v4/core").$strip>>;
1090
+ leftover: import("zod").ZodOptional<import("zod").ZodObject<{
1091
+ text: import("zod").ZodString;
1092
+ }, import("zod/v4/core").$strip>>;
1067
1093
  }, import("zod/v4/core").$strip>, "sandbox.status">;
1068
1094
  }, Record<string, never>, "sandbox">, import("../kernel/define.js").ViewDef<{
1069
1095
  sessions: QueryDef<import("zod").ZodObject<{
@@ -7,7 +7,7 @@ import { checkEngine, cleanEngine, engineLeftovers, engineStatus, engineView } f
7
7
  import { checkHealth, enableSandbox, healthView, lastHealth, repairHealth } from './health/health.def.js';
8
8
  import { cleanData, dataView, pruneStorage, showData } from './data/data.def.js';
9
9
  import { changeDataFolder, containedOff, containedOn, containedStatus, moveStorage, showStorage, storageView, useStorage, } from './storage/storage.def.js';
10
- import { hideWindow, sandboxStatus, sandboxView, showWindow, stopSandbox } from './sandbox/sandbox.def.js';
10
+ import { cleanUpSandbox, hideWindow, sandboxStatus, sandboxView, showWindow, stopSandbox, } from './sandbox/sandbox.def.js';
11
11
  import { addProject, allProjects, chooseProjects, excludeProject, includeProject, listProjects, projectRoots, projectsView, removeProject, } from './projects/projects.def.js';
12
12
  import { listLogs, logsView, tailLog } from './logs/logs.def.js';
13
13
  import { auditView, listAudit } from './audit/audit.def.js';
@@ -23,6 +23,7 @@ export const OPERATIONS = [
23
23
  showWindow,
24
24
  hideWindow,
25
25
  stopSandbox,
26
+ cleanUpSandbox,
26
27
  restartSessions,
27
28
  startShell,
28
29
  closeSession,
@@ -57,6 +57,13 @@ export declare const SandboxStatusSchema: z.ZodObject<{
57
57
  guarded: z.ZodNumber;
58
58
  findings: z.ZodArray<z.ZodString>;
59
59
  }, z.core.$strip>;
60
+ lastEnd: z.ZodOptional<z.ZodObject<{
61
+ text: z.ZodString;
62
+ at: z.ZodString;
63
+ }, z.core.$strip>>;
64
+ leftover: z.ZodOptional<z.ZodObject<{
65
+ text: z.ZodString;
66
+ }, z.core.$strip>>;
60
67
  }, z.core.$strip>;
61
68
  export type SandboxStatus = z.output<typeof SandboxStatusSchema>;
62
69
  /** "pong (2 session(s) running)" from the helper in the sandbox, in plain words. */
@@ -118,6 +125,13 @@ export declare const sandboxStatus: import("../../kernel/define.js").QueryDef<z.
118
125
  guarded: z.ZodNumber;
119
126
  findings: z.ZodArray<z.ZodString>;
120
127
  }, z.core.$strip>;
128
+ lastEnd: z.ZodOptional<z.ZodObject<{
129
+ text: z.ZodString;
130
+ at: z.ZodString;
131
+ }, z.core.$strip>>;
132
+ leftover: z.ZodOptional<z.ZodObject<{
133
+ text: z.ZodString;
134
+ }, z.core.$strip>>;
121
135
  }, z.core.$strip>, "sandbox.status">;
122
136
  export declare const showWindow: import("../../kernel/define.js").OperationDef<z.ZodObject<{}, z.core.$strip>, z.ZodObject<{
123
137
  message: z.ZodString;
@@ -125,6 +139,19 @@ export declare const showWindow: import("../../kernel/define.js").OperationDef<z
125
139
  export declare const hideWindow: import("../../kernel/define.js").OperationDef<z.ZodObject<{}, z.core.$strip>, z.ZodObject<{
126
140
  message: z.ZodString;
127
141
  }, z.core.$strip>, "sandbox.hideWindow">;
142
+ export declare const cleanUpSandbox: import("../../kernel/define.js").OperationDef<z.ZodObject<{
143
+ service: z.ZodOptional<z.ZodBoolean>;
144
+ }, z.core.$strip>, z.ZodObject<{
145
+ outcome: z.ZodEnum<{
146
+ failed: "failed";
147
+ nothing: "nothing";
148
+ alive: "alive";
149
+ declined: "declined";
150
+ cleaned: "cleaned";
151
+ "service-restarted": "service-restarted";
152
+ }>;
153
+ message: z.ZodString;
154
+ }, z.core.$strip>, "sandbox.cleanUp">;
128
155
  export declare const stopSandbox: import("../../kernel/define.js").OperationDef<z.ZodObject<{}, z.core.$strip>, z.ZodObject<{
129
156
  stopped: z.ZodBoolean;
130
157
  message: z.ZodString;
@@ -189,5 +216,12 @@ export declare const sandboxView: import("../../kernel/define.js").ViewDef<{
189
216
  guarded: z.ZodNumber;
190
217
  findings: z.ZodArray<z.ZodString>;
191
218
  }, z.core.$strip>;
219
+ lastEnd: z.ZodOptional<z.ZodObject<{
220
+ text: z.ZodString;
221
+ at: z.ZodString;
222
+ }, z.core.$strip>>;
223
+ leftover: z.ZodOptional<z.ZodObject<{
224
+ text: z.ZodString;
225
+ }, z.core.$strip>>;
192
226
  }, z.core.$strip>, "sandbox.status">;
193
227
  }, Record<string, never>, "sandbox">;
@@ -1,7 +1,8 @@
1
1
  // The running sandbox as a whole: what it is doing (a live query), its window, and stopping it. Pure: schemas, labels
2
2
  // and the view; sandbox.run.ts does the work.
3
3
  import { z } from 'zod';
4
- import { action, ago, defineOperation, defineQuery, defineView, plural, } from '../../kernel/index.js';
4
+ import { action, ago, defineOperation, defineQuery, defineView, link, plural, } from '../../kernel/index.js';
5
+ import { CRASH } from '../logs/logs.def.js';
5
6
  const ProjectSchema = z.object({
6
7
  name: z.string().describe('The project'),
7
8
  host: z.string().describe('Its folder on this PC'),
@@ -38,6 +39,16 @@ export const SandboxStatusSchema = z.object({
38
39
  projects: z.array(ProjectSchema),
39
40
  otherSessions: z.array(z.object({ id: z.string(), harness: z.string(), cwd: z.string() })),
40
41
  repoGuard: z.object({ guarded: z.number(), findings: z.array(z.string()) }),
42
+ /** How the last sandbox ended when nobody closed it (it is not running now). */
43
+ lastEnd: z
44
+ .object({ text: z.string().describe('What happened'), at: z.string().describe('When (ISO time)') })
45
+ .optional()
46
+ .describe('The last sandbox ended unexpectedly'),
47
+ /** Windows Sandbox programs left behind by a sandbox that is gone (Windows then refuses to start a new one). */
48
+ leftover: z
49
+ .object({ text: z.string().describe('What was found') })
50
+ .optional()
51
+ .describe('Windows Sandbox programs are left over from a sandbox that is gone'),
41
52
  });
42
53
  const MessageSchema = z.object({ message: z.string().describe('What happened') });
43
54
  /** "pong (2 session(s) running)" from the helper in the sandbox, in plain words. */
@@ -85,6 +96,8 @@ export const sandboxStatus = defineQuery({
85
96
  projects: o.projects,
86
97
  otherSessions: o.otherSessions,
87
98
  repoGuard: o.repoGuard,
99
+ ...(o.lastEnd ? { lastEnd: o.lastEnd } : {}),
100
+ ...(o.leftover ? { leftover: o.leftover } : {}),
88
101
  };
89
102
  },
90
103
  },
@@ -109,6 +122,23 @@ export const hideWindow = defineOperation({
109
122
  text: (o) => o.message,
110
123
  cli: { name: 'hide', group: 'Sessions' },
111
124
  });
125
+ export const cleanUpSandbox = defineOperation({
126
+ id: 'sandbox.cleanUp',
127
+ title: 'Clean up Windows Sandbox…',
128
+ summary: 'close the Windows Sandbox programs a dead sandbox left behind, so a new one can start',
129
+ input: z.object({
130
+ service: z
131
+ .boolean()
132
+ .optional()
133
+ .describe('Also restart the Windows Sandbox service (Windows asks for administrator permission)'),
134
+ }),
135
+ output: z.object({
136
+ outcome: z.enum(['nothing', 'alive', 'declined', 'cleaned', 'service-restarted', 'failed']),
137
+ message: z.string(),
138
+ }),
139
+ text: (o) => o.message,
140
+ cli: { name: 'cleanup', group: 'Sessions' },
141
+ });
112
142
  export const stopSandbox = defineOperation({
113
143
  id: 'sandbox.stop',
114
144
  access: 'control',
@@ -168,6 +198,8 @@ function facts(o) {
168
198
  fact('sessions', 'Sessions', String(sessions)),
169
199
  ]
170
200
  : []),
201
+ ...(o.leftover ? [fact('leftover', 'Windows Sandbox', o.leftover.text, { tone: 'warn' })] : []),
202
+ ...(o.lastEnd ? [fact('lastEnd', 'Last sandbox', o.lastEnd.text, { tone: 'warn' })] : []),
171
203
  fact('account', 'Account', s.account ?? 'none selected (sandbox account use)'),
172
204
  fact('sync', 'Background sync', s.syncWatcher ? 'on' : 'off'),
173
205
  ];
@@ -221,14 +253,24 @@ export function sandboxPage(o) {
221
253
  title: 'Status',
222
254
  facts: facts(o),
223
255
  // Only the window action that changes something: show a hidden window, hide a shown one.
224
- actions: running
225
- ? [
226
- o.sandbox.headless
227
- ? action(showWindow, {}, { key: 'w', verb: 'show', importance: 3 })
228
- : action(hideWindow, {}, { key: 'h', verb: 'hide', importance: 3 }),
229
- action(stopSandbox, {}, { key: 'x', verb: 'stop', importance: 1 }),
230
- ]
231
- : [],
256
+ actions: [
257
+ ...(o.leftover ? [action(cleanUpSandbox, {}, { key: 'c', verb: 'clean up', importance: 1 })] : []),
258
+ ...(running
259
+ ? [
260
+ o.sandbox.headless
261
+ ? action(showWindow, {}, { key: 'w', verb: 'show', importance: 3 })
262
+ : action(hideWindow, {}, { key: 'h', verb: 'hide', importance: 3 }),
263
+ action(stopSandbox, {}, { key: 'x', verb: 'stop', importance: 1 }),
264
+ ]
265
+ : o.lastEnd
266
+ ? [
267
+ link({ kind: 'view', view: 'logs', params: { name: CRASH } }, 'Show crash report', {
268
+ verb: 'show',
269
+ importance: 2,
270
+ }),
271
+ ]
272
+ : []),
273
+ ],
232
274
  };
233
275
  const others = {
234
276
  id: 'other-sessions',
@@ -255,6 +297,6 @@ export const sandboxView = defineView({
255
297
  title: 'Sandbox',
256
298
  summary: 'Status, projects and controls of the running sandbox.',
257
299
  queries: { status: sandboxStatus },
258
- operations: [showWindow, hideWindow, stopSandbox],
300
+ operations: [showWindow, hideWindow, stopSandbox, cleanUpSandbox],
259
301
  build: ({ status }) => sandboxPage(status),
260
302
  });
@@ -55,6 +55,13 @@ export declare const sandboxStatusRun: import("../../kernel/define.js").Implemen
55
55
  guarded: import("zod").ZodNumber;
56
56
  findings: import("zod").ZodArray<import("zod").ZodString>;
57
57
  }, import("zod/v4/core").$strip>;
58
+ lastEnd: import("zod").ZodOptional<import("zod").ZodObject<{
59
+ text: import("zod").ZodString;
60
+ at: import("zod").ZodString;
61
+ }, import("zod/v4/core").$strip>>;
62
+ leftover: import("zod").ZodOptional<import("zod").ZodObject<{
63
+ text: import("zod").ZodString;
64
+ }, import("zod/v4/core").$strip>>;
58
65
  }, import("zod/v4/core").$strip>, "sandbox.status">, import("../../../cli/commands/context.js").Context>;
59
66
  export declare const showWindowRun: import("../../kernel/define.js").Implementation<import("../../kernel/define.js").OperationDef<import("zod").ZodObject<{}, import("zod/v4/core").$strip>, import("zod").ZodObject<{
60
67
  message: import("zod").ZodString;
@@ -62,6 +69,23 @@ export declare const showWindowRun: import("../../kernel/define.js").Implementat
62
69
  export declare const hideWindowRun: import("../../kernel/define.js").Implementation<import("../../kernel/define.js").OperationDef<import("zod").ZodObject<{}, import("zod/v4/core").$strip>, import("zod").ZodObject<{
63
70
  message: import("zod").ZodString;
64
71
  }, import("zod/v4/core").$strip>, "sandbox.hideWindow">, import("../../../cli/commands/context.js").Context>;
72
+ /**
73
+ * A dead sandbox leaves Windows Sandbox programs behind and Windows then refuses to start a new one ("signed out"):
74
+ * ends them after asking, and restarts the Windows Sandbox service when asked to (or when they will not go).
75
+ */
76
+ export declare const cleanUpSandboxRun: import("../../kernel/define.js").Implementation<import("../../kernel/define.js").OperationDef<import("zod").ZodObject<{
77
+ service: import("zod").ZodOptional<import("zod").ZodBoolean>;
78
+ }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
79
+ outcome: import("zod").ZodEnum<{
80
+ failed: "failed";
81
+ nothing: "nothing";
82
+ alive: "alive";
83
+ declined: "declined";
84
+ cleaned: "cleaned";
85
+ "service-restarted": "service-restarted";
86
+ }>;
87
+ message: import("zod").ZodString;
88
+ }, import("zod/v4/core").$strip>, "sandbox.cleanUp">, import("../../../cli/commands/context.js").Context>;
65
89
  /**
66
90
  * Stopping ends every project's sessions: it says which, and the sessions of other projects need a yes (also on the
67
91
  * command line, where --yes gives it; a script without it fails rather than end them by accident).
@@ -3,6 +3,10 @@
3
3
  import path from 'node:path';
4
4
  import { safeText } from '../../../core/util/index.js';
5
5
  import { SandboxChannel } from '../../../host/channel/index.js';
6
+ import { readSandboxFacts } from '../../../host/health/index.js';
7
+ import { LifeRecord, readLastEnd } from '../../../host/life/index.js';
8
+ import { cleanUpLeftovers, endSandboxPrograms, restartSandboxService } from '../../../host/sandbox/index.js';
9
+ import { judgeSandbox } from '../../../host/health/index.js';
6
10
  import { runningOwner } from '../../../host/sharing/index.js';
7
11
  import { requireOnPc, requireRunning } from '../../../cli/commands/context.js';
8
12
  import { hereDir } from '../../../cli/commands/projects/here.js';
@@ -10,7 +14,7 @@ import { lookAtSandbox, useSharedSandbox } from '../../../cli/commands/shared-us
10
14
  import { SandboxService } from '../../../cli/services/index.js';
11
15
  import { implement, typedConfirm } from '../../context.js';
12
16
  import { cancelled } from '../../kernel/index.js';
13
- import { hideWindow, sandboxStatus, showWindow, stopSandbox } from './sandbox.def.js';
17
+ import { cleanUpSandbox, hideWindow, sandboxStatus, showWindow, stopSandbox, } from './sandbox.def.js';
14
18
  /** Another user's sandbox that is not shared: whose it is, and nothing else. */
15
19
  function limitedStatus(view, folder) {
16
20
  const owner = view.owner;
@@ -36,7 +40,12 @@ export const sandboxStatusRun = implement(sandboxStatus, async (asked) => {
36
40
  if (view.limited)
37
41
  return limitedStatus(view, hereDir(asked));
38
42
  const report = await new SandboxService(view.ctx).status();
43
+ const { storageRoot, dataPath } = view.ctx;
44
+ const lastEnd = readLastEnd(() => new LifeRecord(storageRoot, dataPath).read(), dataPath, report.sandbox.running);
45
+ const leftover = report.sandbox.running ? judgeSandbox(await readSandboxFacts(storageRoot, dataPath, false)) : null;
39
46
  return {
47
+ ...(lastEnd ? { lastEnd } : {}),
48
+ ...(leftover?.state === 'leftover' ? { leftover: { text: leftover.why } } : {}),
40
49
  ...(view.line ? { shared: { line: view.line, limited: false, startedBy: view.ctx.borrowed?.user ?? null } } : {}),
41
50
  ...report,
42
51
  sandbox: { ...report.sandbox, daemon: safeText(report.sandbox.daemon) },
@@ -57,6 +66,41 @@ export const hideWindowRun = implement(hideWindow, async (ctx, _input, io) => {
57
66
  await new SandboxService(ctx).hideWindow();
58
67
  return { message: 'Sandbox window hidden. Bring it back with: sbx-show' };
59
68
  });
69
+ /**
70
+ * A dead sandbox leaves Windows Sandbox programs behind and Windows then refuses to start a new one ("signed out"):
71
+ * ends them after asking, and restarts the Windows Sandbox service when asked to (or when they will not go).
72
+ */
73
+ export const cleanUpSandboxRun = implement(cleanUpSandbox, async (ctx, input, io) => {
74
+ requireOnPc('cleanup');
75
+ const { storageRoot, dataPath } = ctx;
76
+ const lines = [];
77
+ const outcome = await cleanUpLeftovers({
78
+ facts: () => readSandboxFacts(storageRoot, dataPath, true),
79
+ end: endSandboxPrograms,
80
+ restartService: restartSandboxService,
81
+ confirm: (q) => io.confirm({
82
+ ...q,
83
+ danger: true,
84
+ noTerminal: { message: 'Nothing was closed.', fix: 'Add --yes to answer yes.' },
85
+ }),
86
+ say: (line) => {
87
+ lines.push(line);
88
+ io.log(line);
89
+ },
90
+ alsoService: input.service === true,
91
+ });
92
+ if (outcome === 'cleaned' || outcome === 'service-restarted')
93
+ new SandboxChannel(dataPath, () => false).reset();
94
+ const message = {
95
+ nothing: 'No Windows Sandbox program is left over.',
96
+ alive: lines[lines.length - 1] ?? 'A sandbox is working; nothing was closed.',
97
+ declined: 'Nothing was closed.',
98
+ cleaned: 'The leftover Windows Sandbox programs are closed. A new sandbox can start now.',
99
+ 'service-restarted': 'The Windows Sandbox service was restarted. A new sandbox can start now.',
100
+ failed: 'The Windows Sandbox service could not be restarted.',
101
+ }[outcome];
102
+ return { outcome, message };
103
+ });
60
104
  /**
61
105
  * Stopping ends every project's sessions: it says which, and the sessions of other projects need a yes (also on the
62
106
  * command line, where --yes gives it; a script without it fails rather than end them by accident).
@@ -8,7 +8,7 @@ import { cleanDataRun, pruneStorageRun, showDataRun } from './features/data/inde
8
8
  import { changeDataFolderRun, containedOffRun, containedOnRun, containedStatusRun, moveStorageRun, showStorageRun, useStorageRun, } from './features/storage/index.js';
9
9
  import { addProjectRun, allProjectsRun, chooseProjectsRun, excludeProjectRun, includeProjectRun, listProjectsRun, projectRootsRun, removeProjectRun, } from './features/projects/index.js';
10
10
  import { checkHealthRun, enableSandboxRun, lastHealthRun, repairHealthRun } from './features/health/index.js';
11
- import { hideWindowRun, sandboxStatusRun, showWindowRun, stopSandboxRun } from './features/sandbox/index.js';
11
+ import { cleanUpSandboxRun, hideWindowRun, sandboxStatusRun, showWindowRun, stopSandboxRun, } from './features/sandbox/index.js';
12
12
  import { listLogsRun, tailLogRun } from './features/logs/index.js';
13
13
  import { listAuditRun } from './features/audit/index.js';
14
14
  import { closeSessionRun, listSessionsRun, restartSessionsRun, startShellRun } from './features/sessions/index.js';
@@ -24,6 +24,7 @@ export const IMPLEMENTATIONS = [
24
24
  showWindowRun,
25
25
  hideWindowRun,
26
26
  stopSandboxRun,
27
+ cleanUpSandboxRun,
27
28
  listSessionsRun,
28
29
  restartSessionsRun,
29
30
  startShellRun,
@@ -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-IIHL7W6B.js";
6
- import "./chunk-MQIA7GNB.js";
5
+ } from "./chunk-N4GXYTLV.js";
6
+ import "./chunk-FRPLQLSY.js";
7
7
 
8
8
  // src/bin/agent-sandbox.ts
9
9
  run(process.argv.slice(2));