windows-development-sandbox 1.14.0 → 1.16.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 (100) hide show
  1. package/config.example.json +2 -1
  2. package/dist/app/features/accounts/accounts.def.js +4 -3
  3. package/dist/app/features/defs.d.ts +4 -2
  4. package/dist/app/features/defs.js +26 -12
  5. package/dist/app/features/projects/projects.def.d.ts +1 -1
  6. package/dist/app/features/projects/projects.run.d.ts +1 -1
  7. package/dist/app/features/projects/projects.run.js +10 -2
  8. package/dist/app/features/sandbox/sandbox.def.js +12 -6
  9. package/dist/app/features/sessions/sessions.def.js +1 -1
  10. package/dist/app/features/settings/categories.def.js +1 -0
  11. package/dist/bin/agent-sandbox.js +2 -2
  12. package/dist/bin/{chunk-F7IMME5G.js → chunk-7EXDUNE7.js} +98 -79
  13. package/dist/bin/{chunk-BQ23GBLC.js → chunk-HIC4CUMY.js} +1925 -1430
  14. package/dist/bin/{chunk-LQHEUN2A.js → chunk-PXTLKIV4.js} +1 -1
  15. package/dist/bin/claude-host.js +2 -2
  16. package/dist/bin/claude-sbx.js +2 -2
  17. package/dist/bin/codex-sbx.js +2 -2
  18. package/dist/bin/gemini-sbx.js +2 -2
  19. package/dist/bin/sandbox.js +2 -2
  20. package/dist/bin/sbx-hide.js +2 -2
  21. package/dist/bin/sbx-show.js +2 -2
  22. package/dist/cli/commands/projects/here.js +2 -0
  23. package/dist/cli/index.js +4 -1
  24. package/dist/cli/report.d.ts +7 -0
  25. package/dist/cli/report.js +31 -0
  26. package/dist/cli/services/health-service.js +15 -3
  27. package/dist/cli/services/projects-service.d.ts +8 -3
  28. package/dist/cli/services/projects-service.js +21 -4
  29. package/dist/core/config/defaults.js +1 -0
  30. package/dist/core/schema/config.d.ts +4 -0
  31. package/dist/core/schema/projects.d.ts +4 -0
  32. package/dist/core/schema/projects.js +3 -0
  33. package/dist/guest/boot/boot.js +8 -2
  34. package/dist/guest/daemon/daemon.js +3 -0
  35. package/dist/guest/daemon/live-folders.d.ts +48 -0
  36. package/dist/guest/daemon/live-folders.js +245 -0
  37. package/dist/guest/daemon/live.d.ts +4 -0
  38. package/dist/guest/daemon/live.js +57 -0
  39. package/dist/guest/entries/daemon.js +4 -2
  40. package/dist/guest/runtime/csc.d.ts +1 -0
  41. package/dist/guest/runtime/csc.js +1 -0
  42. package/dist/guest/runtime/secrets.d.ts +5 -2
  43. package/dist/guest/runtime/secrets.js +12 -5
  44. package/dist/host/engines/hcs/live-folders.d.ts +117 -0
  45. package/dist/host/engines/hcs/live-folders.js +347 -0
  46. package/dist/host/engines/index.d.ts +1 -0
  47. package/dist/host/engines/index.js +1 -0
  48. package/dist/host/launch/generate.js +8 -1
  49. package/dist/host/projects/join.d.ts +4 -0
  50. package/dist/host/projects/join.js +11 -3
  51. package/dist/host/projects/mapper.d.ts +34 -5
  52. package/dist/host/projects/mapper.js +88 -20
  53. package/dist/host/projects/registry.d.ts +2 -0
  54. package/dist/host/projects/registry.js +1 -1
  55. package/dist/host/secrets/launch-secrets.js +2 -1
  56. package/dist/runtime/agent.js +14 -0
  57. package/dist/runtime/daemon.js +466 -132
  58. package/dist/runtime/db.js +14 -0
  59. package/dist/runtime/main.js +26 -3
  60. package/dist/runtime/netproxy.js +14 -0
  61. package/dist/runtime/view.js +14 -0
  62. package/dist/selftest/accounts.test.js +22 -0
  63. package/dist/selftest/app.test.js +2 -2
  64. package/dist/selftest/index.js +2 -0
  65. package/dist/selftest/layout.test.js +1 -0
  66. package/dist/selftest/live-folders.test.d.ts +2 -0
  67. package/dist/selftest/live-folders.test.js +588 -0
  68. package/dist/selftest/projects.test.js +7 -7
  69. package/dist/selftest/support/engine-golden.js +1 -0
  70. package/dist/selftest/support/fixtures.d.ts +1 -0
  71. package/dist/selftest/support/live.js +2 -0
  72. package/dist/shared/channel/index.d.ts +1 -0
  73. package/dist/shared/channel/index.js +1 -0
  74. package/dist/shared/channel/live.d.ts +53 -0
  75. package/dist/shared/channel/live.js +92 -0
  76. package/dist/shared/channel/run-dir.d.ts +5 -0
  77. package/dist/shared/channel/run-dir.js +11 -0
  78. package/dist/shared/channel/secrets.d.ts +1 -0
  79. package/dist/shared/channel/secrets.js +5 -0
  80. package/dist/shared/paths/projects.d.ts +6 -1
  81. package/dist/shared/paths/projects.js +21 -1
  82. package/dist/sync/watcher.js +9 -0
  83. package/dist/web/index.html +2 -2
  84. package/dist/web/main-JTH3FT4O.js +221 -0
  85. package/dist/web/{main-UAQPI5XD.css → main-XT7T6ODM.css} +1 -1
  86. package/native/csharp/HcsHost/AssemblyInfo.cs +4 -4
  87. package/native/csharp/HcsHost/FolderAccess.cs +361 -0
  88. package/native/csharp/HcsHost/Hcs.cs +31 -3
  89. package/native/csharp/HcsHost/Program.cs +54 -6
  90. package/native/csharp/HcsHost/Share.cs +64 -11
  91. package/native/csharp/HcsHost/WsbGuard.cs +130 -0
  92. package/native/csharp/HcsHost/WsbShare.cs +295 -0
  93. package/native/csharp/HostTools/AssemblyInfo.cs +3 -3
  94. package/native/csharp/StorageHelper/AssemblyInfo.cs +3 -3
  95. package/native/scripts/contained-common.ps1 +4 -0
  96. package/native/scripts/hcs-live-share.ps1 +191 -0
  97. package/package.json +1 -1
  98. package/sandbox/csharp/BindMount.cs +120 -0
  99. package/schemas/config.schema.json +8 -0
  100. package/dist/web/main-6XNBTJLT.js +0 -221
