windows-development-sandbox 1.11.6 → 1.11.7

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.
@@ -115,7 +115,7 @@ function projectItem(p) {
115
115
  icon: 'project',
116
116
  hint: p.running ?? (p.root ? 'in a root' : p.excluded ? 'excluded' : p.mappedAtStart ? 'mapped' : '-'),
117
117
  description: p.here ? 'This is the folder you started the menu in.' : undefined,
118
- details: ['At start ' + atStart(p), 'In sandbox ' + (p.running ?? '-')],
118
+ details: ['At start: ' + atStart(p), 'In the sandbox: ' + (p.running ?? 'not open')],
119
119
  cells: { here: p.here ? '*' : '', folder: p.folder, atStart: atStart(p), running: p.running ?? '-' },
120
120
  actions: [
121
121
  action(removeProject, { projectFolder: p.folder }, { key: 'f', verb: 'forget project', importance: 1 }),
@@ -2,7 +2,7 @@ import type { Context } from '../../../cli/commands/context.js';
2
2
  import type { MigrateContext } from '../../../cli/services/index.js';
3
3
  import type { UpdateResult } from '../../../host/update/index.js';
4
4
  /** One sentence for what an update run did, and whether it counts as done (1: it failed, or is waiting by hand). */
5
- export declare function describeResult(result: UpdateResult | null, unattended: boolean): {
5
+ export declare function describeResult(result: UpdateResult | null, _unattended: boolean): {
6
6
  line: string;
7
7
  code: number;
8
8
  };
@@ -3,7 +3,8 @@
3
3
  import { userDir } from '../../../core/config/index.js';
4
4
  import { holdsData, oldDefaultRoot } from '../../../host/storage/index.js';
5
5
  /** One sentence for what an update run did, and whether it counts as done (1: it failed, or is waiting by hand). */
6
- export function describeResult(result, unattended) {
6
+ // _unattended: kept for the callers; every result now reads the same whether a person or the schedule ran it.
7
+ export function describeResult(result, _unattended) {
7
8
  if (!result)
8
9
  return { line: 'Another update is running right now.', code: 0 };
9
10
  if (result.kind === 'installed')
@@ -12,7 +13,7 @@ export function describeResult(result, unattended) {
12
13
  (result.previous ? 'from ' + result.previous + ' ' : '') +
13
14
  'to ' +
14
15
  result.version +
15
- '. Open a new terminal to use it.',
16
+ '. New commands use it (open a new terminal); a running sandbox keeps the version it started with until it restarts.',
16
17
  code: 0,
17
18
  };
18
19
  if (result.kind === 'failed')
@@ -34,11 +35,6 @@ export function describeResult(result, unattended) {
34
35
  switch (result.reason) {
35
36
  case 'up-to-date':
36
37
  return { line: 'You have the newest version (' + result.current + ').', code: 0 };
37
- case 'sandbox-running':
38
- return {
39
- line: 'A Windows Sandbox is running, so nothing was installed. Close it (sandbox stop), then update again.',
40
- code: unattended ? 0 : 1,
41
- };
42
38
  case 'failed-before':
43
39
  return {
44
40
  line: 'The newest version failed to install before. It is not tried again until a newer one is released.',
@@ -137,7 +137,7 @@ export function updatesPage(s, latest) {
137
137
  tone: s.autoUpdate ? 'ok' : 'muted',
138
138
  description: machine
139
139
  ? 'For everyone: needs an admin terminal.'
140
- : 'A new version installs when no sandbox runs.',
140
+ : 'A new version installs once a day, even while a sandbox runs (it keeps its version until it restarts).',
141
141
  keys: 'Enter switches them.',
142
142
  primary: action(runUpdate, { auto: nextAuto }, {
143
143
  label: 'Automatic updates ' + nextAuto,
@@ -1,6 +1,6 @@
1
1
  import type { ReleaseInfo } from './release-source.js';
2
2
  import type { UpdateState } from './update-state.js';
3
- export type SkipReason = 'no-release' | 'up-to-date' | 'failed-before' | 'sandbox-running' | 'too-soon';
3
+ export type SkipReason = 'no-release' | 'up-to-date' | 'failed-before' | 'too-soon';
4
4
  export type UpdateDecision = {
5
5
  kind: 'install';
6
6
  release: ReleaseInfo;
@@ -24,6 +24,7 @@ export interface DecisionInput {
24
24
  export declare function isTooSoon(state: UpdateState, now: Date, minHours: number): boolean;
25
25
  /**
26
26
  * Install only a version newer than the current one, and never a version that failed before (or an older one than
27
- * the failure). The sandbox must not be running: its files are in use.
27
+ * the failure). A running sandbox is no reason to wait: each version has its own folder, the sandbox and this PC's
28
+ * helpers keep running the version they started with, and old versions are only removed when no sandbox runs.
28
29
  */
29
30
  export declare function decideUpdate(input: DecisionInput): UpdateDecision;
@@ -12,14 +12,13 @@ export function isTooSoon(state, now, minHours) {
12
12
  }
13
13
  /**
14
14
  * Install only a version newer than the current one, and never a version that failed before (or an older one than
15
- * the failure). The sandbox must not be running: its files are in use.
15
+ * the failure). A running sandbox is no reason to wait: each version has its own folder, the sandbox and this PC's
16
+ * helpers keep running the version they started with, and old versions are only removed when no sandbox runs.
16
17
  */
17
18
  export function decideUpdate(input) {
18
19
  const skip = (reason) => ({ kind: 'skip', reason });
19
20
  if (!input.force && isTooSoon(input.state, input.now, input.minHoursBetweenChecks))
20
21
  return skip('too-soon');
21
- if (input.sandboxRunning)
22
- return skip('sandbox-running');
23
22
  if (!input.latest)
24
23
  return skip('no-release');
25
24
  const latest = input.latest;
@@ -70,7 +70,10 @@ export declare class Updater {
70
70
  private checkEntry;
71
71
  /** Through the shim, as a user runs it: the pointers now name this version. */
72
72
  private checkShim;
73
- /** Records the update, keeps the replaced version for a rollback and removes the rest. */
73
+ /**
74
+ * Records the update, keeps the replaced version for a rollback and removes the rest, but only when no sandbox runs:
75
+ * its helpers on this PC may still run an older version from its folder.
76
+ */
74
77
  private finish;
75
78
  private discard;
76
79
  private recordFailure;
@@ -60,14 +60,14 @@ export class Updater {
60
60
  const force = options.force ?? false;
61
61
  // The gates that need no network run first, so a skipped check costs nothing.
62
62
  const early = decideUpdate({ ...base, latest: null, sandboxRunning: options.sandboxRunning, force });
63
- if (early.kind === 'skip' && (early.reason === 'too-soon' || early.reason === 'sandbox-running'))
63
+ if (early.kind === 'skip' && early.reason === 'too-soon')
64
64
  return { kind: 'skipped', reason: early.reason, current };
65
65
  const latest = await this.source.latest();
66
66
  this.state.update((s) => ({ ...s, lastCheck: this.now().toISOString() }));
67
67
  const decision = decideUpdate({ ...base, latest, sandboxRunning: options.sandboxRunning, force });
68
68
  if (decision.kind === 'skip')
69
69
  return { kind: 'skipped', reason: decision.reason, current };
70
- return this.install(decision.release, current, node);
70
+ return this.install(decision.release, current, node, options.sandboxRunning);
71
71
  }
72
72
  /** Points the shims back at the version the last successful update replaced. Returns that version. */
73
73
  rollback() {
@@ -90,7 +90,7 @@ export class Updater {
90
90
  this.state.update((s) => ({ ...s, previous: current }));
91
91
  return target;
92
92
  }
93
- async install(release, current, node) {
93
+ async install(release, current, node, sandboxRunning) {
94
94
  const version = release.version;
95
95
  const archive = path.join(this.layout.root, 'staging', version + '.tgz');
96
96
  const work = this.layout.versionDir(version) + '.staging';
@@ -139,7 +139,7 @@ export class Updater {
139
139
  // A leftover download is replaced by the next one.
140
140
  }
141
141
  }
142
- this.finish(version, current);
142
+ this.finish(version, current, sandboxRunning);
143
143
  return { kind: 'installed', version, previous: current };
144
144
  }
145
145
  /** The commands a version's package.json declares, or null when it cannot be read. */
@@ -277,10 +277,15 @@ export class Updater {
277
277
  '): ' +
278
278
  (errorLine(run.stderr || run.stdout || run.error?.message || '') || 'no output'));
279
279
  }
280
- /** Records the update, keeps the replaced version for a rollback and removes the rest. */
281
- finish(version, previous) {
280
+ /**
281
+ * Records the update, keeps the replaced version for a rollback and removes the rest, but only when no sandbox runs:
282
+ * its helpers on this PC may still run an older version from its folder.
283
+ */
284
+ finish(version, previous, sandboxRunning) {
282
285
  const at = this.now().toISOString();
283
286
  this.state.update((s) => ({ ...s, previous, lastResult: { version, ok: true, at } }));
287
+ if (sandboxRunning)
288
+ return;
284
289
  for (const old of this.layout.installedVersions())
285
290
  if (old !== version && old !== previous) {
286
291
  try {
@@ -55,10 +55,6 @@ async function service(t) {
55
55
  found.available &&
56
56
  found.latest?.version === '1.0.1' &&
57
57
  layout.activeVersion() === '1.0.0', 'a managed install reports its version, automatic updates off by default; check finds a newer version and changes nothing');
58
- sandbox = true;
59
- const blocked = await svc.run();
60
- t.check(blocked?.kind === 'skipped' && blocked.reason === 'sandbox-running' && layout.activeVersion() === '1.0.0', 'a running sandbox stops the update');
61
- sandbox = false;
62
58
  const held = new UpdateLock(root, () => clock.at).acquire();
63
59
  const whileHeld = held !== null && (await svc.run()) === null;
64
60
  held?.();
@@ -239,10 +235,7 @@ async function command(t) {
239
235
  publish(t, feed, '1.0.1', 'good');
240
236
  lines.length = 0;
241
237
  t.check((await runU(machine, { check: true })).includes('Version 1.0.1 is available'), '--check names the newer version and exits 0');
242
- const waiting = { kind: 'skipped', reason: 'sandbox-running', current: '1.0.0' };
243
238
  t.check(describeResult(null, false).code === 0 &&
244
- describeResult(waiting, false).code === 1 &&
245
- describeResult(waiting, true).code === 0 &&
246
239
  describeResult({ kind: 'installed', version: '1.0.1', previous: '1.0.0' }, false).line.includes('from 1.0.0 to 1.0.1') &&
247
240
  describeResult({ kind: 'failed', version: '1.0.1', reason: 'x', blocked: true, rolledBack: true, autoUpdateOff: true }, false).code === 1, 'exit codes: a running sandbox fails by hand but just waits unattended; a failed update exits 1');
248
241
  }
@@ -86,13 +86,13 @@ export const updateSuite = {
86
86
  skip('no-release'),
87
87
  skip('up-to-date'),
88
88
  skip('up-to-date'),
89
- skip('sandbox-running'),
89
+ install('1.0.1'),
90
90
  skip('too-soon'),
91
91
  install('1.0.1'),
92
92
  skip('failed-before'),
93
93
  skip('failed-before'),
94
94
  install('1.0.3'),
95
- ], 'only a newer release installs; a running sandbox blocks; an hour after the last check is too soon unless forced; a failed version (and older) is not retried');
95
+ ], 'only a newer release installs, also while a sandbox runs; an hour after the last check is too soon unless forced; a failed version (and older) is not retried');
96
96
  t.check(!isTooSoon({ lastCheck: 'not a date' }, now, 24), 'a damaged last-check time does not block updates');
97
97
  t.section('updater: install, failure, rollback');
98
98
  const clock = { at: new Date('2026-10-14T12:00:00Z') };
@@ -123,8 +123,8 @@ export const updateSuite = {
123
123
  ], [
124
124
  { kind: 'skipped', reason: 'too-soon', current: '1.0.1' },
125
125
  { kind: 'skipped', reason: 'failed-before', current: '1.0.1' },
126
- { kind: 'skipped', reason: 'sandbox-running', current: '1.0.1' },
127
- ], 'no second look within 24 hours, no retry of the failed version, never under a running sandbox');
126
+ { kind: 'skipped', reason: 'failed-before', current: '1.0.1' },
127
+ ], 'no second look within 24 hours, no retry of the failed version (a running sandbox is not a reason to skip)');
128
128
  publish(t, feed, '1.0.3', 'good');
129
129
  const third = await updater.run({ sandboxRunning: false });
130
130
  t.check(JSON.stringify(third) === JSON.stringify({ kind: 'installed', version: '1.0.3', previous: '1.0.1' }) &&
@@ -6,7 +6,7 @@
6
6
  <title>Sandbox</title>
7
7
  <link rel="icon" href="data:," />
8
8
  <link rel="stylesheet" href="/main-RQFN4WBY.css" />
9
- <script type="module" src="/main-QL7KHCEE.js"></script>
9
+ <script type="module" src="/main-K2JMCAZI.js"></script>
10
10
  </head>
11
11
  <body>
12
12
  <div id="root"></div>
@@ -125,8 +125,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
125
125
  `),cli:{name:"config",action:"export",positional:["settingsFile"],flags:{withPaths:{flag:"--with-paths"}}}}),rb=we({id:"settings.import",title:"Import settings\u2026",summary:"read a shared settings file, show what it would change, and apply it after one yes",input:g.object({settingsFile:LX.optional().describe("(export, import) the .json file")}),output:g.object({changed:g.number().describe("How many settings changed"),message:g.string().describe("What happened")}),text:e=>e.message,cli:{name:"config",action:"import",positional:["settingsFile"]}});function NX(e,t){return{...jc(new la(e.input).child("settingsFile"),["settingsFile"]),label:t,optional:!1}}function Ove(e,t){let r=n=>T1(n,e.field,40).text;return["Setting "+e.id,"All projects "+r(e.yours)+(e.yoursSet?" (your file)":" (built-in default)"),"This project "+(e.inProject===void 0?"not set":r(e.inProject)),...t==="user"?["Default "+r(e.default)]:[],...t==="project"&&e.locked?["A project can only make this stricter than your own setting."]:[]]}function zve(e,t){let r=t==="project"&&!e.set?"all projects: "+T1(e.yours,e.field,40).text:T1(e.value,e.field).text,n=e.field.kind==="secret"?"":e.value;return{id:e.id,label:e.label,hint:r,...e.set?{tone:t==="project"?"accent":"ok",badges:[{text:"changed",tone:"accent"}]}:{},description:e.field.help,group:e.section,details:Ove(e,t),keywords:e.id,code:e.id,cells:{setting:e.id,value:r},primary:ve(es,{scope:t,key:e.id},{label:"Change",verb:"edit",inline:!0,fields:[{...e.field,name:"value",label:e.label}],initial:{value:n}}),actions:e.set?[ve(Z1,{scope:t,key:e.id},{label:t==="project"?"Remove":"Reset to default",verb:"reset",key:"r"})]:[]}}function Nve(e){return{id:"@data-folder",label:"Data folder",hint:e,cells:{setting:"storage.root",value:e},description:"Where your tools, logins, history and logs are kept between sandboxes. Move it with sandbox storage move <folder>.",primary:ve(rh,{},{label:"Data folder",verb:"change"})}}function Fve(e){let t=e.categories.map(r=>({id:r.id,title:r.label,description:r.description,columns:[{key:"setting",label:"SETTING"},{key:"value",label:"VALUE"}],columnsOn:"cli",items:[...e.rows.filter(n=>n.category===r.id).map(n=>zve(n,e.scope)),...r.id==="general"&&e.dataFolder?[Nve(e.dataFolder)]:[]]}));return{title:"Settings",categories:!0,notice:{text:"Editing "+e.file+(e.exists?"":" (created when you save)")+(e.scope==="project"?": only what a project may set; needs your approval at the next launch":""),tone:e.scope==="project"?"accent":"muted"},sections:t,actions:e.scope==="user"?[ve(tb,{},{verb:"export",fields:[NX(tb,"Where to write the settings file")],initial:{settingsFile:e.exportFile},description:"Write the settings you changed to a .json file you can share with a team. Secrets, the data folder and folders or files of this PC are left out."}),ve(rb,{},{verb:"import",fields:[NX(rb,"Which settings file")],description:"Read a settings file someone shared, see what it would change, and apply it once you say yes. Secrets, the data folder and paths in it are never taken."})]:[]}}var MX=mt({id:"settings",title:"Settings",summary:"Every setting, for all projects or just this project, with its value, default and description.",queries:{settings:fz},operations:[es,Z1,tb,rb,rh],pickers:[th],tabs:{param:"scope",choices:[{value:"user",label:"All projects"},{value:"project",label:"This project"}]},inputs:e=>({settings:{scope:e.scope}}),build:({settings:e})=>Fve(e)});var G1=g.enum(["user","project"]).describe("Your own settings (all projects) or this folder's .sandbox-config.json (this project)"),Lve=[{id:"editors",label:"Editors & docs",description:"Code and text editors (VS Code is on by default), plus diagrams and document conversion.",members:["editors","diagrams"]},{id:"clients",label:"DB & API clients",description:"GUIs for the sandbox databases, tools to send HTTP, REST and GraphQL requests, and to inspect traffic.",members:["databases","api","network"]},{id:"tools",label:"Git & utilities",description:"Git GUIs and terminal UIs, archives, file search and comparing, process tools, and decompilers.",members:["git","utilities","reversing"]},{id:"cli",label:"Command-line & cloud",description:"Small developer tools (JSON, search, builds, media ...) and tools for clouds and Kubernetes.",members:["cli","cloud"]},{id:"play",label:"Browsers & games",description:"Browsers for testing web apps, and game engines and editors (they draw with OpenGL on the CPU).",members:["browsers","gamedev"]}];var Mve=g.object({id:g.string().describe("The app"),name:g.string().describe("Its name"),about:g.string().describe("What it is for"),group:g.string().describe("The group it is listed under (APP_GROUPS id)"),size:g.string().describe("Its download size"),cli:g.boolean().describe("A command-line tool (on PATH), not a desktop app"),mesa:g.boolean().describe("Needs OpenGL on the CPU (Mesa)"),fullGitOnly:g.boolean().describe("Only with full git access"),setting:g.string().optional().describe("The setting it stands for (VS Code, Windows Terminal)"),settingField:Bc.optional(),settingValue:g.unknown().optional(),installed:g.boolean().describe("Downloaded already into the data folder"),on:g.boolean().describe("Installed in every sandbox of this scope")}),Bve=g.object({id:g.string().describe("Its key in apps.custom"),name:g.string().describe("Its name"),from:g.string().describe('"From the latest <repo> release." or "From <url>"'),cli:g.boolean().describe("A command-line tool")}),jve=g.object({scope:G1,file:g.string().describe("The file this scope edits"),exists:g.boolean().describe("The file exists already"),apps:g.array(Mve).describe("The catalogue entries this scope can change"),custom:g.array(Bve).describe("Your own programs (your own scope only)"),customValue:g.unknown().optional().describe("apps.custom as saved, to edit (your own scope only)"),customField:Bc.optional().describe("The Field of apps.custom (your own scope only)")}),mz=Qe({id:"apps.list",title:"Apps",summary:"the apps catalogue: what every sandbox installs",input:g.object({scope:G1.default("user")}),output:jve,surfaces:{except:{cli:"apps are settings: sandbox config set apps.install / apps.custom"}}}),q1=we({id:"apps.toggle",title:"Install in every sandbox",summary:"turn an app of the catalogue on or off for this scope",input:g.object({scope:G1.default("user"),id:g.string().max(100).describe("The app"),on:g.boolean().describe("Install it (true) or not (false)")}),output:g.object({message:g.string().describe("What changed")}),text:e=>e.message,surfaces:{except:{cli:"apps are settings: sandbox config set apps.install / apps.custom"}}}),K1=we({id:"apps.setCustom",title:"Your own programs",summary:"change your own programs (apps.custom)",input:g.object({scope:G1.default("user"),value:g.unknown().describe("Programs of your own: a GitHub release or a download link, and the program to start.")}),output:g.object({message:g.string().describe("What changed")}),text:e=>e.message,surfaces:{except:{cli:"apps are settings: sandbox config set apps.custom"}}}),X1=we({id:"apps.request",title:"Request an app\u2026",summary:"open a prefilled GitHub request for an app that is not in the list",input:g.object({}),output:g.object({url:g.string().describe("The request page")}),text:e=>"Opened the request form in your browser: "+e.url,surfaces:{except:{cli:"a request is filed on GitHub; the address is in docs"}}});function Uve(e){return["Download ~"+e.size+" once, kept in C:\\AgentData\\tools\\"+(e.setting?"":"apps\\")+e.id+(e.installed?" (downloaded already)":""),"Starts "+(e.cli?"from a terminal (on PATH)":"from the desktop or Start > Sandbox apps"),...e.mesa?["Graphics OpenGL on the CPU (Mesa) is added for it"]:[],...e.fullGitOnly?["Needs full git access (Settings > Projects & setup > Git): it pushes with its own git."]:[],...e.setting?["Setting "+e.setting]:[]]}function Vve(e){return{name:"on",key:["apps","install"],kind:"boolean",label:e.name,optional:!1}}function Hve(e,t){let r=e.setting&&e.settingField?ve(es,{scope:"user",key:e.setting},{label:"Change",verb:"change",inline:!0,fields:[{...e.settingField,name:"value",label:e.name}],initial:{value:e.settingValue}}):ve(q1,{scope:t,id:e.id},{label:e.on?"Turn off":"Turn on",verb:"tick",inline:!0,fields:[Vve(e)],initial:{on:e.on}});return{id:e.id,label:e.name,description:e.about,code:e.id,keywords:e.id,details:Uve(e),...e.on?{tone:"ok"}:{},primary:r}}function Wve(e){return{id:"custom-"+e.id,label:e.name,description:e.from,hint:e.cli?"command line":"desktop",disabled:"Your own programs are always installed; change or remove them with Your own programs."}}function Zve(e){let t=Lve.map(n=>({id:n.id,title:n.label,description:n.description,items:e.apps.filter(o=>o.group===n.id).map(o=>Hve(o,e.scope))})).filter(n=>(n.items?.length??0)>0);e.scope==="user"&&t.push({id:"custom",title:"Your own programs",description:"Programs you added yourself, from a GitHub release or a download link.",items:e.custom.map(Wve)});let r=e.scope==="user";return{title:"Apps",categories:!0,notice:{text:"Editing "+e.file+(e.exists?"":" (created when you save)")+(e.scope==="project"?": only apps from the list; needs your approval at the next launch":""),tone:e.scope==="project"?"accent":"muted"},sections:t,actions:r?[...e.customField?[ve(K1,{scope:"user"},{key:"c",verb:"your own programs",fields:[{...e.customField,name:"value",label:"Your own programs"}],initial:{value:e.customValue}})]:[],ve(X1,{},{key:"r",verb:"request an app"})]:[]}}var BX=mt({id:"apps",title:"Apps",summary:"Tick the apps every sandbox should have. Each downloads once and is kept.",queries:{apps:mz},operations:[q1,K1,X1,es],tabs:{param:"scope",choices:[{value:"user",label:"All projects"},{value:"project",label:"This project"}]},inputs:e=>({apps:{scope:e.scope}}),build:({apps:e})=>Zve(e)});var ki={desktop:"desktop window",gpu:"GPU",clipboard:"clipboard",audio:"audio",video:"camera",printers:"printers",liveFolderAdd:"add folders while running",attachedDisks:"attached disks",multipleInstances:"several at once",hvsocket:"Hyper-V sockets"},Gve=Object.keys(ki),qve="see docs/design/engines.md",jX=g.enum(["windows-sandbox","hcs"]).describe("The engine"),Kve=g.object({desktop:g.boolean().describe(ki.desktop),gpu:g.boolean().describe(ki.gpu),clipboard:g.boolean().describe(ki.clipboard),audio:g.boolean().describe(ki.audio),video:g.boolean().describe(ki.video),printers:g.boolean().describe(ki.printers),liveFolderAdd:g.boolean().describe(ki.liveFolderAdd),attachedDisks:g.boolean().describe(ki.attachedDisks),multipleInstances:g.boolean().describe(ki.multipleInstances),hvsocket:g.boolean().describe(ki.hvsocket)}),Xve=g.object({available:g.boolean().describe("This PC can run it"),reasons:g.array(g.string()).describe("Why not, in plain words"),fixes:g.array(g.string()).describe("The command that fixes each reason (never run for you)")}).describe("Whether this PC can run it"),UX=g.object({id:jX,inUse:g.boolean().describe("The setting sandbox.engine names it"),capabilities:Kve.describe("What it can do"),check:Xve}),Qve=g.object({engine:jX.describe("The engine in use (the setting sandbox.engine)"),engines:g.array(UX).describe("Every engine")});function Yve(e){if(e.check.available){let r=[e.id+" can be used on this PC."];return e.id==="hcs"&&r.push("HCS is in alpha: starting it is not enabled yet (docs/design/engines.md)."),r.join(`
126
126
  `)}let t=[e.id+" cannot be used on this PC yet:"];return e.check.reasons.forEach((r,n)=>{t.push(""," "+(n+1)+". "+r," To fix: "+(e.check.fixes[n]??qve))}),t.push("","Nothing was changed. These commands change Windows, so they are yours to run (or not)."),t.join(`
127
127
  `)}var pz=Qe({id:"engine.status",title:"Engine",summary:"the engine in use and what each engine can do on this PC",input:g.object({}),output:Qve,cli:{name:"engine",action:"show",json:!0}}),VX=Qe({id:"engine.check",title:"Check an engine",summary:"what this PC lacks for an engine, and the commands that would fix it",input:g.object({engine:g.string().default("hcs").describe("the engine to check")}),output:UX,surfaces:{except:{menu:"the Engine category shows every engine and its check with the fixes (engine.status)",web:"the Engine page shows every engine and its check with the fixes (engine.status)"}},text:Yve,cli:{name:"engine",action:"check",positional:["engine"],json:!0,exitCode:e=>e.check.available?0:1}}),Jve=g.object({id:g.string().describe("The compute system"),kind:g.string().describe("Container or VirtualMachine (the utility VM)"),state:g.string().describe("What it is doing"),since:g.string().nullable().describe("When it was created (ISO time), when HCS says")}),exe=g.object({available:g.boolean().describe("HCS could be asked"),message:g.string().describe("What was found, or why nothing is listed"),systems:g.array(Jve).describe("Ours that are running now")}),hz=Qe({id:"engine.leftovers",title:"HCS systems left running",summary:"list the HCS compute systems this tool left running (a crashed command can leave one up)",input:g.object({}),output:exe,cli:{name:"engine",action:"leftovers",json:!0}}),Q1=we({id:"engine.clean",title:"Stop the leftover systems\u2026",summary:"stop the HCS compute systems this tool left running (frees their memory; nothing else is touched)",input:g.object({}),output:g.object({available:g.boolean().describe("HCS could be asked"),stopped:g.number().describe("How many were stopped"),failed:g.number().describe("How many could not be stopped"),message:g.string().describe("What happened")}),danger:!0,text:e=>e.message,cli:{name:"engine",action:"clean"}});function txe(e){return{id:"leftover:"+e.id,label:e.id,hint:e.state+(e.since?" started "+Tn(e.since):""),tone:"warn",details:["System "+e.id,"Kind "+e.kind,"State "+e.state,...e.since?["Created "+e.since]:[]]}}function rxe(e){return e.systems.length?[{id:"leftovers",title:"HCS systems left running",summary:Yp(e.systems.length,"system")+" (they hold memory; a sandbox running on purpose is listed too)",items:e.systems.map(txe),actions:[ve(Q1,{},{key:"x",verb:"stop them",importance:1})]}]:[]}function oxe(e){let t=Gve.map(n=>{let o=e.capabilities[n];return{id:n,label:ki[n],value:o?"yes":"no",...o?{}:{tone:"muted"}}}),r=[...e.check.reasons.map((n,o)=>({id:"reason:"+o,label:n,tone:"warn"})),...e.check.fixes.map((n,o)=>({id:"fix:"+o,label:"Fix: "+n}))];return{id:e.id,title:e.id+(e.id==="hcs"?" (alpha)":"")+(e.inUse?" (in use)":""),summary:e.check.available?"can be used on this PC":"cannot be used yet",facts:t,items:r}}function nxe(e,t){return{title:"Engine",notice:{text:"Engine in use: "+e.engine+" (setting: sandbox.engine)"},sections:[...e.engines.map(oxe),...t?rxe(t):[]]}}var HX=mt({id:"engine",title:"Engine",summary:"What runs your sandbox, and what each engine can do on this PC.",queries:{engines:pz,leftovers:hz},operations:[Q1],build:({engines:e,leftovers:t})=>nxe(e,t)});var ZX="sandbox doctor and sandbox repair are hand-written renderers of this run (lines redrawn in place, --json alone, repair before the settings load)",ixe=g.enum(["ok","fixed","needs-you","warn","skipped"]).describe("What the check found"),axe=g.object({id:g.string().describe("The check"),group:g.string().describe("Its heading (This PC, Install ...)"),name:g.string().describe("What was checked"),status:ixe,detail:g.string().describe("What was found"),means:g.string().optional().describe("What it means for you"),fix:g.string().optional().describe("What to do yourself"),plan:g.string().optional().describe("What repair would change"),lines:g.array(g.string()).optional().describe("More lines (a diff, folders)"),fixable:g.boolean().describe("Repair can mend it"),timedOut:g.boolean().optional().describe("It took too long"),applied:g.object({done:g.string(),before:g.string(),after:g.string()}).optional().describe("What a fix changed"),error:g.string().optional().describe("Why a fix did not work")}),GX=g.object({total:g.number().describe("Checks that ran"),ok:g.number().describe("Checks that are fine"),fixed:g.number().describe("Problems repair fixed"),warn:g.number().describe("Warnings"),needsYou:g.number().describe("Problems you need to fix"),skipped:g.number().describe("Checks that did not run"),fixable:g.number().describe("Problems repair can still mend")}).describe("How many checks ended how"),sxe=g.object({name:g.string().describe("What was looked at"),status:g.enum(["ok","warn","action"]).describe("OK, a warning, or something to do"),detail:g.string().describe("What was found")}),lxe=g.object({at:g.string().describe("When it ran (ISO time)"),mode:g.enum(["check","repair"]).describe("A check or a repair"),checks:g.array(axe).describe("The results"),summary:GX,details:g.array(g.tuple([g.string(),g.string()])).describe("The doctor details: name and value"),setup:g.object({checks:g.array(sxe),canEnable:g.boolean().describe("Windows Sandbox can be turned on from here"),error:g.string().nullable().describe("Why the setup could not be read")}).nullable().describe("What sandbox setup found"),log:g.string().nullable().describe("The repair log, if one was written")});function qX(e){return[e.ok+" ok",e.fixed&&e.fixed+" fixed",e.needsYou&&e.needsYou+" "+(e.needsYou===1?"needs":"need")+" you",e.warn&&e.warn+(e.warn===1?" warning":" warnings"),e.skipped&&e.skipped+" skipped"].filter(Boolean).join(", ")}var cxe={ok:"[ok]",fixed:"[fixed]",warn:"[!]","needs-you":"[x]",skipped:"[-]"},oh={ok:void 0,fixed:"accent",warn:"warn","needs-you":"danger",skipped:"muted"},gz=6;function dxe(e){let t={text:" "+cxe[e.status]+" "+e.name+": "+e.detail+(e.timedOut?" (took too long)":""),...oh[e.status]?{tone:oh[e.status]}:{}};if(e.status==="ok")return[t];let r=[t];e.means&&r.push({text:" What it means: "+e.means}),e.fixable&&e.plan?r.push({text:" Fix: sandbox repair will "+e.plan}):e.fix&&r.push({text:" Fix: "+e.fix});let n=e.lines??[];for(let o of n.slice(0,gz))r.push({text:" "+o});return n.length>gz&&r.push({text:" ... and "+(n.length-gz)+" more"}),r}var WX={ok:"OK",fixed:"FIXED","needs-you":"NEEDS YOU",warn:"WARNING",skipped:"SKIPPED"};function uxe(e,t){return e.length<=t?e:e.slice(0,t-1)+"\u2026"}var KX=g.object({summary:GX,changed:g.boolean().describe("Fixes were applied"),log:g.string().nullable().describe("The repair log, if one was written")}),vz=Qe({id:"health.last",title:"Health",summary:"the last check or repair of this PC and the sandbox",input:g.object({}),output:g.object({last:lxe.nullable().describe("Nothing yet: no check has run")}),surfaces:{except:{cli:"the command line prints results as it checks (sandbox doctor)"}}}),ob=we({id:"health.check",title:"Check this PC",summary:"check this PC, the install, your settings and the sandbox (changes nothing)",input:g.object({harness:g.string().optional().describe("agent to check")}),output:KX,text:e=>"Summary: "+qX(e.summary),surfaces:{except:{cli:ZX}}}),nb=we({id:"health.repair",title:"Run the checks and fix what can be fixed",summary:"find and fix problems (install, data folder, settings, stale state, half-installed tools)",input:g.object({}),output:KX,danger:!0,text:e=>"Summary: "+qX(e.summary)+(e.log?". Log: "+e.log:". No log was written (the storage folder does not exist yet)."),surfaces:{except:{cli:ZX}}}),Y1=we({id:"health.enableSandbox",title:"Turn on Windows Sandbox",summary:"turn on the Windows Sandbox feature (Windows asks for admin permission; restart to finish)",input:g.object({}),output:g.object({message:g.string().describe("What happened")}),danger:!0,text:e=>e.message,surfaces:{except:{cli:"sandbox setup asks and turns it on (hand-written: it also offers to restart Windows)",web:"Windows asks for admin permission on this PC; use the menu or sandbox setup"}}});function fxe(e){return e.checks.map((t,r)=>{let n=t.detail+(t.timedOut?" (took too long)":""),o=dxe(t).slice(1).map(a=>a.text.trim()).filter(a=>!a.startsWith("What it means: ")),i=t.status==="ok"?[]:["Found: "+n,...o];return{id:"repair:"+r,label:t.name,hint:WX[t.status],...oh[t.status]?{tone:oh[t.status]}:{},badges:[{text:WX[t.status],...oh[t.status]?{tone:oh[t.status]}:{}}],description:t.means??(i.length?void 0:n),...i.length?{details:i}:{}}})}var mxe={ok:"OK",warn:"WARNING",action:"NEEDS YOU"};function pxe(e){let t=e.last,r=ve(ob,{},{verb:"check"}),n=ve(nb,{},{verb:"check and fix"});if(!t)return{title:"Health",sections:[{id:"doctor",title:"Doctor",cards:!0,items:[{id:"health:check",label:"Check this PC",description:"Looks at this PC: Windows features, the install, the folders and the sandbox. It takes a few seconds, so it only runs when you ask.",primary:r}]},{id:"repair",title:"Repair",cards:!0,items:[{id:"repair:run",label:"Run the checks and fix what can be fixed",description:"Checks the install, data folder, settings and sandbox, lists what it would change, and asks before it fixes anything.",primary:n}]}]};let o=!!t.setup?.canEnable,i=[{id:"status",summary:(t.mode==="repair"?"Last repair ":"Last check ")+Tn(t.at),...t.log?{notes:[{text:"Log: "+t.log,tone:"muted"}]}:{}},{id:"doctor",title:"Doctor",items:t.details.map(([a,s],l)=>({id:"doctor:"+l,label:a,hint:uxe(s,50),...s.length>50?{description:s}:{}}))},{id:"repair",title:"Repair",items:fxe(t)}];if(t.setup){let a=t.setup;i.push({id:"setup",title:"Set up this PC",items:a.error?[{id:"setup:error",label:a.error}]:a.checks.map((s,l)=>({id:"setup:"+l,label:s.name,hint:s.detail,tone:s.status==="ok"?"ok":s.status==="warn"?"warn":"danger",badges:[{text:mxe[s.status],tone:s.status==="ok"?"ok":s.status==="warn"?"warn":"danger"}]})),notes:o?[{text:"Turn Windows Sandbox on from the menu or with sandbox setup.",tone:"muted",only:"web"}]:[]})}return{title:"Health",sections:i,actions:[ve(nb,{},{key:"f",verb:"check and fix",importance:1}),ve(ob,{},{key:"l",verb:t?"check again":"check",importance:2}),...o?[ve(Y1,{},{key:"e",verb:"turn on Windows Sandbox",importance:3})]:[]]}}var XX=mt({id:"health",title:"Health",summary:"Checks this PC and your settings, repairs problems, and sets up Windows Sandbox.",queries:{last:vz},operations:[ob,nb,Y1],build:({last:e})=>pxe(e)});var QX=["tools","databases","state","logs","data","host"],hxe={tools:"Toolchains",databases:"Databases",state:"Agent state",logs:"Logs",data:"Other",host:"On this PC"},gxe=g.object({area:g.enum(QX).describe("Which kind of data it is"),name:g.string().describe("The folder as listed (tools: its name; others: area/name)"),path:g.string().describe("Its full path on this PC"),bytes:g.number().describe("Its size in bytes (links count 0)"),size:g.string().describe("Its size in words, e.g. 1.5 KB"),target:g.string().nullable().describe("What sandbox data clean calls it; null when it is kept on purpose")}),vxe=g.object({drive:g.string().describe("The drive, e.g. C:\\"),totalBytes:g.number().describe("Its size in bytes"),freeBytes:g.number().describe("Free bytes"),usedBytes:g.number().describe("Used bytes"),holdsStorage:g.boolean().describe("The sandbox keeps its files on this drive"),used:g.string().describe("Used, in words"),usedPercent:g.number().describe("Used, in percent of the size (rounded)"),free:g.string().describe("Free, in words"),total:g.string().describe("Size, in words")}),xxe=g.object({dataPath:g.string().describe("The data folder (C:\\AgentData inside the sandbox)"),storageRoot:g.string().describe("The storage folder that holds it"),totalBytes:g.number().describe("Everything listed, in bytes"),total:g.string().describe("Everything listed, in words"),storageShare:g.string().describe('"<- sandbox storage (<total> of it)": the note on the storage drive'),areaTotals:g.record(g.string(),g.string()).describe("Each area's total in words"),entries:g.array(gxe).describe("What is stored, by area, biggest first"),disks:g.array(vxe).describe("This PC's drives")}),xz=Qe({id:"data.show",title:"Stored data",summary:"show what is kept between sandboxes (tools, databases, logins, logs), or delete some of it",input:g.object({}),output:xxe,cli:{name:"data",action:"show",json:!0,shapeJson:e=>({dataPath:e.dataPath,storageRoot:e.storageRoot,totalBytes:e.totalBytes,entries:e.entries.map(({area:t,name:r,path:n,bytes:o})=>({area:t,name:r,path:n,bytes:o})),disks:e.disks.map(({drive:t,totalBytes:r,freeBytes:n,usedBytes:o,holdsStorage:i})=>({drive:t,totalBytes:r,freeBytes:n,usedBytes:o,holdsStorage:i}))})}}),YX=g.object({message:g.string().describe("What happened")}),J1=we({id:"data.clean",title:"Delete stored data\u2026",summary:"delete chosen parts of what is kept between sandboxes",input:g.object({targets:g.array(g.string()).default([]).describe("what to clean, e.g. tools/go databases logs")}),output:YX.extend({freedBytes:g.number().describe("Bytes freed (0 when nothing was removed)")}),danger:!0,text:e=>e.message,next:e=>e.freedBytes>0?"sandbox data (see what is left)":void 0,cli:{name:"data",action:"clean",positional:["targets"]}}),JX=g.object({days:g.number().int().min(1).default(7).describe("launch folders older than this many days")}),ek=we({id:"data.prune",title:"Clean up\u2026",summary:"delete old launch folders, logs and leftovers",input:JX,output:YX.extend({removed:g.boolean().describe("Something was deleted")}),text:e=>e.message,next:e=>e.removed?"sandbox data (see what else is saved, and clean it)":void 0,cli:{name:"clean",group:"Data",flags:{days:{flag:"--days",value:"n"}},yes:"do not ask before deleting"}}),bxe=jc(new la(JX).child("days"),["days"]);function yxe(e){let t={id:"disks",title:"Disks",columns:[{key:"drive",label:"DRIVE"},{key:"used",label:"USED"},{key:"free",label:"FREE"},{key:"size",label:"SIZE"},{key:"note",label:""}],columnsOn:"cli",items:e.disks.map(i=>({id:"disk:"+i.drive,label:i.drive,hint:i.used+" used \xB7 "+i.free+" free of "+i.total,...i.holdsStorage?{description:"The sandbox keeps its files on this drive."}:{},cells:{drive:i.drive,used:i.used+" ("+i.usedPercent+"%)",free:i.free,size:i.total,note:i.holdsStorage?e.storageShare:""}})),notes:[{text:"Persistent data for the sandbox: "+e.dataPath+" (C:\\AgentData inside)"}]},r=i=>QX.indexOf(i),n=[...e.entries].sort((i,a)=>r(i.area)-r(a.area)||a.bytes-i.bytes),o={id:"kept",title:"What the sandbox keeps",summary:e.total+" in all",columns:[{key:"kind",label:"KIND"},{key:"name",label:"NAME"},{key:"size",label:"SIZE"},{key:"folder",label:"FOLDER"}],items:n.map(i=>{let a=i.target?ve(J1,{targets:[i.target]},{label:"Delete",key:"x",verb:"delete"}):void 0;return{id:"entry:"+i.name,label:i.name,hint:i.size,cells:{kind:hxe[i.area],name:i.name,size:i.size,folder:i.path},...a?{primary:a}:{}}}),actions:[ve(ek,{},{label:"Clean up\u2026",verb:"choose what",description:"Delete caches, old sandboxes and other leftovers (the clean command, with its options).",fields:[bxe]})],notes:[...e.entries.length?[]:[{text:"Nothing stored yet. Toolchains, databases and agent logins appear here after the first start."}],..._xe(e),{text:"Free space with: sandbox data clean <target...> (e.g. tools/go, databases/postgres-18, logs, launches)",tone:"muted",only:"cli"}]};return{title:"Data",sections:[t,o]}}function _xe(e){let t=e.entries.filter(r=>!r.target).map(r=>r.name);return t.length?[{text:"Kept on this PC on purpose, not deleted here: "+t.join(", ")+"."}]:[]}var eQ=mt({id:"data",title:"Data",summary:"What the sandbox keeps between runs, and deleting parts of it.",queries:{data:xz},operations:[J1,ek],build:({data:e})=>yxe(e)});var Ixe=g.object({name:g.string().describe("The project"),host:g.string().describe("Its folder on this PC"),guest:g.string().describe("Its folder in the sandbox"),mechanism:g.string().describe("How it got into the sandbox (launch, root, boot or share)"),here:g.boolean().describe("The project of the current folder"),setup:g.object({phase:g.string(),message:g.string()}).describe("How far its setup is"),databases:g.array(g.object({database:g.string(),port:g.number(),state:g.string()})),sessions:g.array(g.object({id:g.string(),harness:g.string(),viewers:g.string(),started:g.string()})),repoGuard:g.array(g.string()).describe("Repo guard findings in its folder")}),wxe=g.object({running:g.boolean().describe("A Windows Sandbox is running"),phase:g.string().describe("What it is doing (ready, provisioning ...)"),message:g.string().optional().describe("Its last message"),headless:g.boolean().optional().describe("Its window is hidden"),startedAt:g.number().nullable().describe("When it started (ms), if known"),daemon:g.string().describe("The sandbox helper's answer"),account:g.string().nullable().describe("The Claude login it uses"),syncWatcher:g.boolean().describe("The background history sync runs")}),Sxe=g.object({shared:g.object({line:g.string().describe('"Running, started by alice (shared with you)"'),limited:g.boolean().describe("Not shared with you: nothing more is shown"),startedBy:g.string().nullable().describe("Whose it is")}).optional(),sandbox:wxe,here:g.object({folder:g.string(),project:g.string().nullable(),guest:g.string().nullable()}),roots:g.array(g.object({host:g.string(),guest:g.string()})),projects:g.array(Ixe),otherSessions:g.array(g.object({id:g.string(),harness:g.string(),cwd:g.string()})),repoGuard:g.object({guarded:g.number(),findings:g.array(g.string())})}),tQ=g.object({message:g.string().describe("What happened")});function kxe(e){let t=/^pong \((\d+) session/.exec(e);return t?"responding, "+Yp(Number(t[1]),"session")+" running":e}function rQ(e){return e.running&&e.startedAt?Tn(e.startedAt).replace(" ago",""):null}var bz=Qe({id:"sandbox.status",title:"Sandbox status",summary:"show what the sandbox is doing",input:g.object({}),output:Sxe,live:{everyMs:2500},cli:{name:"status",group:"Sessions",json:!0,shapeJson:e=>{if(e.shared?.limited)return{sandbox:{running:!0,shared:!1,startedBy:e.shared.startedBy}};let t=e.sandbox;return{sandbox:{...e.shared?.startedBy?{startedBy:e.shared.startedBy,shared:!0}:{},running:t.running,phase:t.phase,message:t.message,headless:t.headless,uptime:rQ(t),daemon:t.daemon,account:t.account,syncWatcher:t.syncWatcher},here:e.here,roots:e.roots,projects:e.projects,otherSessions:e.otherSessions,repoGuard:e.repoGuard}}}}),tk=we({id:"sandbox.showWindow",title:"Show the window",summary:"show the sandbox window",input:g.object({}),output:tQ,text:e=>e.message,cli:{name:"show",group:"Sessions"}}),rk=we({id:"sandbox.hideWindow",title:"Hide the window",summary:"hide the sandbox window (it keeps running)",input:g.object({}),output:tQ,text:e=>e.message,cli:{name:"hide",group:"Sessions"}}),ok=we({id:"sandbox.stop",title:"Stop the sandbox\u2026",summary:"shut the sandbox down (every project's sessions end)",input:g.object({}),output:g.object({stopped:g.boolean().describe("It was asked to shut down"),message:g.string()}),danger:!0,text:e=>e.message,cli:{name:"stop",group:"Agents",yes:"do not ask when other projects' sessions would end"}}),Cxe={launch:"started from here",root:"in a project root",boot:"mapped at start"};function Pxe(e){return{id:"project:"+e.name,label:e.name,icon:"project",hint:e.host+" -> "+e.guest+" ("+(Cxe[e.mechanism]??"wsb share")+")",marked:e.here,details:["Setup: "+e.setup.phase+(e.setup.phase==="ready"?"":" - "+e.setup.message),...e.databases.length?["Databases: "+e.databases.map(t=>t.database+" :"+t.port+" "+t.state).join(", ")]:[],...e.sessions.map(t=>"Session: "+t.harness+" "+t.id+", "+t.viewers+", started "+Tn(t.started)),...e.repoGuard.map(t=>"Guard: "+t)]}}function Dxe(e){let t=e.sandbox,r=e.projects.reduce((o,i)=>o+i.sessions.length,0)+e.otherSessions.length,n=(o,i,a,s={})=>({id:o,label:i,value:a,...s});return[...e.shared?.startedBy?[n("owner","Started by",e.shared.startedBy)]:[],n("state","State",t.running?t.message||t.phase:"not running",{tone:t.running?t.phase==="ready"?"ok":"warn":"muted"}),...t.running?[n("window","Window",t.headless?"hidden (sandbox show opens it)":"shown"),n("uptime","Uptime",rQ(t)??"-"),n("helper","Helper",t.daemon==="-"?"-":kxe(t.daemon)),n("sessions","Sessions",String(r))]:[],n("account","Account",t.account??"none selected (sandbox account use)"),n("sync","Background sync",t.syncWatcher?"on":"off")]}function Rxe(e){let t=e.roots.map(n=>({id:"root:"+n.host,label:"root",icon:"folder",hint:n.host+" -> "+n.guest+" (every project in it opens without a restart)"})),r=e.projects.some(n=>n.here);return{id:"projects",title:"Projects",items:[...e.projects.map(Pxe),...t],notes:[r?{text:"* = the project of this folder",tone:"muted"}:{text:e.here.folder+" is not in this sandbox (start it here: sandbox claude)",tone:"muted"}]}}function Exe(e){let t=e.repoGuard.findings;return{id:"guard",title:"Repo guard",summary:e.repoGuard.guarded+" folder(s) guarded; "+(t.length?t.length+" change(s) neutralised:":"nothing from the sandbox needed neutralising"),items:t.slice(-10).map((r,n)=>({id:"guard:"+n,label:r,tone:"warn"}))}}function Txe(e){let t=e.shared?{text:e.shared.line+(e.shared.limited?"":". Its window is on their desktop; use its agents here."),tone:"accent"}:void 0;if(e.shared?.limited)return{title:"Sandbox",notice:t,sections:[]};let r=e.sandbox.running,n={id:"status",title:"Status",facts:Dxe(e),actions:r?[e.sandbox.headless?ve(tk,{},{key:"w",verb:"show",importance:3}):ve(rk,{},{key:"h",verb:"hide",importance:3}),ve(ok,{},{key:"x",verb:"stop",importance:1})]:[]},o={id:"other-sessions",title:"Other sessions",items:e.otherSessions.map(i=>({id:"session:"+i.id,label:"session "+i.id,hint:i.harness+" "+i.cwd}))};return{title:"Sandbox",notice:t,sections:[n,...r?[Rxe(e)]:[],...o.items?.length?[o]:[],Exe(e)]}}var oQ=mt({id:"sandbox",title:"Sandbox",summary:"What the sandbox is doing, its window, and stopping it.",queries:{status:bz},operations:[tk,rk,ok],build:({status:e})=>Txe(e)});var $xe=g.object({folder:g.string().describe("The project folder on this PC"),here:g.boolean().describe("The folder you are in is inside it"),known:g.boolean().describe("Started with this tool before"),root:g.string().nullable().describe("The project root it is in, if any"),excluded:g.boolean().describe("Excluded from the projects mapped at every start"),mappedAtStart:g.boolean().describe("Mapped at every start"),running:g.string().nullable().describe("Its name and how it got into the running sandbox, if it is there")}),Axe=g.object({all:g.boolean().describe("Every project is mapped at every start (except excluded ones)"),roots:g.array(g.string()).describe("Folders of projects, each opened without a restart"),exclude:g.array(g.string()).describe("Folders left out of the projects mapped at every start"),projects:g.array($xe).describe("Every project worth listing")}),nh=g.object({message:g.string().describe("What happened")}),nk=e=>g.string().optional().describe(e+" (. = this folder; asked when left out)"),yz=Qe({id:"projects.list",title:"Projects",summary:"list your projects, roots and the all-projects setting",input:g.object({}),output:Axe,cli:{name:"projects",action:"list",json:!0}}),ik=we({id:"projects.choose",title:"Choose the projects mapped at every start\u2026",summary:"tick the projects mapped at every start; the rest are excluded",input:g.object({}),output:nh,text:e=>e.message,cli:{name:"projects",action:"choose"}}),ib=we({id:"projects.add",title:"Add a project\u2026",summary:"remember a project folder",input:g.object({projectFolder:nk("The project folder")}),output:g.object({message:g.string().describe("What happened"),next:g.string().optional().describe("What to do next, when mapping all projects is off")}),text:e=>e.next?e.message+`
128
- `+e.next:e.message,cli:{name:"projects",action:"add",positional:["projectFolder"]}}),ak=we({id:"projects.remove",title:"Forget project",summary:"forget a project (nothing in the folder is touched)",input:g.object({projectFolder:nk("The project folder")}),output:nh,danger:!0,text:e=>e.message,cli:{name:"projects",action:"remove",positional:["projectFolder"]}}),sk=we({id:"projects.include",title:"Include",summary:"map a project at every start again",input:g.object({projectFolder:nk("The project folder")}),output:nh,text:e=>e.message,cli:{name:"projects",action:"include",positional:["projectFolder"]}}),lk=we({id:"projects.exclude",title:"Exclude",summary:"leave a project out of the projects mapped at every start",input:g.object({projectFolder:nk("The project folder")}),output:nh,text:e=>e.message,cli:{name:"projects",action:"exclude",positional:["projectFolder"]}}),ck=we({id:"projects.all",title:"Map all projects at every start",summary:"map every project at every start, or not",input:g.object({state:g.enum(["on","off"]).optional().describe("on or off (left out: say which it is)")}),output:nh,text:e=>e.message,cli:{name:"projects",action:"all",positional:["state"]}}),Bu=we({id:"projects.roots",title:"Project roots",summary:"list, add or remove project roots",input:g.object({change:g.enum(["list","add","remove"]).default("list").describe("list, add or remove"),rootFolder:g.string().optional().describe("(add, remove) the folder that holds your projects")}),output:nh,text:e=>e.message,cli:{name:"projects",action:"roots",positional:["change","rootFolder"]}}),nQ=E1(Bu.input).find(e=>e.name==="rootFolder"),Oxe=E1(ib.input).find(e=>e.name==="projectFolder"),zxe="Every project in this folder (also new ones) opens without a restart.";function iQ(e){return e.root?"root":e.excluded?"excluded":e.mappedAtStart?"mapped":"-"}function Nxe(e){return{id:"project:"+e.folder,label:e.folder,marked:e.here,icon:"project",hint:e.running??(e.root?"in a root":e.excluded?"excluded":e.mappedAtStart?"mapped":"-"),description:e.here?"This is the folder you started the menu in.":void 0,details:["At start "+iQ(e),"In sandbox "+(e.running??"-")],cells:{here:e.here?"*":"",folder:e.folder,atStart:iQ(e),running:e.running??"-"},actions:[ve(ak,{projectFolder:e.folder},{key:"f",verb:"forget project",importance:1}),e.excluded?ve(sk,{projectFolder:e.folder}):ve(lk,{projectFolder:e.folder})]}}function Fxe(e){return{id:"root:"+e,label:e,hint:"root",icon:"folder",description:zxe,keys:"f removes the root.",actions:[ve(Bu,{change:"remove",rootFolder:e},{label:"Remove the root",key:"f",verb:"remove root",importance:1})]}}function Lxe(e){let t={id:"settings",facts:[{id:"all",label:"All projects",value:e.all?"on (every project below is mapped at every start, except excluded ones)":"off"},{id:"roots",label:"Roots",value:e.roots.length?e.roots.join(", "):"none (sandbox projects roots add <folder>)"}],actions:[ve(ck,{state:e.all?"off":"on"},{label:e.all?"Stop mapping all projects":"Map all projects at every start"})],notes:[{text:"sandbox projects all on|off",tone:"muted",only:"cli"}]},r={id:"roots",title:"Project roots",items:[{id:"@add-root",label:"+ Add a project root\u2026",description:"Pick a folder that holds your projects: every project in it (also new ones) opens without a restart.",primary:ve(Bu,{change:"add"},{label:"Choose the folder\u2026",verb:"choose folder",fields:[nQ]})},...e.roots.map(Fxe)]},n={id:"projects",title:"Projects",columns:[{key:"here",label:""},{key:"folder",label:"FOLDER"},{key:"atStart",label:"AT START"},{key:"running",label:"RUNNING SANDBOX"}],columnsOn:"cli",items:e.projects.map(Nxe),notes:e.projects.length===0?[{text:"No projects yet: start one with sandbox claude in its folder.",tone:"muted"}]:[{text:"* = this folder. Choose with: sandbox projects choose",tone:"muted",only:"cli"}]};return{title:"Projects",sections:[t,r,n],actions:[ve(ik,{},{label:"Choose\u2026"}),ve(ib,{},{fields:[Oxe]}),ve(Bu,{change:"add"},{label:"Add a project root\u2026",key:"a",verb:"add root",importance:1,fields:[nQ]})]}}var aQ=mt({id:"projects",title:"Projects",summary:"The projects a sandbox opens without a restart: roots, your projects, all or chosen.",queries:{projects:yz},operations:[ik,ib,ak,sk,lk,ck,Bu],build:({projects:e})=>Lxe(e)});var dk="@crash",Mxe=1e3,lQ="sandbox logs is hand-written: --follow prints new lines until Ctrl+C; it reads this same query (readQuery)",Bxe=g.object({name:g.string().describe("The log file"),path:g.string().describe("Where it is on this PC"),bytes:g.number().describe("Its size in bytes"),modified:g.number().describe("When it was last written (ms)")}),jxe=g.object({folder:g.string().describe("The folder the logs are in"),files:g.array(Bxe).describe("The log files, newest first"),crash:g.boolean().describe("A crash report exists")}),Uxe=g.object({name:g.string().describe("The log shown, or @crash for the last crash report (empty: there are no logs)"),file:g.string().describe("Its full path (empty: there are no logs)"),text:g.string().describe("The text read, made safe for a terminal"),next:g.number().describe("Where to read on from next time (bytes)")}),_z=Qe({id:"logs.list",title:"Logs",summary:"list the sandbox's own logs",input:g.object({}),output:jxe,surfaces:{except:{cli:lQ}}}),Iz=Qe({id:"logs.tail",title:"Log",summary:"the end of one log, followed as it grows",input:g.object({name:g.string().max(200).optional().describe("The log to show (default: the newest), or @crash for the last crash report"),from:g.number().int().min(0).default(0).describe("Read on from this byte (0: the end of the log)"),lines:g.number().int().min(1).max(1e3).default(120).describe("How many lines of the end to show")}),output:Uxe,live:{everyMs:1e3},surfaces:{except:{cli:lQ}}}),sQ={text:"showing",tone:"accent"};function Vxe(e){let t=e.split(`
129
- `);return t.length&&t[t.length-1]===""&&t.pop(),t.slice(-Mxe)}function Hxe(e,t){let r=t?.file?t:void 0,n=e.files.map(i=>({id:"log:"+i.name,label:i.name,hint:Yx(i.bytes)+" "+Tn(i.modified),keys:"Enter shows the end of this log in the terminal.",primary:Ja({kind:"view",view:"logs",params:{name:i.name}},"Show",{verb:"show"}),...r?.name===i.name?{badges:[sQ]}:{}}));e.crash&&n.push({id:"log:"+dk,label:"Last crash report",description:"What Windows logged, this PC and the sandbox memory when the sandbox last ended in a crash.",primary:Ja({kind:"view",view:"logs",params:{name:dk}},"Show",{verb:"show"}),...r?.name===dk?{badges:[sQ]}:{}});let o=r?[{id:"tail",title:r.name===dk?"Last crash report":r.name,summary:r.file,text:Vxe(r.text)}]:[];return{title:"Logs",sections:[{id:"files",items:n},...o],actions:[Ja({kind:"command",argv:["logs","--follow"]},"Follow the newest log",{key:"f",verb:"follow in the terminal",importance:2})],empty:{title:"No logs yet",text:"Logs appear after the first start.",cliText:"No sandbox logs yet."}}}var cQ=mt({id:"logs",title:"Logs",summary:"The sandbox's own logs: pick one to see its end as it grows.",queries:{files:_z,tail:Iz},operations:[],params:["name"],inputs:e=>({tail:e.name?{name:e.name}:{}}),build:({files:e,tail:t})=>Hxe(e,t)});var Wxe=g.object({viewers:g.number(),remote:g.number()}),Zxe=g.object({id:g.string().describe("The session"),launchId:g.string(),harness:g.string().describe("The agent"),cwd:g.string().describe("Its folder in the sandbox"),project:g.string().optional(),phase:g.enum(["starting","running","exited","failed"]).describe("What it is doing"),message:g.string(),updatedAt:g.string(),startedAt:g.string().describe("When it started"),conversation:g.string().optional(),exitCode:g.number().optional(),projectName:g.string().optional().describe("The project it runs in"),here:g.boolean().optional().describe("In the project of the current folder"),viewers:Wxe.nullable().optional(),canOpen:g.boolean()}),Gxe=g.object({sandboxRunning:g.boolean(),defaultAgent:g.string(),wide:g.boolean(),owner:g.string().optional(),sessions:g.array(Zxe)});function dQ(e){return e===void 0?"-":e===null?"unknown (older session)":e.remote+" here, "+(e.viewers-e.remote)+" in sandbox"}function qxe(e){let t=(r,n)=>r.startedAt<n.startedAt?1:-1;return[...e.filter(r=>r.here).sort(t),...e.filter(r=>!r.here).sort(t)]}var wz=Qe({id:"sessions.list",title:"Sessions",summary:"list agent sessions and who is attached",input:g.object({wide:g.boolean().default(!1).describe("also show each session's folder")}),output:Gxe,live:{everyMs:2500},cli:{name:"sessions",group:"Sessions",json:!0,flags:{wide:{flag:"--wide",noMenu:!0}},shapeJson:e=>e.sessions.map(({canOpen:t,...r})=>r)}}),uk=we({id:"sessions.restart",title:"Restart agents",summary:"restart this project's running agents in place; they resume their conversation",input:g.object({harness:g.string().optional().describe("only this agent"),all:g.boolean().default(!1).describe("every project's sessions, not only this folder's")}),output:g.object({message:g.string().describe("The sandbox's answer")}),text:e=>e.message,cli:{name:"restart",group:"Agents",flags:{harness:{flag:"--harness",shownAs:"--agent",value:"name",choices:"harnesses"},all:{flag:"--all"}}}});function Kxe(e,t){let r=e.phase+(e.exitCode!==void 0?" ("+e.exitCode+")":""),n=e.phase==="running";return{id:e.id,label:e.harness+" "+(e.projectName??"-"),hint:r+" "+Tn(e.startedAt),badges:[{text:e.phase,tone:n?"ok":e.phase==="failed"?"danger":"muted"}],group:e.here?"This folder's project":"Other sessions",marked:e.here,icon:e.harness,description:n?"Opens this session's terminal (in a terminal, Ctrl+] detaches; the session keeps running).":void 0,details:["Session "+e.id,"Folder "+e.cwd,"Attached "+dQ(e.viewers)],cells:{here:e.here?"*":"",id:e.id,agent:e.harness,project:e.projectName??"-",state:r,attached:dQ(e.viewers),...t?{folder:e.cwd}:{},started:Tn(e.startedAt)},...n&&e.canOpen?{primary:Ja({kind:"terminal",session:e.id},"Open",{verb:"attach"})}:{},...n?{}:{disabled:"This session has ended."},...n&&!e.canOpen?{disabled:"Not reachable from this PC."}:{}}}function Xxe(e){let t=qxe(e.sessions),r=e.defaultAgent,n=Ja({kind:"start-agent",agent:r},"Start "+r,{verb:"start"}),o=t.length&&!t.some(i=>i.phase==="running"||i.phase==="starting")?[{id:"start",actions:[{...n,description:"Starts "+r+" in this folder."}]}]:[];return{title:"Sessions",...e.owner?{notice:{text:e.owner,tone:"accent"}}:{},sections:[{id:"sessions",cards:!0,columns:[{key:"here",label:""},{key:"id",label:"ID"},{key:"agent",label:"AGENT"},{key:"project",label:"PROJECT"},{key:"state",label:"STATE"},{key:"attached",label:"ATTACHED"},...e.wide?[{key:"folder",label:"FOLDER"}]:[],{key:"started",label:"STARTED"}],items:t.map(i=>Kxe(i,e.wide)),notes:[...t.some(i=>i.here)?[{text:"* = the project of this folder",tone:"muted"}]:[],...e.wide?[]:[{text:"Folders: sandbox sessions --wide",tone:"muted",only:"cli"}]]},...o],actions:[...e.sandboxRunning?[{...n,label:"New session",key:"n",verb:"new session",importance:1}]:[],...t.length?[ve(uk,{all:!1},{key:"r",verb:"restart agents",importance:1})]:[],Ja({kind:"command",argv:["resume"]},"Reopen last sessions",{key:"o",verb:"reopen last sessions",importance:2})],empty:{title:e.sandboxRunning?"No sessions":"No sandbox is running",text:e.sandboxRunning?"No agent is running. Start "+r+" in this folder.":"Starting "+r+" starts the sandbox too; its session is listed here.",action:n,cliText:"No sessions. Start one with: sandbox claude"}}}var uQ=mt({id:"sessions",title:"Sessions",summary:"The agent sessions of the running sandbox: open one, restart the agents.",queries:{sessions:wz},operations:[uk],build:({sessions:e})=>Xxe(e)});var Qxe=g.enum(["nordvpn","openvpn-file","wireguard-file"]).describe("Which VPN (setting vpn.provider)"),Yxe=g.enum(["none","saved","unreadable"]).describe("Whether a login is saved and can be opened by you"),Jxe=["The VPN is an alpha: a tunnel carrying traffic is not confirmed yet (docs/vpn.md lists what still needs checking).","The agent in the sandbox can read the VPN login (a service login you can revoke) and can turn the tunnel off, because it is the administrator there.","Free VPNs see all your traffic: use one you trust."];var ebe=g.object({enabled:g.boolean().describe("The VPN is on (setting vpn.enabled)"),provider:Qxe,protocol:g.enum(["udp","tcp"]).describe("udp or tcp (setting vpn.protocol)"),country:g.string().describe("The NordVPN country; empty = the best server anywhere"),killSwitch:g.boolean().describe("No internet when the tunnel fails (setting vpn.killSwitch)"),login:Yxe,loginSavedAt:g.string().nullable().describe("When the login was saved (the date and time, never the login)"),running:g.object({level:g.enum(["ok","warn","bad"]).describe("How the tunnel is doing"),text:g.string().describe("What the sandbox reported")}).nullable().describe("The running sandbox's tunnel, when one runs with the VPN on")}),Sz=Qe({id:"vpn.status",title:"VPN",summary:"the VPN settings, whether a login is saved, and the running tunnel",input:g.object({tunnel:g.boolean().default(!0).describe("also ask the running sandbox how its tunnel is doing (one request to api.ipify.org)")}),output:ebe,cli:{name:"vpn",action:"status",json:!0}}),fk=we({id:"vpn.login",title:"Save the VPN login\u2026",summary:"save the VPN login (typed hidden, kept encrypted, never printed)",input:g.object({}).strict(),output:g.object({message:g.string().describe("What happened")}),text:e=>e.message,cli:{name:"vpn",action:"login"}}),mk=we({id:"vpn.logout",title:"Delete the VPN login",summary:"delete the saved VPN login",input:g.object({}).strict(),output:g.object({deleted:g.boolean().describe("A login was saved and is gone"),message:g.string().describe("What happened")}),danger:!0,text:e=>e.message,cli:{name:"vpn",action:"logout"}}),tbe={nordvpn:"NordVPN","wireguard-file":"your own WireGuard .conf","openvpn-file":"your own .ovpn file"},rbe={none:"not saved",saved:"saved (encrypted)",unreadable:"cannot be read"},obe={none:"warn",saved:"ok",unreadable:"danger"},nbe={ok:"ok",warn:"warn",bad:"danger"};function ibe(e){let t=e.provider==="wireguard-file",r=e.loginSavedAt?" "+e.loginSavedAt.slice(0,10):"",n=[e.enabled?{id:"vpn",label:"VPN",value:"on",tone:"ok"}:{id:"vpn",label:"VPN",value:"off (applies from the next start)",tone:"muted"},{id:"provider",label:"Provider",value:tbe[e.provider]},{id:"protocol",label:"Protocol",value:e.protocol},{id:"country",label:"Country",value:e.country||"best available"},e.killSwitch?{id:"killSwitch",label:"Kill switch",value:"on (no internet if the tunnel fails)"}:{id:"killSwitch",label:"Kill switch",value:"off",tone:"warn"},t?{id:"login",label:"Login",value:"not needed (the keys are in your config)",tone:"muted"}:{id:"login",label:"Login",value:rbe[e.login]+r,tone:obe[e.login]},...e.running?[{id:"running",label:"Running now",value:e.running.text,tone:nbe[e.running.level]}]:[]],o=[...!t&&e.login==="none"?[{text:"Next: sandbox vpn login",tone:"muted",only:"cli"}]:[],...e.login==="unreadable"?[{text:"Only the Windows user that saved it can read it. Save it again: sandbox vpn login",tone:"muted"}]:[],...Jxe.map(l=>({text:l,tone:"warn"}))],a=[ve(es,{scope:"user",key:"vpn.enabled",value:!e.enabled},{label:e.enabled?"Turn the VPN off":"Turn the VPN on",verb:e.enabled?"turn off":"turn on",key:"o"}),...t?[]:[ve(fk,{},{key:"v",verb:"save login"}),...e.login!=="none"?[ve(mk,{},{key:"x",verb:"delete login"})]:[]]];return{title:"VPN",sections:[{id:"vpn",facts:n,notes:o,actions:a}]}}var fQ=mt({id:"vpn",title:"VPN",summary:"The sandbox's VPN: whether it is on, the saved login, and the tunnel.",queries:{status:Sz},operations:[fk,mk,es],build:({status:e})=>ibe(e)});var abe="https://github.com/settings/personal-access-tokens/new",Vgt=["Open "+abe+" (a fine-grained personal access token).",'Under "Repository access" choose "Only select repositories" and pick only the ones the agent should reach.',"Give only the permissions it needs, for example Contents (read and write), Pull requests (read and write) and Actions (read).","Set an expiry date, then generate the token and copy it."],sbe=["The agent in the sandbox can read the token and use it for everything it allows: limit it to some repositories and permissions.",'It is handed over only with github.shareToken on and git access "full". Revoke it on GitHub whenever you like.'],lbe=g.enum(["none","saved","unreadable"]).describe("Whether a token is saved and can be opened by you"),cbe=g.object({shareToken:g.boolean().describe("Sandboxes get the token (setting github.shareToken)"),gitAccess:g.enum(["full","readonly","none"]).describe("Git access in your settings (provisioning.git.access)"),token:lbe,kind:g.enum(["fine-grained","classic"]).nullable().describe("What kind of token is saved (never the token)"),savedAt:g.string().nullable().describe("When the token was saved (the date and time, never the token)")}),kz=Qe({id:"github.status",title:"GitHub",summary:"whether sandboxes get your GitHub token, and whether one is saved (never the token)",input:g.object({}).strict(),output:cbe,cli:{name:"github",action:"status",json:!0}}),pk=we({id:"github.login",title:"Save the GitHub token\u2026",summary:"save a GitHub token for sandboxes (typed hidden, kept encrypted, never printed)",input:g.object({}).strict(),output:g.object({message:g.string().describe("What happened")}),text:e=>e.message,cli:{name:"github",action:"login"}}),hk=we({id:"github.logout",title:"Delete the GitHub token",summary:"delete the saved GitHub token",input:g.object({}).strict(),output:g.object({deleted:g.boolean().describe("A token was saved and is gone"),message:g.string().describe("What happened")}),danger:!0,text:e=>e.message,cli:{name:"github",action:"logout"}}),dbe={none:"not saved",saved:"saved (encrypted)",unreadable:"cannot be read"},ube={none:"warn",saved:"ok",unreadable:"danger"},fbe={"fine-grained":"fine-grained",classic:"classic (reaches every repository you can: a fine-grained token is safer)"};function mbe(e){let t=e.savedAt?" "+e.savedAt.slice(0,10):"",r=[e.shareToken?{id:"share",label:"Share with sandboxes",value:"on",tone:"ok"}:{id:"share",label:"Share with sandboxes",value:"off (turn on: sandbox config set github.shareToken true)"},e.gitAccess==="full"?{id:"gitAccess",label:"Git access",value:"full"}:{id:"gitAccess",label:"Git access",value:e.gitAccess+' (the token is only handed over with "full")',tone:"warn"},{id:"token",label:"Token",value:dbe[e.token]+t,tone:ube[e.token]},...e.kind?[{id:"kind",label:"Kind",value:fbe[e.kind],...e.kind==="classic"?{tone:"warn"}:{}}]:[]],n=[...e.token==="none"?[{text:"Next: sandbox github login",tone:"muted",only:"cli"}]:[],...e.token==="unreadable"?[{text:"Only the Windows user that saved it can read it. Save it again: sandbox github login",tone:"muted"}]:[],...sbe.map(a=>({text:a,tone:"muted"}))],o=[ve(pk,{},{key:"g",verb:"save token"}),...e.token!=="none"?[ve(hk,{},{key:"x",verb:"delete token"})]:[]];return{title:"GitHub",sections:[{id:"github",facts:r,notes:n,actions:o}]}}var mQ=mt({id:"github",title:"GitHub",summary:"A GitHub token for sandboxes: whether they get it, and the saved token.",queries:{status:kz},operations:[pk,hk],build:({status:e})=>mbe(e)});var pbe=g.object({version:g.string().describe("The version that failed"),reason:g.string().describe("Why it failed, in one sentence")}),hbe=g.object({managed:g.boolean().describe("Installed by the installer, so it can update itself"),version:g.string().describe("The version installed"),scope:g.enum(["user","machine"]).nullable().describe("Installed for you, or for everyone on this PC"),autoUpdate:g.boolean().describe("New versions install by themselves"),autoOffText:g.string().nullable().describe("Why automatic updates were turned off, if they were"),previous:g.string().nullable().describe("The version an update replaced (roll back goes there)"),failed:pbe.nullable().describe("The last update that failed"),npmCommand:g.string().describe("How an npm install is updated"),error:g.string().nullable().describe("Why the status could not be read"),settings:g.object({pending:g.array(g.string()).describe('Each change still to make, as "name: what it does"'),error:g.string().nullable().describe("Why your saved settings could not be read")}).describe("Changes still to make to your saved settings")}),Cz=Qe({id:"updates.status",title:"Updates",summary:"which version is installed and what your saved settings still need",input:g.object({}),output:hbe,surfaces:{except:{cli:"no command shows this page; sandbox update --check and --version cover the command line"}}}),pQ=g.object({line:g.string().describe("What happened, in one sentence")}),ts=we({id:"updates.update",title:"Update now",summary:"install the newest version, or check, go back, or switch automatic updates",input:g.object({check:g.boolean().default(!1).describe("only look whether a newer version exists"),rollback:g.boolean().default(!1).describe("go back to the version before the last update"),auto:g.enum(["on","off"]).optional().describe("install new versions by themselves (for everyone on this PC: needs an admin terminal)"),unattended:g.boolean().default(!1).describe("used by automatic updates: no questions, short output"),feed:g.string().optional().describe("take the release from this folder (a feed.json and the package) instead of GitHub")}),output:pQ.extend({latest:g.string().nullable().describe("The newer version a check found")}),danger:!0,text:e=>e.line,cli:{name:"update",group:"Health",flags:{check:{flag:"--check"},rollback:{flag:"--rollback"},auto:{flag:"--auto",value:"on|off"},unattended:{flag:"--unattended",noMenu:!0,advanced:!0},feed:{flag:"--feed",value:"folder",noMenu:!0,advanced:!0}},yes:"(--rollback) do not ask first"}}),ab=we({id:"updates.migrate",title:"Update saved settings",summary:"bring your saved settings up to date with this version",input:g.object({dryRun:g.boolean().default(!1).describe("only list what would change"),restore:g.string().optional().describe("put back the files saved before an update (a folder under <storage>\\backups)")}),output:pQ,text:e=>e.line,cli:{name:"migrate",group:"Health",flags:{dryRun:{flag:"--dry-run"},restore:{flag:"--restore",value:"backup",advanced:!0}},yes:"(--restore) do not ask first"}}),Uc=(e,t,r={})=>({id:"updates:"+e,label:t,...r});function gbe(e,t){let r=e.managed&&!e.error,n=e.scope==="machine",o=e.autoUpdate?"off":"on",i=ve(ts,{rollback:!0},{label:"Roll back",verb:"go back"}),a={id:"program",title:"This program",notes:[{text:"After an update this page still runs the version it started with; sandbox web open (or the next sandbox start) starts the new one.",tone:"muted",only:"web"}],items:e.error?[{id:"updates:error",label:e.error,tone:"danger"}]:e.managed?[Uc("version","Version",{hint:e.version,description:"Installed "+(n?"for everyone on this PC":"for you")+"."}),Uc("latest","Newest version",{hint:t??"not looked yet",tone:t?"warn":"muted",keys:t?"Enter installs it.":"Enter looks on GitHub for a newer version.",primary:t?ve(ts,{},{label:"Install "+t,verb:"install"}):ve(ts,{check:!0},{label:"Check now",verb:"look",danger:!1})}),Uc("auto","Automatic updates",{hint:e.autoUpdate?"on":"off",tone:e.autoUpdate?"ok":"muted",description:n?"For everyone: needs an admin terminal.":"A new version installs when no sandbox runs.",keys:"Enter switches them.",primary:ve(ts,{auto:o},{label:"Automatic updates "+o,verb:"switch",danger:!1}),actions:[ve(ts,{auto:o},{key:"a",label:"Automatic updates "+o,verb:"automatic on/off",danger:!1})]}),...e.autoOffText?[Uc("autooff","Automatic updates turned off",{hint:"off",tone:"warn",description:e.autoOffText})]:[],...e.previous?[Uc("previous","Previous version",{hint:e.previous,keys:"Enter goes back to it.",primary:i,actions:[{...i,key:"r",verb:"roll back"}]})]:[],...e.failed?[Uc("failed","Last failed update",{hint:e.failed.version,tone:"danger",description:"It is not tried again until a newer version is released.",details:[e.failed.reason]})]:[]]:[Uc("version","Version",{hint:e.version,description:"Installed with npm, so npm updates it: "+e.npmCommand}),Uc("npm","Update",{hint:"with npm",tone:"muted",description:"Run "+e.npmCommand+" in a terminal."})]},s=e.settings.pending,c={id:"settings",title:"Saved settings",items:e.settings.error?[{id:"settings:error",label:e.settings.error,tone:"danger"}]:s.length?s.map((d,u)=>({id:"settings:"+u,label:d.split(":")[0],hint:"to update",tone:"warn",description:"A backup is kept first.",keys:"Enter updates your saved settings.",details:[d],primary:ve(ab,{},{label:"Update saved settings",verb:"update"}),actions:[ve(ab,{},{key:"s",label:"Update saved settings",verb:"update saved settings"})]})):[{id:"settings:none",label:"Up to date",hint:"OK",tone:"ok",description:"Nothing to update."}]};return{title:"Updates",sections:[a,c],actions:r?[ve(ts,{check:!0},{key:"c",label:"Check now",verb:"check now",danger:!1}),ve(ts,{},{key:"u",label:"Update now",verb:"update now"})]:[]}}var hQ=mt({id:"updates",title:"Updates",summary:"Which version is installed, installing a newer one, and bringing your saved settings up to date.",queries:{status:Cz},operations:[ts,ab],build:({status:e})=>gbe(e,null)});var gk={except:{web:"the page is the web UI"}},vbe=g.object({running:g.boolean().describe("The web UI server answers"),port:g.number().nullable().describe("Its port on 127.0.0.1, when it runs"),address:g.string().nullable().describe("Its address (without the login token), when it runs")}),xbe=g.object({message:g.string().describe("What happened")}),Pz=Qe({id:"web.status",title:"Web UI",summary:"whether the web UI server runs, and its address",input:g.object({}),output:vbe,surfaces:gk,text:e=>e.running&&e.port?"The web UI is running at http://127.0.0.1:"+e.port+"/ (sandbox web open signs you in).":"The web UI is not running (sandbox web start).",cli:{name:"web",action:"status",json:!0}}),vk=we({id:"web.start",title:"Start the web UI",summary:"start the web UI server in the background (this PC only)",input:g.object({}),output:g.object({port:g.number().describe("Its port"),message:g.string().describe("What happened")}),surfaces:gk,text:e=>e.message,cli:{name:"web",action:"start"}}),xk=we({id:"web.open",title:"Open the web UI",summary:"start the web UI if needed and open it in your browser, signed in",input:g.object({}),output:xbe,surfaces:gk,text:e=>e.message,cli:{name:"web",action:"open"}}),bk=we({id:"web.stop",title:"Stop the web UI",summary:"stop the web UI server",input:g.object({}),output:g.object({stopped:g.boolean().describe("It was running and stopped"),message:g.string().describe("What happened")}),surfaces:gk,text:e=>e.message,cli:{name:"web",action:"stop"}});function bbe(e){let t=ve(xk,{},{key:"o",verb:"open"}),r=ve(vk,{},{key:"s",verb:"start"}),n=ve(bk,{},{key:"x",verb:"stop"});return{title:"Web UI",sections:[{id:"web",facts:[{id:"running",label:"Running",value:e.running?"yes":"no",tone:e.running?"ok":"muted"},...e.address?[{id:"address",label:"Address",value:e.address+" (this PC only)"}]:[]],actions:e.running?[t,n]:[r,t],notes:[{text:"Loopback only, behind a token. Open signs you in.",tone:"muted"}]}]}}var gQ=mt({id:"web",title:"Web UI",summary:"The web UI server on this PC: start, open, stop.",queries:{status:Pz},operations:[vk,xk,bk],build:({status:e})=>bbe(e)});var vQ=[Q1,tk,rk,ok,uk,az,O1,z1,N1,es,Z1,tb,rb,Jx,F1,L1,M1,B1,q1,K1,X1,ik,ib,ak,sk,lk,ck,Bu,ob,nb,Y1,J1,ek,ts,ab,j1,U1,rh,V1,H1,vk,xk,bk,fk,mk,pk,hk],xQ=[bz,wz,iz,fz,FX,th,lz,mz,yz,vz,xz,cz,dz,_z,Iz,Cz,pz,VX,hz,Pz,Sz,kz],ybe=[oQ,uQ,AX,MX,OX,BX,aQ,XX,eQ,zX,cQ,hQ,HX,gQ,fQ,mQ],pvt=[...vQ,...xQ];function yk(e){return vQ.find(t=>t.id===e)}function ju(e){return xQ.find(t=>t.id===e)}function Dz(e){return ybe.find(t=>t.id===e)}function Uu(e){return e.flatMap(t=>[t,...Uu(t.children??[])])}function bQ(e){return Uu(e).flatMap(t=>[...t.view?[t.view]:[],...(t.embeds??[]).map(r=>r.view)])}function yQ(e,t){return Uu(e).find(r=>r.embeds?.some(n=>n.view===t))}var nl=[{label:"Agents",view:"agents"},{label:"Sessions",view:"sessions"},{label:"Sandbox",children:[{label:"Status",view:"sandbox"},{label:"Projects",view:"projects"}]},{label:"Apps",view:"apps"},{label:"Settings",view:"settings",embeds:[{label:"VPN",view:"vpn"},{label:"GitHub",view:"github"}]},{label:"Accounts",view:"accounts"},{label:"Maintenance",children:[{label:"Updates",view:"updates"},{label:"Data",view:"data"},{label:"Storage",view:"storage"},{label:"Health",view:"health"},{label:"Logs",view:"logs"},{label:"Engine",view:"engine"},{label:"More",elsewhere:"The less common commands are in the menu (sandbox > Maintenance)."}]}];var _k=y(O(),1),Ik={Agents:IE,Sessions:Ys,Sandbox:dp,Apps:eE,Settings:wE,Accounts:RE,Maintenance:DE},_be={project:Hv,folder:Zv,database:lE,session:Ys},Ibe={claude:"orange",codex:"dark",gemini:"blue",opencode:"gray",qwen:"violet",crush:"pink",copilot:"teal",kilo:"yellow",amp:"red",cline:"cyan",continue:"grape",kimi:"indigo",aider:"green",deepseek:"blue",reasonix:"lime"};function Vu({name:e,size:t=36}){let r=_be[e];return r?(0,_k.jsx)(Gd,{size:t,radius:"md",color:"indigo",variant:"light",children:(0,_k.jsx)(r,{size:t*.55,stroke:1.6})}):(0,_k.jsx)(Gd,{size:t,radius:"md",color:Ibe[e]??"indigo",variant:"filled",fw:700,children:e.slice(0,1).toUpperCase()})}var wk=y(R(),1),wbe="sessions";function Sbe(e,t=""){let r=/^\/s\/([A-Za-z0-9_-]{1,64})\/?$/.exec(e);if(r)return{page:"terminal",id:r[1]};let n=/^\/v\/([a-z][a-z0-9-]{0,40})\/?$/.exec(e),o=Object.fromEntries(new URLSearchParams(t));return{page:"view",view:n?n[1]:wbe,params:o}}function To(e,t={}){let r=new URLSearchParams(t).toString();return"/v/"+e+(r?"?"+r:"")}function Yr(e){history.pushState(null,"",e),window.dispatchEvent(new PopStateEvent("popstate"))}function _Q(){let[e,t]=(0,wk.useState)({path:location.pathname,search:location.search});return(0,wk.useEffect)(()=>{let r=()=>t({path:location.pathname,search:location.search});return window.addEventListener("popstate",r),()=>window.removeEventListener("popstate",r)},[]),Sbe(e.path,e.search)}var os=y(R(),1);var Ck=y(R(),1);var rs=y(R(),1);var vt=y(O(),1),kbe={ask:()=>Promise.resolve(void 0)};function IQ(e,t){if(!t||!/^[A-Za-z]:\\|^\\\\/.test(t))return;if(e==="dir")return t;let r=t.lastIndexOf("\\");return r>2?t.slice(0,r):t.slice(0,3)}function Cbe({kind:e,initial:t,done:r}){let n=kk(),[o,i]=(0,rs.useState)(null),[a,s]=(0,rs.useState)(IQ(e,t)??""),[l,c]=(0,rs.useState)(e==="file"?t??null:null),[d,u]=(0,rs.useState)(null),[f,m]=(0,rs.useState)(!1),p=(0,rs.useCallback)(v=>{m(!0),u(null),Pbe(n,v,e==="file").then(x=>{i(x),s(x.path),c(null),m(!1)},x=>{u(x instanceof Error?x.message:"That folder cannot be opened."),m(!1)})},[n,e]);(0,rs.useEffect)(()=>p(IQ(e,t)),[p,e,t]);let h=e==="dir"?l??o?.path??"":l??"",b=v=>{v.key==="Enter"&&(v.preventDefault(),p(a.trim()||void 0))};return(0,vt.jsxs)(Ue,{gap:"sm",children:[(0,vt.jsxs)(De,{gap:"xs",wrap:"nowrap",children:[(0,vt.jsx)(kt,{label:"Up one folder",children:(0,vt.jsx)(bt,{size:"lg",variant:"default","aria-label":"Up one folder",disabled:!o?.path,onClick:()=>p(o?.parent??void 0),children:(0,vt.jsx)(rE,{size:16})})}),(0,vt.jsx)(er,{style:{flex:1},value:a,placeholder:"Type a path and press Enter",onChange:v=>s(v.currentTarget.value),onKeyDown:b,ff:"monospace","aria-label":"Folder",rightSection:f?(0,vt.jsx)(fo,{size:14}):null})]}),d&&(0,vt.jsx)(za,{color:"red",variant:"light",p:"xs",children:d}),(0,vt.jsxs)(De,{align:"stretch",gap:"sm",wrap:"nowrap",children:[(0,vt.jsxs)(Ue,{gap:2,w:170,visibleFrom:"xs",style:{flex:"none"},children:[(0,vt.jsx)(ye,{size:"xs",fw:650,c:"dimmed",tt:"uppercase",px:6,pb:4,children:"Places"}),(o?.places??[]).map(v=>(0,vt.jsxs)("div",{className:"picker-entry",onClick:()=>p(v.path),title:v.path,children:[v.label.endsWith(":")?(0,vt.jsx)(Vv,{size:16}):(0,vt.jsx)(Hv,{size:16}),(0,vt.jsx)(ye,{size:"sm",truncate:!0,children:v.label})]},v.path))]}),(0,vt.jsxs)(Ar,{className:"picker-list",style:{flex:1},type:"auto",children:[!o?.path&&(0,vt.jsx)(ye,{size:"sm",c:"dimmed",p:"md",children:"Pick a place on the left, or type a path."}),o?.path&&o.entries.length===0&&(0,vt.jsx)(ye,{size:"sm",c:"dimmed",p:"md",children:e==="dir"?"No folders in here.":"Nothing in here."}),o?.entries.map(v=>(0,vt.jsxs)("div",{className:"picker-entry","data-selected":l===v.path,onClick:()=>c(v.path),onDoubleClick:()=>v.kind==="dir"?p(v.path):r(v.path),children:[v.kind==="dir"?(0,vt.jsx)(Zv,{size:16,color:"var(--mantine-color-yellow-6)"}):(0,vt.jsx)(mE,{size:16,color:"var(--app-muted)"}),(0,vt.jsx)(ye,{size:"sm",truncate:!0,children:v.name})]},v.path)),o?.truncated&&(0,vt.jsx)(ye,{size:"xs",c:"dimmed",p:"sm",children:"Only the first entries are shown: type the rest of the path."})]})]}),(0,vt.jsxs)(De,{justify:"space-between",wrap:"nowrap",gap:"sm",children:[(0,vt.jsx)(ye,{size:"xs",c:"dimmed",ff:"monospace",truncate:!0,style:{minWidth:0},children:h||(e==="file"?"Pick a file":"Pick a folder")}),(0,vt.jsxs)(De,{gap:"xs",wrap:"nowrap",style:{flex:"none"},children:[(0,vt.jsx)(ht,{variant:"default",onClick:()=>r(void 0),children:"Cancel"}),(0,vt.jsx)(ht,{disabled:!h,onClick:()=>r(h),children:e==="dir"?"Choose this folder":"Choose"})]})]})]})}function Pbe(e,t,r){return e.call("files.browse",{path:t,files:r},kbe)}function Sk(e,t,r){return new Promise(n=>{let o=!1,i=s=>{o||(o=!0,Ua.close(a),n(s))},a=Ua.open({title:r??(e==="dir"?"Choose a folder":"Choose a file"),size:"xl",onClose:()=>i(void 0),children:(0,vt.jsx)(Cbe,{kind:e,initial:t,done:i})})})}var rr=y(O(),1);function wQ(e){return e.kind==="confirm"?!1:void 0}function Dbe({question:e,said:t,done:r}){let[n,o]=(0,Ck.useState)(e.kind==="text"||e.kind==="path"?e.initial??"":""),[i,a]=(0,Ck.useState)(e.kind==="choose"?e.initial??e.choices[0]?.value??"":""),[s,l]=(0,Ck.useState)(e.kind==="checklist"?[...e.checked??[]]:[]),c=[...t,...e.lines??[]],d="problem"in e?e.problem:void 0,u=e.kind!=="confirm"||!e.typed||n.trim().toLowerCase()===e.typed;return(0,rr.jsx)("form",{onSubmit:m=>{m.preventDefault(),e.kind==="confirm"?r(u):e.kind==="choose"?r(i):e.kind==="checklist"?r(s):r(n===""&&e.kind==="text"&&!e.allowEmpty?void 0:n)},children:(0,rr.jsxs)(Ue,{gap:"sm",children:[c.map((m,p)=>(0,rr.jsx)(ye,{size:"sm",children:m},p)),d&&(0,rr.jsx)(za,{color:"orange",variant:"light",p:"xs",children:d}),e.kind==="confirm"&&e.typed&&(0,rr.jsx)(er,{label:'Type "'+e.typed+'" to confirm',value:n,onChange:m=>o(m.currentTarget.value),"data-autofocus":!0}),e.kind==="choose"&&(0,rr.jsx)(Vs.Group,{value:i,onChange:a,children:(0,rr.jsx)(Ue,{gap:"xs",children:e.choices.map(m=>(0,rr.jsx)(Vs,{value:m.value,label:m.label,description:m.description},m.value))})}),e.kind==="checklist"&&(0,rr.jsx)(Fs.Group,{value:s,onChange:l,children:(0,rr.jsx)(Ue,{gap:"xs",children:e.choices.map(m=>(0,rr.jsx)(Fs,{value:m.value,label:m.label,description:m.description},m.value))})}),e.kind==="text"&&(0,rr.jsx)(er,{value:n,onChange:m=>o(m.currentTarget.value),"data-autofocus":!0}),e.kind==="path"&&(0,rr.jsxs)(De,{gap:"xs",wrap:"nowrap",align:"flex-end",children:[(0,rr.jsx)(er,{style:{flex:1},ff:"monospace",value:n,onChange:m=>o(m.currentTarget.value),description:"The full path of a "+(e.pathKind==="file"?"file":"folder")+" on this PC","data-autofocus":!0}),(0,rr.jsx)(kt,{label:e.pathKind==="file"?"Choose a file":"Choose a folder",children:(0,rr.jsx)(bt,{variant:"default",size:36,"aria-label":"Browse",onClick:()=>{Sk(e.pathKind,n||void 0).then(m=>m&&o(m))},children:(0,rr.jsx)(Wv,{size:18,stroke:1.6})})})]}),e.kind==="secret"&&(0,rr.jsx)(Yd,{value:n,onChange:m=>o(m.currentTarget.value),autoComplete:"new-password","data-autofocus":!0}),(0,rr.jsxs)(De,{justify:"flex-end",children:[(0,rr.jsx)(ht,{variant:"default",onClick:()=>r(wQ(e)),children:"Cancel"}),(0,rr.jsx)(ht,{type:"submit",color:e.kind==="confirm"&&e.danger?"red":void 0,disabled:!u,children:e.kind==="confirm"?e.yes??"Yes":"OK"})]})]})})}function SQ(e,t=[]){return new Promise(r=>{let n=!1,o=a=>{n||(n=!0,Ua.close(i),r(a))},i=Ua.open({title:e.title,centered:!0,onClose:()=>o(wQ(e)),children:(0,rr.jsx)(Dbe,{question:e,said:t,done:o})})})}var PQ=y(O(),1),kQ=(0,os.createContext)(null);function CQ({client:e,children:t}){return(0,PQ.jsx)(kQ.Provider,{value:e,children:t})}function kk(){let e=(0,os.useContext)(kQ);if(!e)throw new Error("useRpc needs an RpcProvider");return e}var Rbe={ask:()=>Promise.resolve(void 0)};function il(e,t){let r=kk(),n=Wm(),o=e.live!==void 0,i=JSON.stringify(t??{}),[a,s]=(0,os.useState)(null),l=QR({queryKey:[e.id,i],queryFn:()=>r.call(e.id,t,Rbe),enabled:!o});(0,os.useEffect)(()=>{if(o)return r.subscribe(e.id,JSON.parse(i),d=>{s(null),n.setQueryData([e.id,i],d)},s)},[r,n,e.id,i,o]);let c=a??l.error;return{data:l.data,error:c,pending:l.data===void 0&&!c}}function Pk(){let e=kk(),t=Wm();return(0,os.useCallback)(async(r,n={},o={})=>{let i=yk(r.op),a="op-"+Date.now(),s=[],l=!1;try{let c=await e.call(r.op,{...r.input,...n},{progress:f=>{let m={id:a,title:r.label,message:f,loading:!0,autoClose:!1};l?Qo.update(m):Qo.show(m),l=!0},log:f=>s.push(f),ask:f=>SQ(f,s.splice(0))}),d=i?.text?.(c)??"Done.",u={id:a,title:o.inline?void 0:r.label,message:d,color:"green",loading:!1,autoClose:o.inline?2500:5e3};return l?Qo.update(u):Qo.show(u),{ok:!0,text:d,output:c}}catch(c){if(l&&Qo.hide(a),c instanceof Qx)return{ok:!1,cancelled:!0,message:"Cancelled."};let d=c instanceof Error?c.message:"It did not work.",u=c.fix??(s.length?s.join(" "):void 0);return o.inline||Qo.show({title:d,message:u,color:"red",autoClose:1e4}),{ok:!1,message:d,fix:u}}finally{await t.invalidateQueries()}},[e,t])}var Ebe=2,Tbe=1,DQ=class{activate(e){this._terminal=e}dispose(){}fit(){let e=this.proposeDimensions();if(!e||!this._terminal||isNaN(e.cols)||isNaN(e.rows))return;let t=this._terminal._core;(this._terminal.rows!==e.rows||this._terminal.cols!==e.cols)&&(t._renderService.clear(),this._terminal.resize(e.cols,e.rows))}proposeDimensions(){if(!this._terminal||!this._terminal.element||!this._terminal.element.parentElement)return;let e=this._terminal._core._renderService.dimensions;if(e.css.cell.width===0||e.css.cell.height===0)return;let t=this._terminal.options.scrollback===0?0:this._terminal.options.overviewRuler?.width||14,r=window.getComputedStyle(this._terminal.element.parentElement),n=parseInt(r.getPropertyValue("height")),o=Math.max(0,parseInt(r.getPropertyValue("width"))),i=window.getComputedStyle(this._terminal.element),a={top:parseInt(i.getPropertyValue("padding-top")),bottom:parseInt(i.getPropertyValue("padding-bottom")),right:parseInt(i.getPropertyValue("padding-right")),left:parseInt(i.getPropertyValue("padding-left"))},s=a.top+a.bottom,l=a.right+a.left,c=n-s,d=o-l-t;return{cols:Math.max(Ebe,Math.floor(d/e.css.cell.width)),rows:Math.max(Tbe,Math.floor(c/e.css.cell.height))}}};var $be=class{constructor(){this.listeners=[],this.unexpectedErrorHandler=function(e){setTimeout(()=>{throw e.stack?RQ.isErrorNoTelemetry(e)?new RQ(e.message+`
128
+ `+e.next:e.message,cli:{name:"projects",action:"add",positional:["projectFolder"]}}),ak=we({id:"projects.remove",title:"Forget project",summary:"forget a project (nothing in the folder is touched)",input:g.object({projectFolder:nk("The project folder")}),output:nh,danger:!0,text:e=>e.message,cli:{name:"projects",action:"remove",positional:["projectFolder"]}}),sk=we({id:"projects.include",title:"Include",summary:"map a project at every start again",input:g.object({projectFolder:nk("The project folder")}),output:nh,text:e=>e.message,cli:{name:"projects",action:"include",positional:["projectFolder"]}}),lk=we({id:"projects.exclude",title:"Exclude",summary:"leave a project out of the projects mapped at every start",input:g.object({projectFolder:nk("The project folder")}),output:nh,text:e=>e.message,cli:{name:"projects",action:"exclude",positional:["projectFolder"]}}),ck=we({id:"projects.all",title:"Map all projects at every start",summary:"map every project at every start, or not",input:g.object({state:g.enum(["on","off"]).optional().describe("on or off (left out: say which it is)")}),output:nh,text:e=>e.message,cli:{name:"projects",action:"all",positional:["state"]}}),Bu=we({id:"projects.roots",title:"Project roots",summary:"list, add or remove project roots",input:g.object({change:g.enum(["list","add","remove"]).default("list").describe("list, add or remove"),rootFolder:g.string().optional().describe("(add, remove) the folder that holds your projects")}),output:nh,text:e=>e.message,cli:{name:"projects",action:"roots",positional:["change","rootFolder"]}}),nQ=E1(Bu.input).find(e=>e.name==="rootFolder"),Oxe=E1(ib.input).find(e=>e.name==="projectFolder"),zxe="Every project in this folder (also new ones) opens without a restart.";function iQ(e){return e.root?"root":e.excluded?"excluded":e.mappedAtStart?"mapped":"-"}function Nxe(e){return{id:"project:"+e.folder,label:e.folder,marked:e.here,icon:"project",hint:e.running??(e.root?"in a root":e.excluded?"excluded":e.mappedAtStart?"mapped":"-"),description:e.here?"This is the folder you started the menu in.":void 0,details:["At start: "+iQ(e),"In the sandbox: "+(e.running??"not open")],cells:{here:e.here?"*":"",folder:e.folder,atStart:iQ(e),running:e.running??"-"},actions:[ve(ak,{projectFolder:e.folder},{key:"f",verb:"forget project",importance:1}),e.excluded?ve(sk,{projectFolder:e.folder}):ve(lk,{projectFolder:e.folder})]}}function Fxe(e){return{id:"root:"+e,label:e,hint:"root",icon:"folder",description:zxe,keys:"f removes the root.",actions:[ve(Bu,{change:"remove",rootFolder:e},{label:"Remove the root",key:"f",verb:"remove root",importance:1})]}}function Lxe(e){let t={id:"settings",facts:[{id:"all",label:"All projects",value:e.all?"on (every project below is mapped at every start, except excluded ones)":"off"},{id:"roots",label:"Roots",value:e.roots.length?e.roots.join(", "):"none (sandbox projects roots add <folder>)"}],actions:[ve(ck,{state:e.all?"off":"on"},{label:e.all?"Stop mapping all projects":"Map all projects at every start"})],notes:[{text:"sandbox projects all on|off",tone:"muted",only:"cli"}]},r={id:"roots",title:"Project roots",items:[{id:"@add-root",label:"+ Add a project root\u2026",description:"Pick a folder that holds your projects: every project in it (also new ones) opens without a restart.",primary:ve(Bu,{change:"add"},{label:"Choose the folder\u2026",verb:"choose folder",fields:[nQ]})},...e.roots.map(Fxe)]},n={id:"projects",title:"Projects",columns:[{key:"here",label:""},{key:"folder",label:"FOLDER"},{key:"atStart",label:"AT START"},{key:"running",label:"RUNNING SANDBOX"}],columnsOn:"cli",items:e.projects.map(Nxe),notes:e.projects.length===0?[{text:"No projects yet: start one with sandbox claude in its folder.",tone:"muted"}]:[{text:"* = this folder. Choose with: sandbox projects choose",tone:"muted",only:"cli"}]};return{title:"Projects",sections:[t,r,n],actions:[ve(ik,{},{label:"Choose\u2026"}),ve(ib,{},{fields:[Oxe]}),ve(Bu,{change:"add"},{label:"Add a project root\u2026",key:"a",verb:"add root",importance:1,fields:[nQ]})]}}var aQ=mt({id:"projects",title:"Projects",summary:"The projects a sandbox opens without a restart: roots, your projects, all or chosen.",queries:{projects:yz},operations:[ik,ib,ak,sk,lk,ck,Bu],build:({projects:e})=>Lxe(e)});var dk="@crash",Mxe=1e3,lQ="sandbox logs is hand-written: --follow prints new lines until Ctrl+C; it reads this same query (readQuery)",Bxe=g.object({name:g.string().describe("The log file"),path:g.string().describe("Where it is on this PC"),bytes:g.number().describe("Its size in bytes"),modified:g.number().describe("When it was last written (ms)")}),jxe=g.object({folder:g.string().describe("The folder the logs are in"),files:g.array(Bxe).describe("The log files, newest first"),crash:g.boolean().describe("A crash report exists")}),Uxe=g.object({name:g.string().describe("The log shown, or @crash for the last crash report (empty: there are no logs)"),file:g.string().describe("Its full path (empty: there are no logs)"),text:g.string().describe("The text read, made safe for a terminal"),next:g.number().describe("Where to read on from next time (bytes)")}),_z=Qe({id:"logs.list",title:"Logs",summary:"list the sandbox's own logs",input:g.object({}),output:jxe,surfaces:{except:{cli:lQ}}}),Iz=Qe({id:"logs.tail",title:"Log",summary:"the end of one log, followed as it grows",input:g.object({name:g.string().max(200).optional().describe("The log to show (default: the newest), or @crash for the last crash report"),from:g.number().int().min(0).default(0).describe("Read on from this byte (0: the end of the log)"),lines:g.number().int().min(1).max(1e3).default(120).describe("How many lines of the end to show")}),output:Uxe,live:{everyMs:1e3},surfaces:{except:{cli:lQ}}}),sQ={text:"showing",tone:"accent"};function Vxe(e){let t=e.split(`
129
+ `);return t.length&&t[t.length-1]===""&&t.pop(),t.slice(-Mxe)}function Hxe(e,t){let r=t?.file?t:void 0,n=e.files.map(i=>({id:"log:"+i.name,label:i.name,hint:Yx(i.bytes)+" "+Tn(i.modified),keys:"Enter shows the end of this log in the terminal.",primary:Ja({kind:"view",view:"logs",params:{name:i.name}},"Show",{verb:"show"}),...r?.name===i.name?{badges:[sQ]}:{}}));e.crash&&n.push({id:"log:"+dk,label:"Last crash report",description:"What Windows logged, this PC and the sandbox memory when the sandbox last ended in a crash.",primary:Ja({kind:"view",view:"logs",params:{name:dk}},"Show",{verb:"show"}),...r?.name===dk?{badges:[sQ]}:{}});let o=r?[{id:"tail",title:r.name===dk?"Last crash report":r.name,summary:r.file,text:Vxe(r.text)}]:[];return{title:"Logs",sections:[{id:"files",items:n},...o],actions:[Ja({kind:"command",argv:["logs","--follow"]},"Follow the newest log",{key:"f",verb:"follow in the terminal",importance:2})],empty:{title:"No logs yet",text:"Logs appear after the first start.",cliText:"No sandbox logs yet."}}}var cQ=mt({id:"logs",title:"Logs",summary:"The sandbox's own logs: pick one to see its end as it grows.",queries:{files:_z,tail:Iz},operations:[],params:["name"],inputs:e=>({tail:e.name?{name:e.name}:{}}),build:({files:e,tail:t})=>Hxe(e,t)});var Wxe=g.object({viewers:g.number(),remote:g.number()}),Zxe=g.object({id:g.string().describe("The session"),launchId:g.string(),harness:g.string().describe("The agent"),cwd:g.string().describe("Its folder in the sandbox"),project:g.string().optional(),phase:g.enum(["starting","running","exited","failed"]).describe("What it is doing"),message:g.string(),updatedAt:g.string(),startedAt:g.string().describe("When it started"),conversation:g.string().optional(),exitCode:g.number().optional(),projectName:g.string().optional().describe("The project it runs in"),here:g.boolean().optional().describe("In the project of the current folder"),viewers:Wxe.nullable().optional(),canOpen:g.boolean()}),Gxe=g.object({sandboxRunning:g.boolean(),defaultAgent:g.string(),wide:g.boolean(),owner:g.string().optional(),sessions:g.array(Zxe)});function dQ(e){return e===void 0?"-":e===null?"unknown (older session)":e.remote+" here, "+(e.viewers-e.remote)+" in sandbox"}function qxe(e){let t=(r,n)=>r.startedAt<n.startedAt?1:-1;return[...e.filter(r=>r.here).sort(t),...e.filter(r=>!r.here).sort(t)]}var wz=Qe({id:"sessions.list",title:"Sessions",summary:"list agent sessions and who is attached",input:g.object({wide:g.boolean().default(!1).describe("also show each session's folder")}),output:Gxe,live:{everyMs:2500},cli:{name:"sessions",group:"Sessions",json:!0,flags:{wide:{flag:"--wide",noMenu:!0}},shapeJson:e=>e.sessions.map(({canOpen:t,...r})=>r)}}),uk=we({id:"sessions.restart",title:"Restart agents",summary:"restart this project's running agents in place; they resume their conversation",input:g.object({harness:g.string().optional().describe("only this agent"),all:g.boolean().default(!1).describe("every project's sessions, not only this folder's")}),output:g.object({message:g.string().describe("The sandbox's answer")}),text:e=>e.message,cli:{name:"restart",group:"Agents",flags:{harness:{flag:"--harness",shownAs:"--agent",value:"name",choices:"harnesses"},all:{flag:"--all"}}}});function Kxe(e,t){let r=e.phase+(e.exitCode!==void 0?" ("+e.exitCode+")":""),n=e.phase==="running";return{id:e.id,label:e.harness+" "+(e.projectName??"-"),hint:r+" "+Tn(e.startedAt),badges:[{text:e.phase,tone:n?"ok":e.phase==="failed"?"danger":"muted"}],group:e.here?"This folder's project":"Other sessions",marked:e.here,icon:e.harness,description:n?"Opens this session's terminal (in a terminal, Ctrl+] detaches; the session keeps running).":void 0,details:["Session "+e.id,"Folder "+e.cwd,"Attached "+dQ(e.viewers)],cells:{here:e.here?"*":"",id:e.id,agent:e.harness,project:e.projectName??"-",state:r,attached:dQ(e.viewers),...t?{folder:e.cwd}:{},started:Tn(e.startedAt)},...n&&e.canOpen?{primary:Ja({kind:"terminal",session:e.id},"Open",{verb:"attach"})}:{},...n?{}:{disabled:"This session has ended."},...n&&!e.canOpen?{disabled:"Not reachable from this PC."}:{}}}function Xxe(e){let t=qxe(e.sessions),r=e.defaultAgent,n=Ja({kind:"start-agent",agent:r},"Start "+r,{verb:"start"}),o=t.length&&!t.some(i=>i.phase==="running"||i.phase==="starting")?[{id:"start",actions:[{...n,description:"Starts "+r+" in this folder."}]}]:[];return{title:"Sessions",...e.owner?{notice:{text:e.owner,tone:"accent"}}:{},sections:[{id:"sessions",cards:!0,columns:[{key:"here",label:""},{key:"id",label:"ID"},{key:"agent",label:"AGENT"},{key:"project",label:"PROJECT"},{key:"state",label:"STATE"},{key:"attached",label:"ATTACHED"},...e.wide?[{key:"folder",label:"FOLDER"}]:[],{key:"started",label:"STARTED"}],items:t.map(i=>Kxe(i,e.wide)),notes:[...t.some(i=>i.here)?[{text:"* = the project of this folder",tone:"muted"}]:[],...e.wide?[]:[{text:"Folders: sandbox sessions --wide",tone:"muted",only:"cli"}]]},...o],actions:[...e.sandboxRunning?[{...n,label:"New session",key:"n",verb:"new session",importance:1}]:[],...t.length?[ve(uk,{all:!1},{key:"r",verb:"restart agents",importance:1})]:[],Ja({kind:"command",argv:["resume"]},"Reopen last sessions",{key:"o",verb:"reopen last sessions",importance:2})],empty:{title:e.sandboxRunning?"No sessions":"No sandbox is running",text:e.sandboxRunning?"No agent is running. Start "+r+" in this folder.":"Starting "+r+" starts the sandbox too; its session is listed here.",action:n,cliText:"No sessions. Start one with: sandbox claude"}}}var uQ=mt({id:"sessions",title:"Sessions",summary:"The agent sessions of the running sandbox: open one, restart the agents.",queries:{sessions:wz},operations:[uk],build:({sessions:e})=>Xxe(e)});var Qxe=g.enum(["nordvpn","openvpn-file","wireguard-file"]).describe("Which VPN (setting vpn.provider)"),Yxe=g.enum(["none","saved","unreadable"]).describe("Whether a login is saved and can be opened by you"),Jxe=["The VPN is an alpha: a tunnel carrying traffic is not confirmed yet (docs/vpn.md lists what still needs checking).","The agent in the sandbox can read the VPN login (a service login you can revoke) and can turn the tunnel off, because it is the administrator there.","Free VPNs see all your traffic: use one you trust."];var ebe=g.object({enabled:g.boolean().describe("The VPN is on (setting vpn.enabled)"),provider:Qxe,protocol:g.enum(["udp","tcp"]).describe("udp or tcp (setting vpn.protocol)"),country:g.string().describe("The NordVPN country; empty = the best server anywhere"),killSwitch:g.boolean().describe("No internet when the tunnel fails (setting vpn.killSwitch)"),login:Yxe,loginSavedAt:g.string().nullable().describe("When the login was saved (the date and time, never the login)"),running:g.object({level:g.enum(["ok","warn","bad"]).describe("How the tunnel is doing"),text:g.string().describe("What the sandbox reported")}).nullable().describe("The running sandbox's tunnel, when one runs with the VPN on")}),Sz=Qe({id:"vpn.status",title:"VPN",summary:"the VPN settings, whether a login is saved, and the running tunnel",input:g.object({tunnel:g.boolean().default(!0).describe("also ask the running sandbox how its tunnel is doing (one request to api.ipify.org)")}),output:ebe,cli:{name:"vpn",action:"status",json:!0}}),fk=we({id:"vpn.login",title:"Save the VPN login\u2026",summary:"save the VPN login (typed hidden, kept encrypted, never printed)",input:g.object({}).strict(),output:g.object({message:g.string().describe("What happened")}),text:e=>e.message,cli:{name:"vpn",action:"login"}}),mk=we({id:"vpn.logout",title:"Delete the VPN login",summary:"delete the saved VPN login",input:g.object({}).strict(),output:g.object({deleted:g.boolean().describe("A login was saved and is gone"),message:g.string().describe("What happened")}),danger:!0,text:e=>e.message,cli:{name:"vpn",action:"logout"}}),tbe={nordvpn:"NordVPN","wireguard-file":"your own WireGuard .conf","openvpn-file":"your own .ovpn file"},rbe={none:"not saved",saved:"saved (encrypted)",unreadable:"cannot be read"},obe={none:"warn",saved:"ok",unreadable:"danger"},nbe={ok:"ok",warn:"warn",bad:"danger"};function ibe(e){let t=e.provider==="wireguard-file",r=e.loginSavedAt?" "+e.loginSavedAt.slice(0,10):"",n=[e.enabled?{id:"vpn",label:"VPN",value:"on",tone:"ok"}:{id:"vpn",label:"VPN",value:"off (applies from the next start)",tone:"muted"},{id:"provider",label:"Provider",value:tbe[e.provider]},{id:"protocol",label:"Protocol",value:e.protocol},{id:"country",label:"Country",value:e.country||"best available"},e.killSwitch?{id:"killSwitch",label:"Kill switch",value:"on (no internet if the tunnel fails)"}:{id:"killSwitch",label:"Kill switch",value:"off",tone:"warn"},t?{id:"login",label:"Login",value:"not needed (the keys are in your config)",tone:"muted"}:{id:"login",label:"Login",value:rbe[e.login]+r,tone:obe[e.login]},...e.running?[{id:"running",label:"Running now",value:e.running.text,tone:nbe[e.running.level]}]:[]],o=[...!t&&e.login==="none"?[{text:"Next: sandbox vpn login",tone:"muted",only:"cli"}]:[],...e.login==="unreadable"?[{text:"Only the Windows user that saved it can read it. Save it again: sandbox vpn login",tone:"muted"}]:[],...Jxe.map(l=>({text:l,tone:"warn"}))],a=[ve(es,{scope:"user",key:"vpn.enabled",value:!e.enabled},{label:e.enabled?"Turn the VPN off":"Turn the VPN on",verb:e.enabled?"turn off":"turn on",key:"o"}),...t?[]:[ve(fk,{},{key:"v",verb:"save login"}),...e.login!=="none"?[ve(mk,{},{key:"x",verb:"delete login"})]:[]]];return{title:"VPN",sections:[{id:"vpn",facts:n,notes:o,actions:a}]}}var fQ=mt({id:"vpn",title:"VPN",summary:"The sandbox's VPN: whether it is on, the saved login, and the tunnel.",queries:{status:Sz},operations:[fk,mk,es],build:({status:e})=>ibe(e)});var abe="https://github.com/settings/personal-access-tokens/new",Vgt=["Open "+abe+" (a fine-grained personal access token).",'Under "Repository access" choose "Only select repositories" and pick only the ones the agent should reach.',"Give only the permissions it needs, for example Contents (read and write), Pull requests (read and write) and Actions (read).","Set an expiry date, then generate the token and copy it."],sbe=["The agent in the sandbox can read the token and use it for everything it allows: limit it to some repositories and permissions.",'It is handed over only with github.shareToken on and git access "full". Revoke it on GitHub whenever you like.'],lbe=g.enum(["none","saved","unreadable"]).describe("Whether a token is saved and can be opened by you"),cbe=g.object({shareToken:g.boolean().describe("Sandboxes get the token (setting github.shareToken)"),gitAccess:g.enum(["full","readonly","none"]).describe("Git access in your settings (provisioning.git.access)"),token:lbe,kind:g.enum(["fine-grained","classic"]).nullable().describe("What kind of token is saved (never the token)"),savedAt:g.string().nullable().describe("When the token was saved (the date and time, never the token)")}),kz=Qe({id:"github.status",title:"GitHub",summary:"whether sandboxes get your GitHub token, and whether one is saved (never the token)",input:g.object({}).strict(),output:cbe,cli:{name:"github",action:"status",json:!0}}),pk=we({id:"github.login",title:"Save the GitHub token\u2026",summary:"save a GitHub token for sandboxes (typed hidden, kept encrypted, never printed)",input:g.object({}).strict(),output:g.object({message:g.string().describe("What happened")}),text:e=>e.message,cli:{name:"github",action:"login"}}),hk=we({id:"github.logout",title:"Delete the GitHub token",summary:"delete the saved GitHub token",input:g.object({}).strict(),output:g.object({deleted:g.boolean().describe("A token was saved and is gone"),message:g.string().describe("What happened")}),danger:!0,text:e=>e.message,cli:{name:"github",action:"logout"}}),dbe={none:"not saved",saved:"saved (encrypted)",unreadable:"cannot be read"},ube={none:"warn",saved:"ok",unreadable:"danger"},fbe={"fine-grained":"fine-grained",classic:"classic (reaches every repository you can: a fine-grained token is safer)"};function mbe(e){let t=e.savedAt?" "+e.savedAt.slice(0,10):"",r=[e.shareToken?{id:"share",label:"Share with sandboxes",value:"on",tone:"ok"}:{id:"share",label:"Share with sandboxes",value:"off (turn on: sandbox config set github.shareToken true)"},e.gitAccess==="full"?{id:"gitAccess",label:"Git access",value:"full"}:{id:"gitAccess",label:"Git access",value:e.gitAccess+' (the token is only handed over with "full")',tone:"warn"},{id:"token",label:"Token",value:dbe[e.token]+t,tone:ube[e.token]},...e.kind?[{id:"kind",label:"Kind",value:fbe[e.kind],...e.kind==="classic"?{tone:"warn"}:{}}]:[]],n=[...e.token==="none"?[{text:"Next: sandbox github login",tone:"muted",only:"cli"}]:[],...e.token==="unreadable"?[{text:"Only the Windows user that saved it can read it. Save it again: sandbox github login",tone:"muted"}]:[],...sbe.map(a=>({text:a,tone:"muted"}))],o=[ve(pk,{},{key:"g",verb:"save token"}),...e.token!=="none"?[ve(hk,{},{key:"x",verb:"delete token"})]:[]];return{title:"GitHub",sections:[{id:"github",facts:r,notes:n,actions:o}]}}var mQ=mt({id:"github",title:"GitHub",summary:"A GitHub token for sandboxes: whether they get it, and the saved token.",queries:{status:kz},operations:[pk,hk],build:({status:e})=>mbe(e)});var pbe=g.object({version:g.string().describe("The version that failed"),reason:g.string().describe("Why it failed, in one sentence")}),hbe=g.object({managed:g.boolean().describe("Installed by the installer, so it can update itself"),version:g.string().describe("The version installed"),scope:g.enum(["user","machine"]).nullable().describe("Installed for you, or for everyone on this PC"),autoUpdate:g.boolean().describe("New versions install by themselves"),autoOffText:g.string().nullable().describe("Why automatic updates were turned off, if they were"),previous:g.string().nullable().describe("The version an update replaced (roll back goes there)"),failed:pbe.nullable().describe("The last update that failed"),npmCommand:g.string().describe("How an npm install is updated"),error:g.string().nullable().describe("Why the status could not be read"),settings:g.object({pending:g.array(g.string()).describe('Each change still to make, as "name: what it does"'),error:g.string().nullable().describe("Why your saved settings could not be read")}).describe("Changes still to make to your saved settings")}),Cz=Qe({id:"updates.status",title:"Updates",summary:"which version is installed and what your saved settings still need",input:g.object({}),output:hbe,surfaces:{except:{cli:"no command shows this page; sandbox update --check and --version cover the command line"}}}),pQ=g.object({line:g.string().describe("What happened, in one sentence")}),ts=we({id:"updates.update",title:"Update now",summary:"install the newest version, or check, go back, or switch automatic updates",input:g.object({check:g.boolean().default(!1).describe("only look whether a newer version exists"),rollback:g.boolean().default(!1).describe("go back to the version before the last update"),auto:g.enum(["on","off"]).optional().describe("install new versions by themselves (for everyone on this PC: needs an admin terminal)"),unattended:g.boolean().default(!1).describe("used by automatic updates: no questions, short output"),feed:g.string().optional().describe("take the release from this folder (a feed.json and the package) instead of GitHub")}),output:pQ.extend({latest:g.string().nullable().describe("The newer version a check found")}),danger:!0,text:e=>e.line,cli:{name:"update",group:"Health",flags:{check:{flag:"--check"},rollback:{flag:"--rollback"},auto:{flag:"--auto",value:"on|off"},unattended:{flag:"--unattended",noMenu:!0,advanced:!0},feed:{flag:"--feed",value:"folder",noMenu:!0,advanced:!0}},yes:"(--rollback) do not ask first"}}),ab=we({id:"updates.migrate",title:"Update saved settings",summary:"bring your saved settings up to date with this version",input:g.object({dryRun:g.boolean().default(!1).describe("only list what would change"),restore:g.string().optional().describe("put back the files saved before an update (a folder under <storage>\\backups)")}),output:pQ,text:e=>e.line,cli:{name:"migrate",group:"Health",flags:{dryRun:{flag:"--dry-run"},restore:{flag:"--restore",value:"backup",advanced:!0}},yes:"(--restore) do not ask first"}}),Uc=(e,t,r={})=>({id:"updates:"+e,label:t,...r});function gbe(e,t){let r=e.managed&&!e.error,n=e.scope==="machine",o=e.autoUpdate?"off":"on",i=ve(ts,{rollback:!0},{label:"Roll back",verb:"go back"}),a={id:"program",title:"This program",notes:[{text:"After an update this page still runs the version it started with; sandbox web open (or the next sandbox start) starts the new one.",tone:"muted",only:"web"}],items:e.error?[{id:"updates:error",label:e.error,tone:"danger"}]:e.managed?[Uc("version","Version",{hint:e.version,description:"Installed "+(n?"for everyone on this PC":"for you")+"."}),Uc("latest","Newest version",{hint:t??"not looked yet",tone:t?"warn":"muted",keys:t?"Enter installs it.":"Enter looks on GitHub for a newer version.",primary:t?ve(ts,{},{label:"Install "+t,verb:"install"}):ve(ts,{check:!0},{label:"Check now",verb:"look",danger:!1})}),Uc("auto","Automatic updates",{hint:e.autoUpdate?"on":"off",tone:e.autoUpdate?"ok":"muted",description:n?"For everyone: needs an admin terminal.":"A new version installs once a day, even while a sandbox runs (it keeps its version until it restarts).",keys:"Enter switches them.",primary:ve(ts,{auto:o},{label:"Automatic updates "+o,verb:"switch",danger:!1}),actions:[ve(ts,{auto:o},{key:"a",label:"Automatic updates "+o,verb:"automatic on/off",danger:!1})]}),...e.autoOffText?[Uc("autooff","Automatic updates turned off",{hint:"off",tone:"warn",description:e.autoOffText})]:[],...e.previous?[Uc("previous","Previous version",{hint:e.previous,keys:"Enter goes back to it.",primary:i,actions:[{...i,key:"r",verb:"roll back"}]})]:[],...e.failed?[Uc("failed","Last failed update",{hint:e.failed.version,tone:"danger",description:"It is not tried again until a newer version is released.",details:[e.failed.reason]})]:[]]:[Uc("version","Version",{hint:e.version,description:"Installed with npm, so npm updates it: "+e.npmCommand}),Uc("npm","Update",{hint:"with npm",tone:"muted",description:"Run "+e.npmCommand+" in a terminal."})]},s=e.settings.pending,c={id:"settings",title:"Saved settings",items:e.settings.error?[{id:"settings:error",label:e.settings.error,tone:"danger"}]:s.length?s.map((d,u)=>({id:"settings:"+u,label:d.split(":")[0],hint:"to update",tone:"warn",description:"A backup is kept first.",keys:"Enter updates your saved settings.",details:[d],primary:ve(ab,{},{label:"Update saved settings",verb:"update"}),actions:[ve(ab,{},{key:"s",label:"Update saved settings",verb:"update saved settings"})]})):[{id:"settings:none",label:"Up to date",hint:"OK",tone:"ok",description:"Nothing to update."}]};return{title:"Updates",sections:[a,c],actions:r?[ve(ts,{check:!0},{key:"c",label:"Check now",verb:"check now",danger:!1}),ve(ts,{},{key:"u",label:"Update now",verb:"update now"})]:[]}}var hQ=mt({id:"updates",title:"Updates",summary:"Which version is installed, installing a newer one, and bringing your saved settings up to date.",queries:{status:Cz},operations:[ts,ab],build:({status:e})=>gbe(e,null)});var gk={except:{web:"the page is the web UI"}},vbe=g.object({running:g.boolean().describe("The web UI server answers"),port:g.number().nullable().describe("Its port on 127.0.0.1, when it runs"),address:g.string().nullable().describe("Its address (without the login token), when it runs")}),xbe=g.object({message:g.string().describe("What happened")}),Pz=Qe({id:"web.status",title:"Web UI",summary:"whether the web UI server runs, and its address",input:g.object({}),output:vbe,surfaces:gk,text:e=>e.running&&e.port?"The web UI is running at http://127.0.0.1:"+e.port+"/ (sandbox web open signs you in).":"The web UI is not running (sandbox web start).",cli:{name:"web",action:"status",json:!0}}),vk=we({id:"web.start",title:"Start the web UI",summary:"start the web UI server in the background (this PC only)",input:g.object({}),output:g.object({port:g.number().describe("Its port"),message:g.string().describe("What happened")}),surfaces:gk,text:e=>e.message,cli:{name:"web",action:"start"}}),xk=we({id:"web.open",title:"Open the web UI",summary:"start the web UI if needed and open it in your browser, signed in",input:g.object({}),output:xbe,surfaces:gk,text:e=>e.message,cli:{name:"web",action:"open"}}),bk=we({id:"web.stop",title:"Stop the web UI",summary:"stop the web UI server",input:g.object({}),output:g.object({stopped:g.boolean().describe("It was running and stopped"),message:g.string().describe("What happened")}),surfaces:gk,text:e=>e.message,cli:{name:"web",action:"stop"}});function bbe(e){let t=ve(xk,{},{key:"o",verb:"open"}),r=ve(vk,{},{key:"s",verb:"start"}),n=ve(bk,{},{key:"x",verb:"stop"});return{title:"Web UI",sections:[{id:"web",facts:[{id:"running",label:"Running",value:e.running?"yes":"no",tone:e.running?"ok":"muted"},...e.address?[{id:"address",label:"Address",value:e.address+" (this PC only)"}]:[]],actions:e.running?[t,n]:[r,t],notes:[{text:"Loopback only, behind a token. Open signs you in.",tone:"muted"}]}]}}var gQ=mt({id:"web",title:"Web UI",summary:"The web UI server on this PC: start, open, stop.",queries:{status:Pz},operations:[vk,xk,bk],build:({status:e})=>bbe(e)});var vQ=[Q1,tk,rk,ok,uk,az,O1,z1,N1,es,Z1,tb,rb,Jx,F1,L1,M1,B1,q1,K1,X1,ik,ib,ak,sk,lk,ck,Bu,ob,nb,Y1,J1,ek,ts,ab,j1,U1,rh,V1,H1,vk,xk,bk,fk,mk,pk,hk],xQ=[bz,wz,iz,fz,FX,th,lz,mz,yz,vz,xz,cz,dz,_z,Iz,Cz,pz,VX,hz,Pz,Sz,kz],ybe=[oQ,uQ,AX,MX,OX,BX,aQ,XX,eQ,zX,cQ,hQ,HX,gQ,fQ,mQ],pvt=[...vQ,...xQ];function yk(e){return vQ.find(t=>t.id===e)}function ju(e){return xQ.find(t=>t.id===e)}function Dz(e){return ybe.find(t=>t.id===e)}function Uu(e){return e.flatMap(t=>[t,...Uu(t.children??[])])}function bQ(e){return Uu(e).flatMap(t=>[...t.view?[t.view]:[],...(t.embeds??[]).map(r=>r.view)])}function yQ(e,t){return Uu(e).find(r=>r.embeds?.some(n=>n.view===t))}var nl=[{label:"Agents",view:"agents"},{label:"Sessions",view:"sessions"},{label:"Sandbox",children:[{label:"Status",view:"sandbox"},{label:"Projects",view:"projects"}]},{label:"Apps",view:"apps"},{label:"Settings",view:"settings",embeds:[{label:"VPN",view:"vpn"},{label:"GitHub",view:"github"}]},{label:"Accounts",view:"accounts"},{label:"Maintenance",children:[{label:"Updates",view:"updates"},{label:"Data",view:"data"},{label:"Storage",view:"storage"},{label:"Health",view:"health"},{label:"Logs",view:"logs"},{label:"Engine",view:"engine"},{label:"More",elsewhere:"The less common commands are in the menu (sandbox > Maintenance)."}]}];var _k=y(O(),1),Ik={Agents:IE,Sessions:Ys,Sandbox:dp,Apps:eE,Settings:wE,Accounts:RE,Maintenance:DE},_be={project:Hv,folder:Zv,database:lE,session:Ys},Ibe={claude:"orange",codex:"dark",gemini:"blue",opencode:"gray",qwen:"violet",crush:"pink",copilot:"teal",kilo:"yellow",amp:"red",cline:"cyan",continue:"grape",kimi:"indigo",aider:"green",deepseek:"blue",reasonix:"lime"};function Vu({name:e,size:t=36}){let r=_be[e];return r?(0,_k.jsx)(Gd,{size:t,radius:"md",color:"indigo",variant:"light",children:(0,_k.jsx)(r,{size:t*.55,stroke:1.6})}):(0,_k.jsx)(Gd,{size:t,radius:"md",color:Ibe[e]??"indigo",variant:"filled",fw:700,children:e.slice(0,1).toUpperCase()})}var wk=y(R(),1),wbe="sessions";function Sbe(e,t=""){let r=/^\/s\/([A-Za-z0-9_-]{1,64})\/?$/.exec(e);if(r)return{page:"terminal",id:r[1]};let n=/^\/v\/([a-z][a-z0-9-]{0,40})\/?$/.exec(e),o=Object.fromEntries(new URLSearchParams(t));return{page:"view",view:n?n[1]:wbe,params:o}}function To(e,t={}){let r=new URLSearchParams(t).toString();return"/v/"+e+(r?"?"+r:"")}function Yr(e){history.pushState(null,"",e),window.dispatchEvent(new PopStateEvent("popstate"))}function _Q(){let[e,t]=(0,wk.useState)({path:location.pathname,search:location.search});return(0,wk.useEffect)(()=>{let r=()=>t({path:location.pathname,search:location.search});return window.addEventListener("popstate",r),()=>window.removeEventListener("popstate",r)},[]),Sbe(e.path,e.search)}var os=y(R(),1);var Ck=y(R(),1);var rs=y(R(),1);var vt=y(O(),1),kbe={ask:()=>Promise.resolve(void 0)};function IQ(e,t){if(!t||!/^[A-Za-z]:\\|^\\\\/.test(t))return;if(e==="dir")return t;let r=t.lastIndexOf("\\");return r>2?t.slice(0,r):t.slice(0,3)}function Cbe({kind:e,initial:t,done:r}){let n=kk(),[o,i]=(0,rs.useState)(null),[a,s]=(0,rs.useState)(IQ(e,t)??""),[l,c]=(0,rs.useState)(e==="file"?t??null:null),[d,u]=(0,rs.useState)(null),[f,m]=(0,rs.useState)(!1),p=(0,rs.useCallback)(v=>{m(!0),u(null),Pbe(n,v,e==="file").then(x=>{i(x),s(x.path),c(null),m(!1)},x=>{u(x instanceof Error?x.message:"That folder cannot be opened."),m(!1)})},[n,e]);(0,rs.useEffect)(()=>p(IQ(e,t)),[p,e,t]);let h=e==="dir"?l??o?.path??"":l??"",b=v=>{v.key==="Enter"&&(v.preventDefault(),p(a.trim()||void 0))};return(0,vt.jsxs)(Ue,{gap:"sm",children:[(0,vt.jsxs)(De,{gap:"xs",wrap:"nowrap",children:[(0,vt.jsx)(kt,{label:"Up one folder",children:(0,vt.jsx)(bt,{size:"lg",variant:"default","aria-label":"Up one folder",disabled:!o?.path,onClick:()=>p(o?.parent??void 0),children:(0,vt.jsx)(rE,{size:16})})}),(0,vt.jsx)(er,{style:{flex:1},value:a,placeholder:"Type a path and press Enter",onChange:v=>s(v.currentTarget.value),onKeyDown:b,ff:"monospace","aria-label":"Folder",rightSection:f?(0,vt.jsx)(fo,{size:14}):null})]}),d&&(0,vt.jsx)(za,{color:"red",variant:"light",p:"xs",children:d}),(0,vt.jsxs)(De,{align:"stretch",gap:"sm",wrap:"nowrap",children:[(0,vt.jsxs)(Ue,{gap:2,w:170,visibleFrom:"xs",style:{flex:"none"},children:[(0,vt.jsx)(ye,{size:"xs",fw:650,c:"dimmed",tt:"uppercase",px:6,pb:4,children:"Places"}),(o?.places??[]).map(v=>(0,vt.jsxs)("div",{className:"picker-entry",onClick:()=>p(v.path),title:v.path,children:[v.label.endsWith(":")?(0,vt.jsx)(Vv,{size:16}):(0,vt.jsx)(Hv,{size:16}),(0,vt.jsx)(ye,{size:"sm",truncate:!0,children:v.label})]},v.path))]}),(0,vt.jsxs)(Ar,{className:"picker-list",style:{flex:1},type:"auto",children:[!o?.path&&(0,vt.jsx)(ye,{size:"sm",c:"dimmed",p:"md",children:"Pick a place on the left, or type a path."}),o?.path&&o.entries.length===0&&(0,vt.jsx)(ye,{size:"sm",c:"dimmed",p:"md",children:e==="dir"?"No folders in here.":"Nothing in here."}),o?.entries.map(v=>(0,vt.jsxs)("div",{className:"picker-entry","data-selected":l===v.path,onClick:()=>c(v.path),onDoubleClick:()=>v.kind==="dir"?p(v.path):r(v.path),children:[v.kind==="dir"?(0,vt.jsx)(Zv,{size:16,color:"var(--mantine-color-yellow-6)"}):(0,vt.jsx)(mE,{size:16,color:"var(--app-muted)"}),(0,vt.jsx)(ye,{size:"sm",truncate:!0,children:v.name})]},v.path)),o?.truncated&&(0,vt.jsx)(ye,{size:"xs",c:"dimmed",p:"sm",children:"Only the first entries are shown: type the rest of the path."})]})]}),(0,vt.jsxs)(De,{justify:"space-between",wrap:"nowrap",gap:"sm",children:[(0,vt.jsx)(ye,{size:"xs",c:"dimmed",ff:"monospace",truncate:!0,style:{minWidth:0},children:h||(e==="file"?"Pick a file":"Pick a folder")}),(0,vt.jsxs)(De,{gap:"xs",wrap:"nowrap",style:{flex:"none"},children:[(0,vt.jsx)(ht,{variant:"default",onClick:()=>r(void 0),children:"Cancel"}),(0,vt.jsx)(ht,{disabled:!h,onClick:()=>r(h),children:e==="dir"?"Choose this folder":"Choose"})]})]})]})}function Pbe(e,t,r){return e.call("files.browse",{path:t,files:r},kbe)}function Sk(e,t,r){return new Promise(n=>{let o=!1,i=s=>{o||(o=!0,Ua.close(a),n(s))},a=Ua.open({title:r??(e==="dir"?"Choose a folder":"Choose a file"),size:"xl",onClose:()=>i(void 0),children:(0,vt.jsx)(Cbe,{kind:e,initial:t,done:i})})})}var rr=y(O(),1);function wQ(e){return e.kind==="confirm"?!1:void 0}function Dbe({question:e,said:t,done:r}){let[n,o]=(0,Ck.useState)(e.kind==="text"||e.kind==="path"?e.initial??"":""),[i,a]=(0,Ck.useState)(e.kind==="choose"?e.initial??e.choices[0]?.value??"":""),[s,l]=(0,Ck.useState)(e.kind==="checklist"?[...e.checked??[]]:[]),c=[...t,...e.lines??[]],d="problem"in e?e.problem:void 0,u=e.kind!=="confirm"||!e.typed||n.trim().toLowerCase()===e.typed;return(0,rr.jsx)("form",{onSubmit:m=>{m.preventDefault(),e.kind==="confirm"?r(u):e.kind==="choose"?r(i):e.kind==="checklist"?r(s):r(n===""&&e.kind==="text"&&!e.allowEmpty?void 0:n)},children:(0,rr.jsxs)(Ue,{gap:"sm",children:[c.map((m,p)=>(0,rr.jsx)(ye,{size:"sm",children:m},p)),d&&(0,rr.jsx)(za,{color:"orange",variant:"light",p:"xs",children:d}),e.kind==="confirm"&&e.typed&&(0,rr.jsx)(er,{label:'Type "'+e.typed+'" to confirm',value:n,onChange:m=>o(m.currentTarget.value),"data-autofocus":!0}),e.kind==="choose"&&(0,rr.jsx)(Vs.Group,{value:i,onChange:a,children:(0,rr.jsx)(Ue,{gap:"xs",children:e.choices.map(m=>(0,rr.jsx)(Vs,{value:m.value,label:m.label,description:m.description},m.value))})}),e.kind==="checklist"&&(0,rr.jsx)(Fs.Group,{value:s,onChange:l,children:(0,rr.jsx)(Ue,{gap:"xs",children:e.choices.map(m=>(0,rr.jsx)(Fs,{value:m.value,label:m.label,description:m.description},m.value))})}),e.kind==="text"&&(0,rr.jsx)(er,{value:n,onChange:m=>o(m.currentTarget.value),"data-autofocus":!0}),e.kind==="path"&&(0,rr.jsxs)(De,{gap:"xs",wrap:"nowrap",align:"flex-end",children:[(0,rr.jsx)(er,{style:{flex:1},ff:"monospace",value:n,onChange:m=>o(m.currentTarget.value),description:"The full path of a "+(e.pathKind==="file"?"file":"folder")+" on this PC","data-autofocus":!0}),(0,rr.jsx)(kt,{label:e.pathKind==="file"?"Choose a file":"Choose a folder",children:(0,rr.jsx)(bt,{variant:"default",size:36,"aria-label":"Browse",onClick:()=>{Sk(e.pathKind,n||void 0).then(m=>m&&o(m))},children:(0,rr.jsx)(Wv,{size:18,stroke:1.6})})})]}),e.kind==="secret"&&(0,rr.jsx)(Yd,{value:n,onChange:m=>o(m.currentTarget.value),autoComplete:"new-password","data-autofocus":!0}),(0,rr.jsxs)(De,{justify:"flex-end",children:[(0,rr.jsx)(ht,{variant:"default",onClick:()=>r(wQ(e)),children:"Cancel"}),(0,rr.jsx)(ht,{type:"submit",color:e.kind==="confirm"&&e.danger?"red":void 0,disabled:!u,children:e.kind==="confirm"?e.yes??"Yes":"OK"})]})]})})}function SQ(e,t=[]){return new Promise(r=>{let n=!1,o=a=>{n||(n=!0,Ua.close(i),r(a))},i=Ua.open({title:e.title,centered:!0,onClose:()=>o(wQ(e)),children:(0,rr.jsx)(Dbe,{question:e,said:t,done:o})})})}var PQ=y(O(),1),kQ=(0,os.createContext)(null);function CQ({client:e,children:t}){return(0,PQ.jsx)(kQ.Provider,{value:e,children:t})}function kk(){let e=(0,os.useContext)(kQ);if(!e)throw new Error("useRpc needs an RpcProvider");return e}var Rbe={ask:()=>Promise.resolve(void 0)};function il(e,t){let r=kk(),n=Wm(),o=e.live!==void 0,i=JSON.stringify(t??{}),[a,s]=(0,os.useState)(null),l=QR({queryKey:[e.id,i],queryFn:()=>r.call(e.id,t,Rbe),enabled:!o});(0,os.useEffect)(()=>{if(o)return r.subscribe(e.id,JSON.parse(i),d=>{s(null),n.setQueryData([e.id,i],d)},s)},[r,n,e.id,i,o]);let c=a??l.error;return{data:l.data,error:c,pending:l.data===void 0&&!c}}function Pk(){let e=kk(),t=Wm();return(0,os.useCallback)(async(r,n={},o={})=>{let i=yk(r.op),a="op-"+Date.now(),s=[],l=!1;try{let c=await e.call(r.op,{...r.input,...n},{progress:f=>{let m={id:a,title:r.label,message:f,loading:!0,autoClose:!1};l?Qo.update(m):Qo.show(m),l=!0},log:f=>s.push(f),ask:f=>SQ(f,s.splice(0))}),d=i?.text?.(c)??"Done.",u={id:a,title:o.inline?void 0:r.label,message:d,color:"green",loading:!1,autoClose:o.inline?2500:5e3};return l?Qo.update(u):Qo.show(u),{ok:!0,text:d,output:c}}catch(c){if(l&&Qo.hide(a),c instanceof Qx)return{ok:!1,cancelled:!0,message:"Cancelled."};let d=c instanceof Error?c.message:"It did not work.",u=c.fix??(s.length?s.join(" "):void 0);return o.inline||Qo.show({title:d,message:u,color:"red",autoClose:1e4}),{ok:!1,message:d,fix:u}}finally{await t.invalidateQueries()}},[e,t])}var Ebe=2,Tbe=1,DQ=class{activate(e){this._terminal=e}dispose(){}fit(){let e=this.proposeDimensions();if(!e||!this._terminal||isNaN(e.cols)||isNaN(e.rows))return;let t=this._terminal._core;(this._terminal.rows!==e.rows||this._terminal.cols!==e.cols)&&(t._renderService.clear(),this._terminal.resize(e.cols,e.rows))}proposeDimensions(){if(!this._terminal||!this._terminal.element||!this._terminal.element.parentElement)return;let e=this._terminal._core._renderService.dimensions;if(e.css.cell.width===0||e.css.cell.height===0)return;let t=this._terminal.options.scrollback===0?0:this._terminal.options.overviewRuler?.width||14,r=window.getComputedStyle(this._terminal.element.parentElement),n=parseInt(r.getPropertyValue("height")),o=Math.max(0,parseInt(r.getPropertyValue("width"))),i=window.getComputedStyle(this._terminal.element),a={top:parseInt(i.getPropertyValue("padding-top")),bottom:parseInt(i.getPropertyValue("padding-bottom")),right:parseInt(i.getPropertyValue("padding-right")),left:parseInt(i.getPropertyValue("padding-left"))},s=a.top+a.bottom,l=a.right+a.left,c=n-s,d=o-l-t;return{cols:Math.max(Ebe,Math.floor(d/e.css.cell.width)),rows:Math.max(Tbe,Math.floor(c/e.css.cell.height))}}};var $be=class{constructor(){this.listeners=[],this.unexpectedErrorHandler=function(e){setTimeout(()=>{throw e.stack?RQ.isErrorNoTelemetry(e)?new RQ(e.message+`
130
130
 
131
131
  `+e.stack):new Error(e.message+`
132
132
 
@@ -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.11.6.0")]
12
- [assembly: AssemblyFileVersion("1.11.6.0")]
13
- [assembly: AssemblyInformationalVersion("1.11.6")]
11
+ [assembly: AssemblyVersion("1.11.7.0")]
12
+ [assembly: AssemblyFileVersion("1.11.7.0")]
13
+ [assembly: AssemblyInformationalVersion("1.11.7")]
14
14
  [assembly: ComVisible(false)]
@@ -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.11.6.0")]
12
- [assembly: AssemblyFileVersion("1.11.6.0")]
13
- [assembly: AssemblyInformationalVersion("1.11.6")]
11
+ [assembly: AssemblyVersion("1.11.7.0")]
12
+ [assembly: AssemblyFileVersion("1.11.7.0")]
13
+ [assembly: AssemblyInformationalVersion("1.11.7")]
14
14
  [assembly: ComVisible(false)]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "windows-development-sandbox",
3
- "version": "1.11.6",
3
+ "version": "1.11.7",
4
4
  "description": "A disposable Windows Sandbox for development, with or without an AI agent (Claude Code, Codex, Gemini, opencode, ...): the folder you run it from is the workspace, toolchains and databases for your project install themselves, and nothing else on your PC is reachable.",
5
5
  "type": "module",
6
6
  "license": "MIT",