windows-development-sandbox 1.28.0 → 1.29.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 (92) hide show
  1. package/README.md +5 -4
  2. package/dist/app/features/sandbox/sandbox.run.js +1 -1
  3. package/dist/bin/{chunk-UZ5S3EMB.js → chunk-O4EL32TM.js} +1 -1
  4. package/dist/bin/{chunk-CB44HTTN.js → chunk-QYRRWGFY.js} +1442 -1280
  5. package/dist/bin/sandbox.js +10772 -3
  6. package/dist/cli/commands/launch/launch.js +1 -1
  7. package/dist/cli/commands/launch/running.js +2 -2
  8. package/dist/cli/index.js +1 -1
  9. package/dist/cli/menu/pages/agents-page.js +1 -1
  10. package/dist/cli/services/sandbox-service.js +1 -1
  11. package/dist/core/schema/sandbox.js +1 -1
  12. package/dist/guest/apps/app-installer.js +1 -1
  13. package/dist/guest/apps/store-check.d.ts +2 -0
  14. package/dist/guest/apps/store-check.js +3 -1
  15. package/dist/guest/boot/guide-lines.d.ts +2 -0
  16. package/dist/guest/boot/guide-lines.js +7 -0
  17. package/dist/guest/boot/guide.js +2 -1
  18. package/dist/guest/toolchains/native/unreal.js +3 -22
  19. package/dist/host/accounts/deliver.js +16 -2
  20. package/dist/host/apps/store-package.d.ts +2 -0
  21. package/dist/host/apps/store-package.js +16 -15
  22. package/dist/host/channel/attach.js +1 -1
  23. package/dist/host/channel/channel.js +3 -3
  24. package/dist/host/guard/commands.js +1 -0
  25. package/dist/host/health/path-copies.d.ts +1 -1
  26. package/dist/host/health/path-copies.js +2 -2
  27. package/dist/host/install/index.d.ts +1 -1
  28. package/dist/host/install/index.js +1 -1
  29. package/dist/host/install/shims.d.ts +8 -1
  30. package/dist/host/install/shims.js +39 -1
  31. package/dist/host/launch/generate.js +4 -0
  32. package/dist/host/launch/index.d.ts +1 -0
  33. package/dist/host/launch/index.js +1 -0
  34. package/dist/host/launch/pc-logs.d.ts +6 -0
  35. package/dist/host/launch/pc-logs.js +129 -0
  36. package/dist/host/sandbox/platform.js +1 -1
  37. package/dist/host/sandbox/window.js +1 -1
  38. package/dist/runtime/agent.js +1 -1
  39. package/dist/runtime/daemon.js +72 -90
  40. package/dist/runtime/db.js +1 -1
  41. package/dist/runtime/main.js +35 -57
  42. package/dist/runtime/netproxy.js +1 -1
  43. package/dist/runtime/tool.js +1 -1
  44. package/dist/runtime/view.js +1 -1
  45. package/dist/selftest/cli.test.js +5 -5
  46. package/dist/selftest/config.test.js +1 -1
  47. package/dist/selftest/credentials.test.js +1 -1
  48. package/dist/selftest/engine.test.js +1 -1
  49. package/dist/selftest/generate.test.js +66 -1
  50. package/dist/selftest/hcs.test.js +1 -1
  51. package/dist/selftest/health.test.js +21 -10
  52. package/dist/selftest/heavy-load.test.js +1 -5
  53. package/dist/selftest/host-sim.test.js +13 -14
  54. package/dist/selftest/install.test.js +6 -2
  55. package/dist/selftest/store-app.test.js +62 -4
  56. package/dist/selftest/support/web-preview.js +2 -2
  57. package/dist/shared/accounts/kinds.d.ts +5 -0
  58. package/dist/shared/accounts/kinds.js +8 -0
  59. package/dist/shared/apps/index.d.ts +1 -1
  60. package/dist/shared/apps/index.js +1 -1
  61. package/dist/shared/apps/selection.d.ts +1 -0
  62. package/dist/shared/apps/selection.js +4 -3
  63. package/dist/shared/apps/store-package.d.ts +13 -0
  64. package/dist/shared/apps/store-package.js +24 -0
  65. package/dist/shared/memory/index.d.ts +0 -1
  66. package/dist/shared/memory/index.js +0 -1
  67. package/native/csharp/HcsHost/AssemblyInfo.cs +3 -3
  68. package/native/csharp/HostTools/AssemblyInfo.cs +3 -3
  69. package/native/csharp/HostTools/Window.cs +1 -1
  70. package/native/csharp/StorageHelper/AssemblyInfo.cs +3 -3
  71. package/native/scripts/get-store-package.ps1 +24 -13
  72. package/package.json +4 -14
  73. package/sandbox/scripts/install-store-package.ps1 +11 -4
  74. package/schemas/config.schema.json +1 -1
  75. package/schemas/sandbox-config.schema.json +1 -1
  76. package/dist/bin/agent-sandbox.d.ts +0 -2
  77. package/dist/bin/agent-sandbox.js +0 -9
  78. package/dist/bin/chunk-ZMZI3625.js +0 -10778
  79. package/dist/bin/claude-host.d.ts +0 -2
  80. package/dist/bin/claude-host.js +0 -11
  81. package/dist/bin/claude-sbx.d.ts +0 -2
  82. package/dist/bin/claude-sbx.js +0 -9
  83. package/dist/bin/codex-sbx.d.ts +0 -2
  84. package/dist/bin/codex-sbx.js +0 -9
  85. package/dist/bin/gemini-sbx.d.ts +0 -2
  86. package/dist/bin/gemini-sbx.js +0 -9
  87. package/dist/bin/sbx-hide.d.ts +0 -2
  88. package/dist/bin/sbx-hide.js +0 -9
  89. package/dist/bin/sbx-show.d.ts +0 -2
  90. package/dist/bin/sbx-show.js +0 -9
  91. package/dist/shared/memory/unreal-build.d.ts +0 -2
  92. package/dist/shared/memory/unreal-build.js +0 -21
@@ -3,7 +3,8 @@
3
3
  import fs from 'node:fs';
4
4
  import path from 'node:path';
5
5
  import { LocalOverridesSchema } from '../core/schema/index.js';