@@ -574,6 +574,7 @@
574
574
  "askToAddRoot": true,
575
575
  "declinedRoots": [],
576
576
  "allowOutsideMappings": "ask",
577
- "wsbShare": true
577
+ "wsbShare": true,
578
+ "liveFolders": "off"
578
579
  }
579
580
  }
@@ -188,9 +188,10 @@ function loginItem(a, r) {
188
188
  ...(r.isActive ? [{ text: 'in the sandbox', tone: 'ok' }] : []),
189
189
  ...(r.isDefault ? [{ text: 'default', tone: 'accent' }] : []),
190
190
  ],
191
- description: 'Choose it to make the sandbox use this login.',
191
+ description: r.isActive ? 'The sandbox uses this login now.' : 'Choose it to make the sandbox use this login.',
192
192
  details: ['Agent ' + a.name, 'Email ' + (r.email ?? '-'), 'Saved ' + r.savedAt.slice(0, 16).replace('T', ' ')],
193
- primary: action(useAccount, { agent: a.name, name: r.name }, { verb: 'use' }),
193
+ // The login in use already offers nothing to switch to.
194
+ ...(r.isActive ? {} : { primary: action(useAccount, { agent: a.name, name: r.name }, { verb: 'use' }) }),
194
195
  actions: [
195
196
  action(defaultAccount, { agent: a.name, name: r.name }, { key: 'd', verb: 'make default', importance: 1 }),
196
197
  action(renameAccount, { agent: a.name, name: r.name }, { key: 'r', verb: 'rename', importance: 2 }),
@@ -244,7 +245,7 @@ export function agentPage(a) {
244
245
  export const accountsView = defineView({
245
246
  id: 'accounts',
246
247
  title: 'Accounts',
247
- summary: 'Logins saved on this PC, per agent. Use puts one into the sandbox; new launches start with the default.',
248
+ summary: 'Logins saved on this PC for each agent. Every launch starts with the default; "Use this login" switches now.',
248
249
  queries: { accounts: listAccounts },
249
250
  operations: [saveAccount, useAccount, defaultAccount, renameAccount, removeAccount],
250
251
  build: ({ accounts }) => accountsPage(accounts),
@@ -172,9 +172,9 @@ export declare const OPERATIONS: readonly [OperationDef<import("zod").ZodObject<
172
172
  message: import("zod").ZodString;
173
173
  }, import("zod/v4/core").$strip>, "projects.all">, OperationDef<import("zod").ZodObject<{
174
174
  change: import("zod").ZodDefault<import("zod").ZodEnum<{
175
- list: "list";
176
175
  add: "add";
177
176
  remove: "remove";
177
+ list: "list";
178
178
  }>>;
179
179
  rootFolder: import("zod").ZodOptional<import("zod").ZodString>;
180
180
  }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
@@ -1606,7 +1606,7 @@ export interface NavSection {
1606
1606
  readonly view?: ViewId;
1607
1607
  /** Where the area is today when it has no view yet. */
1608
1608
  readonly elsewhere?: string;
1609
- /** Entries under this one (Sandbox: Status, Projects). */
1609
+ /** Entries under this one: a group of the navigation (Sandbox: Overview, Projects, Apps). */
1610
1610
  readonly children?: readonly NavSection[];
1611
1611
  /** Views shown as more categories inside this entry's page (Settings: VPN, GitHub), not as entries of their own. */
1612
1612
  readonly embeds?: readonly {
@@ -1618,6 +1618,8 @@ export interface NavSection {
1618
1618
  export declare function navSections(nav: readonly NavSection[]): NavSection[];
1619
1619
  /** Every view the web's navigation opens. */
1620
1620
  export declare function navViews(nav: readonly NavSection[]): ViewId[];
1621
+ /** The group of the navigation a view's entry sits in (Updates: Maintenance), for the page's breadcrumb. */
1622
+ export declare function navGroupOf(nav: readonly NavSection[], view: string): NavSection | undefined;
1621
1623
  /** The entry whose page shows this view as one of its categories (VPN: Settings), if any. */
1622
1624
  export declare function embeddingOf(nav: readonly NavSection[], view: string): NavSection | undefined;
1623
1625
  export declare const NAV: readonly NavSection[];
@@ -140,32 +140,46 @@ export function navSections(nav) {
140
140
  export function navViews(nav) {
141
141
  return navSections(nav).flatMap((n) => [...(n.view ? [n.view] : []), ...(n.embeds ?? []).map((e) => e.view)]);
142
142
  }
143
+ /** The group of the navigation a view's entry sits in (Updates: Maintenance), for the page's breadcrumb. */
144
+ export function navGroupOf(nav, view) {
145
+ return navSections(nav).find((n) => n.children?.some((c) => c.view === view));
146
+ }
143
147
  /** The entry whose page shows this view as one of its categories (VPN: Settings), if any. */
144
148
  export function embeddingOf(nav, view) {
145
149
  return navSections(nav).find((n) => n.embeds?.some((e) => e.view === view));
146
150
  }
147
151
  export const NAV = [
148
- { label: 'Agents', view: 'agents' },
149
- { label: 'Sessions', view: 'sessions' },
150
- { label: 'Activity', view: 'audit' },
152
+ {
153
+ label: 'Workspace',
154
+ children: [
155
+ { label: 'Agents', view: 'agents' },
156
+ { label: 'Sessions', view: 'sessions' },
157
+ { label: 'Activity', view: 'audit' },
158
+ ],
159
+ },
151
160
  {
152
161
  label: 'Sandbox',
153
162
  children: [
154
- { label: 'Status', view: 'sandbox' },
163
+ { label: 'Overview', view: 'sandbox' },
155
164
  { label: 'Projects', view: 'projects' },
165
+ { label: 'Apps', view: 'apps' },
156
166
  ],
157
167
  },
158
- { label: 'Apps', view: 'apps' },
159
168
  {
160
- label: 'Settings',
161
- view: 'settings',
162
- embeds: [
163
- { label: 'VPN', view: 'vpn' },
164
- { label: 'GitHub', view: 'github' },
169
+ label: 'Configuration',
170
+ children: [
171
+ {
172
+ label: 'Settings',
173
+ view: 'settings',
174
+ embeds: [
175
+ { label: 'VPN', view: 'vpn' },
176
+ { label: 'GitHub', view: 'github' },
177
+ ],
178
+ },
179
+ { label: 'Accounts', view: 'accounts' },
180
+ { label: 'Remote access', view: 'access' },
165
181
  ],
166
182
  },
167
- { label: 'Accounts', view: 'accounts' },
168
- { label: 'Remote access', view: 'access' },
169
183
  {
170
184
  label: 'Maintenance',
171
185
  children: [
@@ -73,9 +73,9 @@ export declare const allProjects: import("../../kernel/define.js").OperationDef<
73
73
  }, z.core.$strip>, "projects.all">;
74
74
  export declare const projectRoots: import("../../kernel/define.js").OperationDef<z.ZodObject<{
75
75
  change: z.ZodDefault<z.ZodEnum<{
76
- list: "list";
77
76
  add: "add";
78
77
  remove: "remove";
78
+ list: "list";
79
79
  }>>;
80
80
  rootFolder: z.ZodOptional<z.ZodString>;
81
81
  }, z.core.$strip>, z.ZodObject<{
@@ -46,9 +46,9 @@ export declare const allProjectsRun: import("../../kernel/define.js").Implementa
46
46
  }, import("zod/v4/core").$strip>, "projects.all">, import("../../../cli/commands/context.js").Context>;
47
47
  export declare const projectRootsRun: import("../../kernel/define.js").Implementation<import("../../kernel/define.js").OperationDef<import("zod").ZodObject<{
48
48
  change: import("zod").ZodDefault<import("zod").ZodEnum<{
49
- list: "list";
50
49
  add: "add";
51
50
  remove: "remove";
51
+ list: "list";
52
52
  }>>;
53
53
  rootFolder: import("zod").ZodOptional<import("zod").ZodString>;
54
54
  }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
@@ -68,14 +68,22 @@ export const removeProjectRun = implement(removeProject, async (ctx, input, io)
68
68
  const dir = await pickFolder(ctx, io, input.projectFolder);
69
69
  const yes = await io.confirm({
70
70
  title: 'Forget ' + path.basename(dir) + '?',
71
- lines: ['The project is dropped from your list of projects.', 'Nothing in ' + dir + ' is touched.'],
71
+ lines: [
72
+ 'The project is dropped from your list of projects.',
73
+ 'Nothing in ' + dir + ' is touched.',
74
+ ...(new ProjectsService(ctx).isLive(dir)
75
+ ? [
76
+ 'It is a live folder: it is hidden in the running sandbox and its share removed (Windows asks for permission).',
77
+ ]
78
+ : []),
79
+ ],
72
80
  yes: 'Forget',
73
81
  danger: true,
74
82
  implied: true,
75
83
  });
76
84
  if (!yes)
77
85
  cancelled();
78
- const { dir: forgotten, removed } = new ProjectsService(ctx).forget(dir);
86
+ const { dir: forgotten, removed } = await new ProjectsService(ctx).forget(dir);
79
87
  return {
80
88
  message: removed
81
89
  ? 'Removed ' + forgotten + ' from your projects (nothing in the folder was touched).'
@@ -6,7 +6,7 @@ const ProjectSchema = z.object({
6
6
  name: z.string().describe('The project'),
7
7
  host: z.string().describe('Its folder on this PC'),
8
8
  guest: z.string().describe('Its folder in the sandbox'),
9
- mechanism: z.string().describe('How it got into the sandbox (launch, root, boot or share)'),
9
+ mechanism: z.string().describe('How it got into the sandbox (launch, root, boot, wsb or hcs)'),
10
10
  here: z.boolean().describe('The project of the current folder'),
11
11
  setup: z.object({ phase: z.string(), message: z.string() }).describe('How far its setup is'),
12
12
  databases: z.array(z.object({ database: z.string(), port: z.number(), state: z.string() })),
@@ -124,6 +124,8 @@ const MECHANISM_TEXT = {
124
124
  launch: 'started from here',
125
125
  root: 'in a project root',
126
126
  boot: 'mapped at start',
127
+ wsb: 'wsb share',
128
+ hcs: 'live folder through HCS, alpha',
127
129
  };
128
130
  function projectItem(p) {
129
131
  return {
@@ -161,7 +163,8 @@ function facts(o) {
161
163
  ? [
162
164
  fact('window', 'Window', s.headless ? 'hidden (sandbox show opens it)' : 'shown'),
163
165
  fact('uptime', 'Uptime', uptime(s) ?? '-'),
164
- fact('helper', 'Helper', s.daemon === '-' ? '-' : helperText(s.daemon)),
166
+ // The session count is its own fact: the helper only says whether it answers.
167
+ fact('helper', 'Helper', s.daemon === '-' ? '-' : s.daemon.startsWith('pong') ? 'Responding' : s.daemon),
165
168
  fact('sessions', 'Sessions', String(sessions)),
166
169
  ]
167
170
  : []),
@@ -193,9 +196,12 @@ function guardSection(o) {
193
196
  return {
194
197
  id: 'guard',
195
198
  title: 'Repo guard',
196
- summary: o.repoGuard.guarded +
197
- ' folder(s) guarded; ' +
198
- (findings.length ? findings.length + ' change(s) neutralised:' : 'nothing from the sandbox needed neutralising'),
199
+ summary: 'Watching ' +
200
+ plural(o.repoGuard.guarded, 'folder') +
201
+ '. ' +
202
+ (findings.length
203
+ ? plural(findings.length, 'change') + ' from the sandbox were blocked:'
204
+ : 'Nothing from the sandbox needed blocking.'),
199
205
  items: findings.slice(-10).map((f, i) => ({ id: 'guard:' + i, label: f, tone: 'warn' })),
200
206
  };
201
207
  }
@@ -247,7 +253,7 @@ export function sandboxPage(o) {
247
253
  export const sandboxView = defineView({
248
254
  id: 'sandbox',
249
255
  title: 'Sandbox',
250
- summary: 'What the sandbox is doing, its window, and stopping it.',
256
+ summary: 'Status, projects and controls of the running sandbox.',
251
257
  queries: { status: sandboxStatus },
252
258
  operations: [showWindow, hideWindow, stopSandbox],
253
259
  build: ({ status }) => sandboxPage(status),
@@ -240,7 +240,7 @@ export function sessionsPage(o) {
240
240
  export const sessionsView = defineView({
241
241
  id: 'sessions',
242
242
  title: 'Sessions',
243
- summary: 'The sessions of the running sandbox: open one, restart the agents, open a shell, close one.',
243
+ summary: 'Agent and shell sessions in the running sandbox.',
244
244
  queries: { sessions: listSessions },
245
245
  operations: [restartSessions, startShell, closeSession],
246
246
  build: ({ sessions }) => sessionsPage(sessions),
@@ -123,6 +123,7 @@ const LABELS = {
123
123
  'sandbox.pressureResponse': 'Slow builds down when memory runs low',
124
124
  'network.dns': 'DNS servers',
125
125
  'projects.wsbShare': 'Add folders while the sandbox runs',
126
+ 'projects.liveFolders': 'Add folders through HCS (alpha, untested)',
126
127
  'projects.all': 'Map all known projects at start',
127
128
  'claudeProfile.hostDir': 'Claude folder on this PC',
128
129
  'claudeProfile.globalStateFile': 'Claude state file on this PC',
@@ -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-F7IMME5G.js";
6
- import "./chunk-BQ23GBLC.js";
5
+ } from "./chunk-7EXDUNE7.js";
6
+ import "./chunk-HIC4CUMY.js";
7
7
 
8
8
  // src/bin/agent-sandbox.ts
9
9
  run(process.argv.slice(2));