6
- import { buildWsb, gitIdentity, logonEnv, resolveMemoryMb } from '../host/launch/index.js';
6
+ import { buildWsb, gitIdentity, isSandboxSourceRepo, launchHasSandboxSource, logonEnv, resolveMemoryMb, stagePcLogs, } from '../host/launch/index.js';
7
+ import { pcLogsGuideLine } from '../guest/boot/guide-lines.js';
7
8
  export const generateSuite = {
8
9
  name: 'generate',
9
10
  title: 'generated .wsb, launch folder and session.json',
@@ -90,5 +91,69 @@ export const generateSuite = {
90
91
  resolveMemoryMb({ ...auto, maxMemoryPercent: 50 }, 60000 * MB, pcRam),
91
92
  ], [31744, 21504, 32768], "memory 'auto' follows free RAM minus the reserve and never takes more than maxMemoryPercent of the PC's RAM");
92
93
  await t.throws(() => resolveMemoryMb(auto, 9000 * MB, pcRam), 'Not enough free RAM', "memory 'auto' refuses when too little RAM is free");
94
+ t.section('PC logs for developing the sandbox');
95
+ const root = path.join(t.temp, 'pc-logs');
96
+ const URL = 'git+https://github.com/JoshDiDuca/windows-development-sandbox.git';
97
+ const project = (name, pkg) => {
98
+ t.write(path.join(root, name, 'package.json'), pkg);
99
+ return path.join(root, name);
100
+ };
101
+ const own = project('own', JSON.stringify({ name: 'windows-development-sandbox', repository: { type: 'git', url: URL } }));
102
+ const pkgOf = (url, name = 'windows-development-sandbox') => JSON.stringify({ name, repository: { url } });
103
+ const variants = [
104
+ project('v-https', pkgOf('https://github.com/joshdiduca/windows-development-sandbox')),
105
+ project('v-dotgit', pkgOf('https://github.com/JoshDiDuca/windows-development-sandbox.git')),
106
+ project('v-string', JSON.stringify({ name: 'windows-development-sandbox', repository: URL })),
107
+ ];
108
+ t.equal([isSandboxSourceRepo(own, own), ...variants.map((v) => isSandboxSourceRepo(v, own))], [true, true, true, true], 'a folder with the sandbox name and repository url is its source (git+, .git and case variants too)');
109
+ const big = project('big', pkgOf(URL) + ' '.repeat(300 * 1024));
110
+ const link = path.join(root, 'linked');
111
+ let linked = false;
112
+ try {
113
+ fs.symlinkSync(path.join(root, 'own'), link, 'junction');
114
+ linked = true;
115
+ }
116
+ catch {
117
+ // No permission for links here: that case is skipped.
118
+ }
119
+ t.equal([
120
+ isSandboxSourceRepo(project('wrong-url', pkgOf('https://github.com/someone/windows-development-sandbox')), own),
121
+ isSandboxSourceRepo(project('wrong-name', pkgOf(URL, 'other')), own),
122
+ isSandboxSourceRepo(project('invalid', '{ nope'), own),
123
+ isSandboxSourceRepo(path.join(root, 'missing'), own),
124
+ isSandboxSourceRepo(big, own),
125
+ linked ? isSandboxSourceRepo(link, own) : false,
126
+ ], [false, false, false, false, false, false], 'a wrong url or name, a missing, oversized or invalid package.json and a link are not the sandbox source');
127
+ t.check(launchHasSandboxSource([f.workA, own], own) && !launchHasSandboxSource([f.workA, f.workB], own), 'any project of the launch can be the source; a launch without it has none');
128
+ t.check(!fs.existsSync(path.join(gen.launchPath, 'pc-logs')), 'a launch of another project gets no pc-logs');
129
+ const storage = path.join(root, 'storage');
130
+ const dataPath = path.join(root, 'data-x');
131
+ for (let i = 0; i < 12; i++)
132
+ t.write(path.join(storage, 'logs', 'crashes', 'crash-2026-01-' + String(10 + i) + '.txt'), 'report ' + i);
133
+ t.write(path.join(storage, 'logs', 'host-memory-data-x.log'), 'start\n' + 'm'.repeat(600 * 1024) + 'END');
134
+ t.write(path.join(storage, 'logs', 'host-sync-data-x.log'), 'small');
135
+ t.write(path.join(storage, 'state', 'live-folders.log'), 'live');
136
+ t.write(path.join(storage, 'state', 'logins.json'), 'SECRET');
137
+ t.write(path.join(storage, 'secrets', 'x.json'), 'SECRET');
138
+ const launch = path.join(root, 'launch');
139
+ const out = stagePcLogs(storage, dataPath, launch);
140
+ const listing = (dir) => fs
141
+ .readdirSync(dir, { recursive: true, withFileTypes: true })
142
+ .flatMap((e) => e.isFile() ? [path.relative(out, path.join(e.parentPath, e.name)).replaceAll('\\', '/')] : []);
143
+ const copied = listing(out).sort();
144
+ const reports = copied.filter((n) => n.startsWith('crashes/'));
145
+ const memory = fs.readFileSync(path.join(out, 'host-memory.log'), 'utf8');
146
+ t.check(reports.length === 10 &&
147
+ reports.includes('crashes/crash-2026-01-21.txt') &&
148
+ !reports.includes('crashes/crash-2026-01-11.txt') &&
149
+ fs.statSync(path.join(out, 'host-memory.log')).size <= 256 * 1024 &&
150
+ memory.endsWith('END') &&
151
+ fs.readFileSync(path.join(out, 'README.txt'), 'utf8').includes('never commit'), 'the newest 10 crash reports, the last 256 KB of the memory log and a README are copied', copied.join(', '));
152
+ t.equal(copied.filter((n) => !n.startsWith('crashes/')), ['README.txt', 'host-memory.log', 'host-sync.log', 'live-folders.log'], 'nothing else from the storage folder is copied (no state, logins or secrets)');
153
+ t.section('the guide line for the PC logs');
154
+ t.equal([pcLogsGuideLine('C:\\AgentLaunch', () => false), pcLogsGuideLine('C:\\AgentLaunch', () => true)], [
155
+ [],
156
+ ['- PC crash reports and logs (this project only, read-only, never commit them): `C:\\AgentLaunch\\pc-logs`'],
157
+ ], 'the guide mentions the PC logs only when the folder exists');
93
158
  },
94
159
  };
@@ -514,7 +514,7 @@ export const hcsSuite = {
514
514
  LEFTOVER_DEPS.current = null;
515
515
  }
516
516
  t.section('sandbox engine check hcs');
517
- const out = await f.runBin('agent-sandbox', ['engine', 'check', 'hcs', '--config', t.writeJson(t.at('hcs-config.json'), {})], {
517
+ const out = await f.runBin('sandbox', ['engine', 'check', 'hcs', '--config', t.writeJson(t.at('hcs-config.json'), {})], {
518
518
  cwd: f.workA,
519
519
  env: { ...process.env, NO_COLOR: '1', AGENT_SANDBOX_HOME: t.dir('hcs-home'), AGENT_SANDBOX_HOST_ONLY: '1' },
520
520
  });
@@ -14,7 +14,7 @@ import { logText, writeRepairLog } from '../cli/commands/repair/log.js';
14
14
  import { HealthPrinter, summaryText } from '../cli/commands/repair/printer.js';
15
15
  import { runHealth } from '../cli/commands/repair/run.js';
16
16
  import { configBackups, findCommandCopies, fileOnDisk, groupEntries, inspectPointers, lineDiff, orphanWatchers, parseGroupMembers, parseIcacls, parseRegPath, pathHas, privacyProblems, repairPointers, runStateProblems, shimProblems, staleActiveRecords, staleLaunchFolders, watcherRecords, } from '../host/health/index.js';
17
- import { InstallLayout, writeShims } from '../host/install/index.js';
17
+ import { InstallLayout, REMOVED_COMMANDS, shimText, writeShims } from '../host/install/index.js';
18
18
  import { ActiveRecords } from '../host/sharing/index.js';
19
19
  const SID = 'S-1-5-21-1-2-3-1001';
20
20
  function fakeSystem(over = {}) {
@@ -225,7 +225,7 @@ function installChecks(t, mode) {
225
225
  t.writeJson(path.join(layout.packageDir(v), 'package.json'), { name: 'x', version: v, bin });
226
226
  };
227
227
  pkg('1.4.0', { sandbox: 'dist/bin/sandbox.js' });
228
- pkg('1.5.0', { sandbox: 'dist/bin/sandbox.js', 'agent-sandbox': 'dist/bin/agent-sandbox.js' });
228
+ pkg('1.5.0', { sandbox: 'dist/bin/sandbox.js', 'other-tool': 'dist/bin/other-tool.js' });
229
229
  t.dir('health', 'install-' + mode, 'WindowsDevelopmentSandbox', 'versions', '1.6.0', 'staging');
230
230
  t.write(layout.nodeExe('v22.1.0'), 'x');
231
231
  t.write(layout.nodeExe('v22.11.0'), 'x');
@@ -257,13 +257,13 @@ async function pointers(t, layout, env) {
257
257
  async function shims(t, layout, env) {
258
258
  t.section('install: shims');
259
259
  layout.setActive('1.5.0', 'v22.11.0');
260
- const bins = { sandbox: 'dist/bin/sandbox.js', 'agent-sandbox': 'dist/bin/agent-sandbox.js' };
260
+ const bins = { sandbox: 'dist/bin/sandbox.js', 'other-tool': 'dist/bin/other-tool.js' };
261
261
  writeShims(layout, bins);
262
262
  t.equal(shimProblems(layout, bins), { missing: [], different: [] }, 'freshly written shims are fine');
263
263
  fs.rmSync(path.join(layout.binDir, 'sandbox.cmd'));
264
- fs.writeFileSync(path.join(layout.binDir, 'agent-sandbox'), '#!/bin/sh\necho old\n');
264
+ fs.writeFileSync(path.join(layout.binDir, 'other-tool'), '#!/bin/sh\necho old\n');
265
265
  const problems = shimProblems(layout, bins);
266
- t.equal([problems.missing, problems.different], [['sandbox.cmd'], ['agent-sandbox']], 'a missing and an out-of-date shim are found');
266
+ t.equal([problems.missing, problems.different], [['sandbox.cmd'], ['other-tool']], 'a missing and an out-of-date shim are found');
267
267
  const { run, result } = await lookAt(env, 'install-shims');
268
268
  t.check(result.status === 'needs-you' && result.fixable, 'the shims check offers to rewrite them');
269
269
  const fixed = await run.fix([result]);
@@ -271,6 +271,19 @@ async function shims(t, layout, env) {
271
271
  t.equal(shimProblems(layout, bins), { missing: [], different: [] }, 'the shims read the active version again');
272
272
  fs.writeFileSync(path.join(layout.binDir, 'extra.cmd'), 'echo mine');
273
273
  t.check(fs.existsSync(path.join(layout.binDir, 'extra.cmd')), 'a file that is not one of ours is left alone');
274
+ t.section('install: shims of removed commands');
275
+ for (const [name, target] of Object.entries(REMOVED_COMMANDS)) {
276
+ fs.writeFileSync(path.join(layout.binDir, name + '.cmd'), shimText(name, 'cmd', target));
277
+ fs.writeFileSync(path.join(layout.binDir, name), shimText(name, 'sh', target));
278
+ }
279
+ fs.writeFileSync(path.join(layout.binDir, 'claude-sbx.cmd'), '@echo off rem mine');
280
+ writeShims(layout, bins);
281
+ const left = Object.keys(REMOVED_COMMANDS).flatMap((name) => [name + '.cmd', name].filter((file) => fs.existsSync(path.join(layout.binDir, file))));
282
+ t.equal(left, ['claude-sbx.cmd'], 'writing the shims removes the shims of old commands, but not a file that is not ours');
283
+ const kept = { ...bins, 'claude-sbx': 'dist/bin/claude-sbx.js' };
284
+ writeShims(layout, kept);
285
+ t.check(fs.readFileSync(path.join(layout.binDir, 'claude-sbx'), 'utf8') ===
286
+ shimText('claude-sbx', 'sh', kept['claude-sbx']), 'a package that still declares an old command keeps its shim (a rollback)');
274
287
  }
275
288
  async function copiesOnPath(t) {
276
289
  t.section('install: copies of the commands on the PATH');
@@ -282,9 +295,7 @@ async function copiesOnPath(t) {
282
295
  t.write(path.join(npmBin, 'sandbox'), '#!/bin/sh');
283
296
  t.write(path.join(npmBin, 'node_modules', 'windows-development-sandbox', 'package.json'), '{}');
284
297
  t.write(path.join(managedBin, 'sandbox.cmd'), '@echo off');
285
- t.write(path.join(managedBin, 'claude-sbx.cmd'), '@echo off');
286
- t.write(path.join(managedBin, 'claude-sbx.exe'), '');
287
- t.write(path.join(oldBin, 'claude-sbx.cmd'), '@echo off');
298
+ t.write(path.join(oldBin, 'sandbox.cmd'), '@echo off');
288
299
  const machinePath = [npmBin, managedBin].join(';');
289
300
  const userPath = [oldBin, npmBin, t.dir('copies', 'empty')].join(';');
290
301
  const pathext = '.COM;.EXE;.BAT;.CMD';
@@ -298,8 +309,8 @@ async function copiesOnPath(t) {
298
309
  });
299
310
  t.equal(found.map((c) => [c.kind, c.files.join(',')]), [
300
311
  ['npm', 'sandbox.cmd'],
301
- ['managed-machine', 'sandbox.cmd,claude-sbx.exe'],
302
- ['old-scripts', 'claude-sbx.cmd'],
312
+ ['managed-machine', 'sandbox.cmd'],
313
+ ['old-scripts', 'sandbox.cmd'],
303
314
  ], 'copies come in PATH order (machine first, a folder once), each command as its PATHEXT match, extensionless files ignored');
304
315
  const asked = [];
305
316
  const saved = process.env.ProgramFiles;
@@ -8,7 +8,7 @@ import { DEFAULTS } from '../core/config/index.js';
8
8
  import { isWin } from '../core/util/index.js';
9
9
  import { SYSTEM } from '../shared/system.js';
10
10
  import { GuestRequestSchema, PressureStatusSchema } from '../shared/channel/index.js';
11
- import { BURST_GATE, compilersAllowed, DIAL_CALM_SAMPLES, DIAL_STEPS, HOST_THRESHOLDS, buildJobs, cargoJobs, linkSlotsFor, usableCpus, effectiveCpuLimit, gentleCpuLimit, maxCompilers, chooseTargetDir, cargoLinkerVar, ubtBuildConfiguration, } from '../shared/memory/index.js';
11
+ import { BURST_GATE, compilersAllowed, DIAL_CALM_SAMPLES, DIAL_STEPS, HOST_THRESHOLDS, buildJobs, cargoJobs, linkSlotsFor, usableCpus, effectiveCpuLimit, gentleCpuLimit, maxCompilers, chooseTargetDir, cargoLinkerVar, } from '../shared/memory/index.js';
12
12
  import { HeartbeatSchema } from '../shared/channel/index.js';
13
13
  import { heartbeatLine, lastEndOf, vanishedCause, stopWarning } from '../host/life/index.js';
14
14
  import { cappedMemoryMb, decideMemory, memoryNote, residentCapMb, residentNote, resolveMemoryMb, } from '../host/launch/index.js';
@@ -96,10 +96,6 @@ export const heavyLoadSuite = {
96
96
  t.check(cargoJobs(16, 32, 3) === 2 && cargoJobs(16, 32, 8) === 1, 'fewer when several sessions build');
97
97
  t.check(cargoJobs(32, 32, 1) === 8, '32 processors, 32 GB: 8 jobs for one session');
98
98
  t.check(buildJobs(32, 32) === 8 && buildJobs(32, 64) === 19 && buildJobs(4, 64) === 4 && buildJobs(16, 4) === 2, 'the build budget: 3 GB per job after 6 GB, never more than the processors, at least 2');
99
- const ubt = ubtBuildConfiguration(buildJobs(20, 36));
100
- t.check(ubt.includes('<MaxParallelActions>10</MaxParallelActions>') &&
101
- ubt.includes('xmlns="https://www.unrealengine.com/BuildConfiguration"') &&
102
- ubtBuildConfiguration(0).includes('<MaxParallelActions>1</MaxParallelActions>'), "UnrealBuildTool's compiles at once follow the build budget (at least 1)", ubt);
103
99
  t.check(linkSlotsFor(32) === 8 && linkSlotsFor(16) === 3 && linkSlotsFor(8) === 2 && linkSlotsFor(128) === 8, 'links at once ("auto"): one per 3 GB after 6 GB, 2 to 8');
104
100
  t.check(usableCpus(32, 80) === 25 && usableCpus(32, null) === 32 && usableCpus(4, 10) === 2 && usableCpus(8, 100) === 8, 'the processors programs see follow the CPU cap (at least 2)');
105
101
  t.section('the governor: which programs');
@@ -27,9 +27,9 @@ const SANDBOX_ONLY_VARS = [
27
27
  'AGENT_SANDBOX_CONFIG',
28
28
  ];
29
29
  const PARALLEL = 2;
30
- const sandbox = (label, ...args) => ({ bin: 'agent-sandbox', label, args, config: true });
30
+ const sandbox = (label, ...args) => ({ bin: 'sandbox', label, args, config: true });
31
31
  const COMMANDS = [
32
- { bin: 'agent-sandbox', label: '--help', args: ['--help'] },
32
+ { bin: 'sandbox', label: '--help', args: ['--help'] },
33
33
  sandbox('status', 'status'),
34
34
  sandbox('status --json', 'status', '--json'),
35
35
  sandbox('sessions', 'sessions'),
@@ -38,26 +38,25 @@ const COMMANDS = [
38
38
  sandbox('vpn status', 'vpn', 'status'),
39
39
  sandbox('vpn status --json', 'vpn', 'status', '--json'),
40
40
  sandbox('account use nobody', 'account', 'use', 'nobody'),
41
- { bin: 'agent-sandbox', label: 'config path', args: ['config', 'path'] },
41
+ { bin: 'sandbox', label: 'config path', args: ['config', 'path'] },
42
42
  sandbox('config show', 'config', 'show'),
43
- { bin: 'agent-sandbox', label: 'config schema', args: ['config', 'schema'] },
43
+ { bin: 'sandbox', label: 'config schema', args: ['config', 'schema'] },
44
44
  sandbox('clean', 'clean'),
45
45
  sandbox('sync', 'sync'),
46
46
  sandbox('logs', 'logs'),
47
47
  sandbox('attach', 'attach'),
48
- { bin: 'agent-sandbox', label: 'exec', args: ['exec'], after: ['--', 'echo', 'hi'], config: true },
48
+ { bin: 'sandbox', label: 'exec', args: ['exec'], after: ['--', 'echo', 'hi'], config: true },
49
49
  sandbox('restart', 'restart'),
50
50
  sandbox('stop', 'stop'),
51
51
  sandbox('show', 'show'),
52
52
  sandbox('hide', 'hide'),
53
53
  sandbox('generate', 'generate'),
54
54
  sandbox('launch', 'launch'),
55
- { bin: 'agent-sandbox', label: 'unknown command', args: ['frobnicate'] },
56
- { bin: 'claude-sbx', label: 'claude-sbx --headless --continue', args: ['--headless', '--continue'], config: true },
57
- { bin: 'codex-sbx', label: 'codex-sbx', args: [], config: true },
58
- { bin: 'gemini-sbx', label: 'gemini-sbx', args: [], config: true },
59
- { bin: 'sbx-show', label: 'sbx-show', args: [], config: true },
60
- { bin: 'claude-host', label: 'claude-host --version', args: ['--version'] },
55
+ { bin: 'sandbox', label: 'unknown command', args: ['frobnicate'] },
56
+ { bin: 'sandbox', label: 'claude --headless --continue', args: ['claude', '--headless', '--continue'], config: true },
57
+ { bin: 'sandbox', label: 'codex', args: ['codex'], config: true },
58
+ { bin: 'sandbox', label: 'gemini', args: ['gemini'], config: true },
59
+ { bin: 'sandbox', label: 'host claude --version', args: ['host', 'claude', '--', '--version'] },
61
60
  ];
62
61
  export const hostSimSuite = {
63
62
  name: 'host-sim',
@@ -93,7 +92,7 @@ export const hostSimSuite = {
93
92
  const args = command.config ? [...command.args, '--config', pc.config, ...(command.after ?? [])] : command.args;
94
93
  const r = await f.runBin(command.bin, args, { cwd: project, env: envFor(pc.home), timeoutMs: 120000 });
95
94
  const crashed = r.status === null || r.status > 1 || /Unexpected error|node:internal|^\s+at .+:\d+:\d+\)?$/m.test(r.out);
96
- const unexplained = r.status === 1 && !/(^|\n)(Error: |error: )/.test(r.out) && command.bin !== 'claude-host';
95
+ const unexplained = r.status === 1 && !/(^|\n)(Error: |error: )/.test(r.out) && !command.label.startsWith('host ');
97
96
  if (crashed || unexplained)
98
97
  broken.push(command.label +
99
98
  ' (exit ' +
@@ -110,8 +109,8 @@ export const hostSimSuite = {
110
109
  await runOne(next.command, next.index);
111
110
  });
112
111
  const initRuns = (async () => {
113
- await runOne({ bin: 'agent-sandbox', label: 'config init', args: ['config', 'init'] }, 1000, initPc);
114
- await runOne({ bin: 'agent-sandbox', label: 'config init (again)', args: ['config', 'init'] }, 1001, initPc);
112
+ await runOne({ bin: 'sandbox', label: 'config init', args: ['config', 'init'] }, 1000, initPc);
113
+ await runOne({ bin: 'sandbox', label: 'config init (again)', args: ['config', 'init'] }, 1001, initPc);
115
114
  })();
116
115
  await Promise.all([...workers, initRuns]);
117
116
  t.equal(broken, [], 'all ' + (COMMANDS.length + 2) + ' commands work or fail with a clear message when run outside a sandbox');
@@ -3,7 +3,7 @@
3
3
  import fs from 'node:fs';
4
4
  import path from 'node:path';
5
5
  import { spawnSync } from 'node:child_process';
6
- import { shimText } from '../host/install/index.js';
6
+ import { REMOVED_COMMANDS, shimText } from '../host/install/index.js';
7
7
  import { nativeScript } from '../host/native.js';
8
8
  import { SYSTEM } from '../shared/system.js';
9
9
  /** The text install.ps1 writes for one shim, run by powershell.exe as a script file. */
@@ -39,11 +39,15 @@ function installerText(t, script) {
39
39
  const cases = [
40
40
  ['sandbox', undefined],
41
41
  ['claude-sbx', 'dist/bin/claude-sbx.js'],
42
- ['agent-sandbox', 'dist/bin/agent-sandbox.js'],
42
+ ['sbx-show', undefined],
43
43
  ];
44
44
  for (const [name, target] of cases)
45
45
  for (const kind of ['cmd', 'sh'])
46
46
  t.equal(printedShim(script, kind, name, target), shimText(name, kind, target), name + '.' + kind + ' is identical');
47
+ t.section('install.ps1 and package.json: the commands');
48
+ const bin = JSON.parse(fs.readFileSync(path.join(t.packageRoot, 'package.json'), 'utf8')).bin;
49
+ t.equal(Object.keys(bin).sort(), ['sandbox', 'windows-development-sandbox'], 'the package declares only those commands');
50
+ t.equal(Object.keys(REMOVED_COMMANDS).filter((name) => !source.includes("'" + name + "'")), [], 'the installer removes the shim of every removed command');
47
51
  t.includes(printedShim(script, 'cmd', 'x y'), 'exit', 'a name that is not a command name is refused');
48
52
  }
49
53
  export const installSuite = {
@@ -8,11 +8,12 @@ import { DEFAULTS } from '../core/config/index.js';
8
8
  import { OFF_IS_SAFER } from '../core/local-config/index.js';
9
9
  import { HOST_SOURCES } from '../host/accounts/index.js';
10
10
  import { prepareStorePackages, stagedPackageDir, StorePackages } from '../host/apps/index.js';
11
+ import { prepareServiceAccounts } from '../host/accounts/index.js';
11
12
  import { checkedPackage } from '../guest/apps/store-check.js';
12
13
  import { peekPendingPerplexityApp, perplexityAppPendingFile } from '../guest/accounts/kinds/perplexityapp.js';
13
14
  import { perplexityAppShortcutArgs, restoreAppSignIn } from '../guest/browser/perplexity-app.js';
14
- import { appsToInstall, appById, isStorePackageFile, PERPLEXITY_DESKTOP_FAMILY, PERPLEXITY_DESKTOP_PACKAGE, PERPLEXITY_DESKTOP_PRODUCT_ID, storeApps, StorePackageManifestSchema, storePackageRole, } from '../shared/apps/index.js';
15
- import { appUsersOn, PAYLOADS, SERVICE_SPECS, serviceWanted, usedByText, } from '../shared/accounts/index.js';
15
+ import { appsToInstall, appById, isStorePackageFile, PERPLEXITY_DESKTOP_FAMILY, PERPLEXITY_DESKTOP_PACKAGE, PERPLEXITY_DESKTOP_PRODUCT_ID, assignStoreRoles, storeApps, StorePackageManifestSchema, storePackageFitsX64, storePackageRole, } from '../shared/apps/index.js';
16
+ import { appsNeededFor, appUsersOn, PAYLOADS, SERVICE_SPECS, serviceWanted, usedByText, } from '../shared/accounts/index.js';
16
17
  const MAIN = 'PerplexityAI.PerplexityApp_1.2.3.0_x64__3jh4kjrg4dzr2.msix';
17
18
  const FRAMEWORK = 'Microsoft.VCLibs.140.00_14.0.33519.0_x64__8wekyb3d8bbwe.appx';
18
19
  const sha = (text) => crypto.createHash('sha256').update(text).digest('hex');
@@ -137,7 +138,9 @@ export const storeAppSuite = {
137
138
  run: fakeStore({}, false).run,
138
139
  });
139
140
  t.check(outcome.notes.length === 1 &&
140
- outcome.notes[0].includes('is not prepared') &&
141
+ outcome.notes[0].includes('NOT installed in this sandbox') &&
142
+ outcome.notes[0].includes('no Store access') &&
143
+ outcome.notes[0].startsWith('perplexity-desktop:') &&
141
144
  outcome.notes[0].includes('winget download --id 9P9XG917PWCJ'), 'with nothing kept the launch goes on, with a note that says what to fix');
142
145
  const good = await prepareStorePackages([app], {
143
146
  storageRoot: emptyRoot,
@@ -145,12 +148,67 @@ export const storeAppSuite = {
145
148
  run: fakeStore({ [MAIN]: 'a' }).run,
146
149
  });
147
150
  t.check(good.notes[0].includes('downloaded') && fs.existsSync(path.join(stagedPackageDir(emptyData, app.id), MAIN)), 'and a working Store stages it');
151
+ t.section('frameworks: the right builds, and the app told from them');
152
+ const VCLIBS_X86 = 'Microsoft.VCLibs.140.00_14.0.33519.0_x86__8wekyb3d8bbwe.appx';
153
+ const VCLIBS_ARM = 'Microsoft.VCLibs.140.00_14.0.33519.0_arm64__8wekyb3d8bbwe.appx';
154
+ t.check(storePackageFitsX64(FRAMEWORK) &&
155
+ !storePackageFitsX64(VCLIBS_X86) &&
156
+ !storePackageFitsX64(VCLIBS_ARM) &&
157
+ storePackageFitsX64('Microsoft.WindowsAppRuntime.1.6_6000.311.0_neutral__8wekyb3d8bbwe.msix'), '32-bit and ARM frameworks are left out, x64 and neutral ones stay');
158
+ t.equal(assignStoreRoles(['Perplexity_x64.msixbundle', FRAMEWORK], PERPLEXITY_DESKTOP_PACKAGE, [FRAMEWORK]).map((f) => f.role), ['main', 'dependency'], 'an app winget names another way is still found: it is the file outside Dependencies');
159
+ t.check((() => {
160
+ try {
161
+ assignStoreRoles([FRAMEWORK], PERPLEXITY_DESKTOP_PACKAGE, [FRAMEWORK]);
162
+ return false;
163
+ }
164
+ catch (e) {
165
+ return String(e).includes('only frameworks');
166
+ }
167
+ })(), 'frameworks without the app are refused with a plain reason');
168
+ const rich = await new StorePackages({
169
+ storageRoot: t.dir('pc3', 'storage'),
170
+ dataPath: t.dir('pc3', 'storage', 'data', 'set'),
171
+ run: (_exe, args) => {
172
+ const out = args[args.indexOf('-OutDir') + 1];
173
+ fs.mkdirSync(out, { recursive: true });
174
+ for (const n of ['Perplexity_1.2_x64.msixbundle', FRAMEWORK, VCLIBS_X86, VCLIBS_ARM])
175
+ fs.writeFileSync(path.join(out, n), n);
176
+ return Promise.resolve({
177
+ status: 0,
178
+ stdout: JSON.stringify({
179
+ ok: true,
180
+ message: 'ok',
181
+ files: ['Perplexity_1.2_x64.msixbundle', FRAMEWORK, VCLIBS_X86, VCLIBS_ARM],
182
+ dependencies: [FRAMEWORK, VCLIBS_X86, VCLIBS_ARM],
183
+ }),
184
+ stderr: '',
185
+ });
186
+ },
187
+ }).fetch(app);
188
+ t.check(rich.files.map((f) => f.role).join() === 'main,dependency' &&
189
+ rich.files.every((f) => storePackageFitsX64(f.name)), 'a fetch keeps the app and only the x64 framework, with the roles the script reported');
190
+ t.section('a sign-in for an app that is not installed says so');
191
+ t.equal(appsNeededFor('perplexityapp'), ['perplexity-desktop'], 'the app a sign-in needs');
192
+ t.equal(appsNeededFor('epic'), [], 'a sign-in a toolchain uses needs no app');
193
+ const sharing = { ...DEFAULTS, accounts: { ...DEFAULTS.accounts, perplexityapp: { share: true } } };
194
+ const deliverFor = (on) => prepareServiceAccounts({
195
+ config: sharing,
196
+ others: [],
197
+ on: new Set(on),
198
+ storageRoot: t.dir('pc4', 'storage'),
199
+ dataPath: t.dir('pc4', 'storage', 'data', 'set'),
200
+ launchId: 'L-1',
201
+ isRunning: () => false,
202
+ });
203
+ const missing = deliverFor([]);
204
+ t.check(missing.notes.some((n) => n.includes('accounts.perplexityapp.share') && n.includes('Fix: turn the app on in Apps')) && !missing.leased.length, 'share on with the app off: a launch note names the setting and the app to turn on');
205
+ t.check(!deliverFor(['perplexityapp']).notes.some((n) => n.includes('is not installed')), 'with the app on there is no such note');
148
206
  t.section('the sandbox checks the kept files');
149
207
  const checked = await checkedPackage(staged);
150
208
  t.check(path.basename(checked.main) === MAIN && checked.dependencies.map((d) => path.basename(d)).join() === FRAMEWORK, 'the app and its frameworks, in order');
151
209
  fs.writeFileSync(path.join(staged, FRAMEWORK), 'tampered');
152
210
  t.check((await t.thrownMessage(() => checkedPackage(staged))).includes('missing or changed'), 'a changed file stops the install');
153
- t.check((await t.thrownMessage(() => checkedPackage(t.dir('nothing')))).includes('not handed over'), 'no package says so');
211
+ t.check((await t.thrownMessage(() => checkedPackage(t.dir('nothing')))).includes('did not hand over its package'), 'no package says so');
154
212
  t.section('the shortcut');
155
213
  const args = perplexityAppShortcutArgs('C:\\AgentLaunch\\sandbox\\scripts\\start-store-app.ps1', 'C:\\Users\\X\\profile');
156
214
  t.check(args.includes('-File "C:\\AgentLaunch\\sandbox\\scripts\\start-store-app.ps1"') &&
@@ -563,7 +563,7 @@ export function sampleAuditLog(dir) {
563
563
  proc(94, 'C:\\AgentData\\tools\\uv\\uv.exe', 'uv tool install aider-chat', {}),
564
564
  { t: at(90), k: 'session', a: 'start', p: 'claude', w: SESSIONS[0].cwd, c: claude.s, ...claude, sv: undefined },
565
565
  proc(88, git, 'git status --short', claude),
566
- proc(87, node, 'node dist/bin/agent-sandbox.js self-test audit', claude, 2),
566
+ proc(87, node, 'node dist/bin/sandbox.js self-test audit', claude, 2),
567
567
  file(80, 'src/host/audit/collect.ts', ws, claude),
568
568
  file(79, 'src/app/features/audit/audit.def.ts', ws, claude),
569
569
  proc(70, 'C:\\nvm\\nodejs\\npm.cmd', 'npm run build', claude),
@@ -613,7 +613,7 @@ export function sampleAuditLog(dir) {
613
613
  ...codex,
614
614
  sv: undefined,
615
615
  },
616
- proc(5, node, 'node dist/bin/agent-sandbox.js self-test app', claude),
616
+ proc(5, node, 'node dist/bin/sandbox.js self-test app', claude),
617
617
  { t: at(4), k: 'remote', p: 'sessions.restart', a: 'ok', au: "device Josh's iPhone" },
618
618
  { t: at(3), k: 'remote', p: 'settings.set', a: 'refused', au: 'Windows user DESKTOP-JOSH\\sam' },
619
619
  ]);
@@ -58,3 +58,8 @@ export declare function usedByText(usedBy: readonly string[]): string;
58
58
  export declare function appUsersOn(install: readonly string[]): AppUser[];
59
59
  /** Whether a sign-in is wanted when these toolchains (and apps) are on. */
60
60
  export declare function serviceWanted(kind: ServiceKind, on: ReadonlySet<UsedBy>): boolean;
61
+ /**
62
+ * For a sign-in that only apps use (Perplexity API, Comet, the Perplexity app): the apps.install ids to turn on for it
63
+ * to be handed over; empty for a kind a toolchain uses. A sign-in shared with its app off would otherwise do nothing.
64
+ */
65
+ export declare function appsNeededFor(kind: ServiceKind): string[];
@@ -256,3 +256,11 @@ export function serviceWanted(kind, on) {
256
256
  return true;
257
257
  return spec.usedBy.includes('android') && ANDROID_USERS.some((u) => on.has(u));
258
258
  }
259
+ /**
260
+ * For a sign-in that only apps use (Perplexity API, Comet, the Perplexity app): the apps.install ids to turn on for it
261
+ * to be handed over; empty for a kind a toolchain uses. A sign-in shared with its app off would otherwise do nothing.
262
+ */
263
+ export function appsNeededFor(kind) {
264
+ const { usedBy } = SERVICE_SPECS[kind];
265
+ return usedBy.every((u) => u in APP_USERS) ? usedBy.flatMap((u) => [...APP_USERS[u]]) : [];
266
+ }
@@ -5,4 +5,4 @@ export { exeMatcher, appsToInstall, appsNeedMesa, appsNeedUv, storeApps } from '
5
5
  export { signerMatches, type AppSigner, type Signature } from '../signer.js';
6
6
  export { AGENTS_WEB_APPS, BROWSER_USE_VERSION, CHROME_DEVTOOLS_MCP_VERSION, COMET_DOWNLOAD_URL, PERPLEXITY_DESKTOP_FAMILY, PERPLEXITY_DESKTOP_PACKAGE, PERPLEXITY_DESKTOP_PRODUCT_ID, COMET_MCP_VERSION, COMET_SIGNER, PERPLEXITY_COMPUTER_MCP_URL, PERPLEXITY_MCP_VERSION, } from './catalogue-agents-web.js';
7
7
  export { ADDON_MCP_PREFIX, addonGuideLine, addonMcpServer, type AddonPlace, claudeManagedSettings, codexWithAddonMcpServers, type CodexMcpServer, HEADROOM_ENV, HEADROOM_PORT, HEADROOM_UPSTREAM, headroomArgs, headroomEnv, headroomGuideLine, headroomUrl, type ManagedSettings, type McpServerConfig, rtkHookCommand, RTK_GUIDE_LINE, withAddonMcpServers, } from './agent-addons.js';
8
- export { isStorePackageFile, STORE_PACKAGE_MANIFEST, StorePackageFileSchema, StorePackageManifestSchema, storePackageRole, type StorePackageFile, type StorePackageManifest, } from './store-package.js';
8
+ export { isStorePackageFile, STORE_PACKAGE_MANIFEST, StorePackageFileSchema, StorePackageManifestSchema, storePackageRole, storePackageFitsX64, assignStoreRoles, type StorePackageFile, type StorePackageManifest, } from './store-package.js';
@@ -5,4 +5,4 @@ export { exeMatcher, appsToInstall, appsNeedMesa, appsNeedUv, storeApps } from '
5
5
  export { signerMatches } from '../signer.js';
6
6
  export { AGENTS_WEB_APPS, BROWSER_USE_VERSION, CHROME_DEVTOOLS_MCP_VERSION, COMET_DOWNLOAD_URL, PERPLEXITY_DESKTOP_FAMILY, PERPLEXITY_DESKTOP_PACKAGE, PERPLEXITY_DESKTOP_PRODUCT_ID, COMET_MCP_VERSION, COMET_SIGNER, PERPLEXITY_COMPUTER_MCP_URL, PERPLEXITY_MCP_VERSION, } from './catalogue-agents-web.js';
7
7
  export { ADDON_MCP_PREFIX, addonGuideLine, addonMcpServer, claudeManagedSettings, codexWithAddonMcpServers, HEADROOM_ENV, HEADROOM_PORT, HEADROOM_UPSTREAM, headroomArgs, headroomEnv, headroomGuideLine, headroomUrl, rtkHookCommand, RTK_GUIDE_LINE, withAddonMcpServers, } from './agent-addons.js';
8
- export { isStorePackageFile, STORE_PACKAGE_MANIFEST, StorePackageFileSchema, StorePackageManifestSchema, storePackageRole, } from './store-package.js';
8
+ export { isStorePackageFile, STORE_PACKAGE_MANIFEST, StorePackageFileSchema, StorePackageManifestSchema, storePackageRole, storePackageFitsX64, assignStoreRoles, } from './store-package.js';
@@ -14,6 +14,7 @@ export declare function appsNeedMesa(apps: Config['apps'] | undefined): boolean;
14
14
  /** The chosen apps that come from the Microsoft Store: what this PC has to fetch for them. */
15
15
  export declare function storeApps(install: readonly string[]): {
16
16
  id: string;
17
+ name: string;
17
18
  productId: string;
18
19
  packageName: string;
19
20
  }[];
@@ -50,9 +50,10 @@ export function appsNeedMesa(apps) {
50
50
  /** The chosen apps that come from the Microsoft Store: what this PC has to fetch for them. */
51
51
  export function storeApps(install) {
52
52
  return install.flatMap((id) => {
53
- const source = appById(id)?.source;
54
- return source !== undefined && 'store' in source
55
- ? [{ id, productId: source.store.productId, packageName: source.store.packageName }]
53
+ const entry = appById(id);
54
+ const source = entry?.source;
55
+ return entry && source !== undefined && 'store' in source
56
+ ? [{ id, name: entry.name, productId: source.store.productId, packageName: source.store.packageName }]
56
57
  : [];
57
58
  });
58
59
  }
@@ -29,3 +29,16 @@ export declare const STORE_PACKAGE_MANIFEST = "store-package.json";
29
29
  export declare function isStorePackageFile(name: string): boolean;
30
30
  /** The app itself (named <package name>_<version>_...), as opposed to a framework it depends on (VCLibs ...). */
31
31
  export declare function storePackageRole(fileName: string, packageName: string): 'main' | 'dependency';
32
+ /**
33
+ * Whether a dependency package suits this sandbox (64-bit Windows): winget may also fetch the 32-bit and ARM builds
34
+ * of a framework, and Add-AppxPackage is only handed the builds that can run here.
35
+ */
36
+ export declare function storePackageFitsX64(fileName: string): boolean;
37
+ /**
38
+ * Which fetched file is the app and which are its frameworks. The app is named after its package; when winget names
39
+ * it otherwise, the one file that did not come from the Dependencies folder is the app. Throws when that is unclear.
40
+ */
41
+ export declare function assignStoreRoles(names: readonly string[], packageName: string, dependencyNames: readonly string[]): {
42
+ name: string;
43
+ role: 'main' | 'dependency';
44
+ }[];
@@ -32,3 +32,27 @@ export function isStorePackageFile(name) {
32
32
  export function storePackageRole(fileName, packageName) {
33
33
  return fileName.toLowerCase().startsWith(packageName.toLowerCase() + '_') ? 'main' : 'dependency';
34
34
  }
35
+ /**
36
+ * Whether a dependency package suits this sandbox (64-bit Windows): winget may also fetch the 32-bit and ARM builds
37
+ * of a framework, and Add-AppxPackage is only handed the builds that can run here.
38
+ */
39
+ export function storePackageFitsX64(fileName) {
40
+ return !/[_.-](x86|arm|arm64)(?=[_.-]|$)/i.test(fileName.replace(/\.(msix|appx|msixbundle|appxbundle)$/i, ''));
41
+ }
42
+ /**
43
+ * Which fetched file is the app and which are its frameworks. The app is named after its package; when winget names
44
+ * it otherwise, the one file that did not come from the Dependencies folder is the app. Throws when that is unclear.
45
+ */
46
+ export function assignStoreRoles(names, packageName, dependencyNames) {
47
+ const listed = new Set(dependencyNames.map((n) => n.toLowerCase()));
48
+ let mains = names.filter((n) => storePackageRole(n, packageName) === 'main');
49
+ if (mains.length === 0)
50
+ mains = names.filter((n) => !listed.has(n.toLowerCase()));
51
+ if (mains.length > 1)
52
+ mains = mains.filter(storePackageFitsX64);
53
+ if (mains.length !== 1)
54
+ throw new Error(mains.length === 0
55
+ ? 'winget gave only frameworks, not the app itself (' + names.join(', ') + ')'
56
+ : 'winget gave more than one app package and it is not clear which to install (' + mains.join(', ') + ')');
57
+ return names.map((name) => ({ name, role: name === mains[0] ? 'main' : 'dependency' }));
58
+ }
@@ -1,2 +1 @@
1
1
  export * from './policy.js';
2
- export * from './unreal-build.js';
@@ -1,2 +1 @@
1
1
  export * from './policy.js';
2
- export * from './unreal-build.js';
@@ -8,9 +8,9 @@ using System.Runtime.InteropServices;
8
8
  [assembly: AssemblyCompany("windows-development-sandbox (open source)")]
9
9
  [assembly: AssemblyProduct("Windows Development Sandbox")]
10
10
  [assembly: AssemblyCopyright("MIT License, windows-development-sandbox contributors")]
11
- [assembly: AssemblyVersion("1.28.0.0")]
12
- [assembly: AssemblyFileVersion("1.28.0.0")]
13
- [assembly: AssemblyInformationalVersion("1.28.0")]
11
+ [assembly: AssemblyVersion("1.29.0.0")]
12
+ [assembly: AssemblyFileVersion("1.29.0.0")]
13
+ [assembly: AssemblyInformationalVersion("1.29.0")]
14
14
  [assembly: ComVisible(false)]
15
15
  // computecore.dll and computestorage.dll are not KnownDLLs: load them from System32 only, never from the exe's folder.
16
16
  [assembly: DefaultDllImportSearchPaths(DllImportSearchPath.System32)]
@@ -8,7 +8,7 @@ using System.Runtime.InteropServices;
8
8
  [assembly: AssemblyCompany("windows-development-sandbox (open source)")]
9
9
  [assembly: AssemblyProduct("Windows Development Sandbox")]
10
10
  [assembly: AssemblyCopyright("MIT License, windows-development-sandbox contributors")]
11
- [assembly: AssemblyVersion("1.28.0.0")]
12
- [assembly: AssemblyFileVersion("1.28.0.0")]
13
- [assembly: AssemblyInformationalVersion("1.28.0")]
11
+ [assembly: AssemblyVersion("1.29.0.0")]
12
+ [assembly: AssemblyFileVersion("1.29.0.0")]
13
+ [assembly: AssemblyInformationalVersion("1.29.0")]
14
14
  [assembly: ComVisible(false)]
@@ -1,4 +1,4 @@
1
- // The Windows Sandbox client window: hidden for a headless sandbox (the VM keeps running), shown again by sbx-show.
1
+ // The Windows Sandbox client window: hidden for a headless sandbox (the VM keeps running), shown again by sandbox show.
2
2
  // HostTools hide-window / show-window, or Add-Type from scripts\*-window.ps1 when HostTools cannot be built. C# 5.
3
3
  using System;
4
4
  using System.Collections.Generic;
@@ -9,7 +9,7 @@ using System.Runtime.InteropServices;
9
9
  [assembly: AssemblyCompany("windows-development-sandbox (open source)")]
10
10
  [assembly: AssemblyProduct("Windows Development Sandbox")]
11
11
  [assembly: AssemblyCopyright("MIT License, windows-development-sandbox contributors")]
12
- [assembly: AssemblyVersion("1.28.0.0")]
13
- [assembly: AssemblyFileVersion("1.28.0.0")]
14
- [assembly: AssemblyInformationalVersion("1.28.0")]
12
+ [assembly: AssemblyVersion("1.29.0.0")]
13
+ [assembly: AssemblyFileVersion("1.29.0.0")]
14
+ [assembly: AssemblyInformationalVersion("1.29.0")]
15
15
  [assembly: ComVisible(false)]