windows-development-sandbox 1.15.0 → 1.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,8 +2,8 @@
2
2
  import { createRequire as __createRequire } from 'node:module'; const require = __createRequire(import.meta.url);
3
3
  import {
4
4
  run
5
- } from "./chunk-HTQT5SBY.js";
6
- import "./chunk-D7CM63BS.js";
5
+ } from "./chunk-7EXDUNE7.js";
6
+ import "./chunk-HIC4CUMY.js";
7
7
 
8
8
  // src/bin/claude-sbx.ts
9
9
  run(process.argv.slice(2), { harness: "claude" });
@@ -2,8 +2,8 @@
2
2
  import { createRequire as __createRequire } from 'node:module'; const require = __createRequire(import.meta.url);
3
3
  import {
4
4
  run
5
- } from "./chunk-HTQT5SBY.js";
6
- import "./chunk-D7CM63BS.js";
5
+ } from "./chunk-7EXDUNE7.js";
6
+ import "./chunk-HIC4CUMY.js";
7
7
 
8
8
  // src/bin/codex-sbx.ts
9
9
  run(process.argv.slice(2), { harness: "codex" });
@@ -2,8 +2,8 @@
2
2
  import { createRequire as __createRequire } from 'node:module'; const require = __createRequire(import.meta.url);
3
3
  import {
4
4
  run
5
- } from "./chunk-HTQT5SBY.js";
6
- import "./chunk-D7CM63BS.js";
5
+ } from "./chunk-7EXDUNE7.js";
6
+ import "./chunk-HIC4CUMY.js";
7
7
 
8
8
  // src/bin/gemini-sbx.ts
9
9
  run(process.argv.slice(2), { harness: "gemini" });
@@ -2,8 +2,8 @@
2
2
  import { createRequire as __createRequire } from 'node:module'; const require = __createRequire(import.meta.url);
3
3
  import {
4
4
  run
5
- } from "./chunk-HTQT5SBY.js";
6
- import "./chunk-D7CM63BS.js";
5
+ } from "./chunk-7EXDUNE7.js";
6
+ import "./chunk-HIC4CUMY.js";
7
7
 
8
8
  // src/bin/sandbox.ts
9
9
  run(process.argv.slice(2));
@@ -2,8 +2,8 @@
2
2
  import { createRequire as __createRequire } from 'node:module'; const require = __createRequire(import.meta.url);
3
3
  import {
4
4
  run
5
- } from "./chunk-HTQT5SBY.js";
6
- import "./chunk-D7CM63BS.js";
5
+ } from "./chunk-7EXDUNE7.js";
6
+ import "./chunk-HIC4CUMY.js";
7
7
 
8
8
  // src/bin/sbx-hide.ts
9
9
  run(process.argv.slice(2), { command: "hide" });
@@ -2,8 +2,8 @@
2
2
  import { createRequire as __createRequire } from 'node:module'; const require = __createRequire(import.meta.url);
3
3
  import {
4
4
  run
5
- } from "./chunk-HTQT5SBY.js";
6
- import "./chunk-D7CM63BS.js";
5
+ } from "./chunk-7EXDUNE7.js";
6
+ import "./chunk-HIC4CUMY.js";
7
7
 
8
8
  // src/bin/sbx-show.ts
9
9
  run(process.argv.slice(2), { command: "show" });
package/dist/cli/index.js CHANGED
@@ -12,7 +12,7 @@ import { takeHiddenInstallNotice } from '../host/install/index.js';
12
12
  import { c } from './output/index.js';
13
13
  import { dropInheritedModulePath } from '../shared/system.js';
14
14
  import { withConfig, hoistConfig, withoutConfig } from './argv.js';
15
- import { report } from './report.js';
15
+ import { recordUnexpected, report } from './report.js';
16
16
  import { versionText } from './version.js';
17
17
  import { HANDLERS } from './commands/index.js';
18
18
  import { definitionHandler, operationHandler } from './operations/index.js';
@@ -121,6 +121,9 @@ export async function main(rawArgv, preset = {}) {
121
121
  }
122
122
  export function run(argv, preset) {
123
123
  dropInheritedModulePath();
124
+ // Background work that throws must not end the menu or the web server: the error is kept with its stack instead.
125
+ process.on('unhandledRejection', (e) => recordUnexpected(e, 'unhandled rejection'));
126
+ process.on('uncaughtException', (e) => recordUnexpected(e, 'uncaught exception'));
124
127
  main(argv, preset).then((code) => {
125
128
  process.exitCode = code;
126
129
  }, (e) => {
@@ -1,2 +1,9 @@
1
+ /** Where errors nobody caught are written in full (this user's temp folder; kept under 1 MB). */
2
+ export declare const UNEXPECTED_LOG: string;
3
+ /**
4
+ * An error thrown by background work (a timer, a promise nobody awaited): written in full to UNEXPECTED_LOG and named
5
+ * in one line, instead of ending the menu, the web server or a command half-way.
6
+ */
7
+ export declare function recordUnexpected(e: unknown, kind: string): void;
1
8
  /** Prints a problem the way a user can act on it: what happened, how to fix it; stack traces only for real bugs. */
2
9
  export declare function report(e: unknown): number;
@@ -1,6 +1,37 @@
1
+ import fs from 'node:fs';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
1
4
  import { CommanderError } from 'commander';
2
5
  import { c } from './output/index.js';
3
6
  import { UserError, errMsg, plainText } from '../core/util/index.js';
7
+ /** Where errors nobody caught are written in full (this user's temp folder; kept under 1 MB). */
8
+ export const UNEXPECTED_LOG = path.join(os.tmpdir(), 'windows-development-sandbox-errors.log');
9
+ const UNEXPECTED_LOG_MAX = 1024 * 1024;
10
+ /**
11
+ * An error thrown by background work (a timer, a promise nobody awaited): written in full to UNEXPECTED_LOG and named
12
+ * in one line, instead of ending the menu, the web server or a command half-way.
13
+ */
14
+ export function recordUnexpected(e, kind) {
15
+ const text = new Date().toISOString() +
16
+ ' ' +
17
+ kind +
18
+ ' (pid ' +
19
+ process.pid +
20
+ ', ' +
21
+ process.argv.slice(2).join(' ') +
22
+ ')\n' +
23
+ (e instanceof Error && e.stack ? e.stack : errMsg(e)) +
24
+ '\n\n';
25
+ try {
26
+ if (fs.existsSync(UNEXPECTED_LOG) && fs.statSync(UNEXPECTED_LOG).size > UNEXPECTED_LOG_MAX)
27
+ fs.rmSync(UNEXPECTED_LOG, { force: true });
28
+ fs.appendFileSync(UNEXPECTED_LOG, text);
29
+ }
30
+ catch {
31
+ // Nowhere to write it: the line below still says what happened.
32
+ }
33
+ console.error(c.red('Unexpected error (kept running): ') + plainText(errMsg(e)) + c.dim(' Details: ' + UNEXPECTED_LOG));
34
+ }
4
35
  /** Prints a problem the way a user can act on it: what happened, how to fix it; stack traces only for real bugs. */
5
36
  export function report(e) {
6
37
  if (e instanceof CommanderError)
@@ -2,6 +2,9 @@
2
2
  import fs from 'node:fs';
3
3
  import path from 'node:path';
4
4
  import { AccountStore } from '../host/profile/index.js';
5
+ import { DEFAULTS } from '../core/config/index.js';
6
+ import { defaultAccount } from '../app/features/accounts/index.js';
7
+ import { MenuOperations } from '../cli/menu/io-menu.js';
5
8
  export const accountsSuite = {
6
9
  name: 'accounts',
7
10
  title: 'Claude accounts (save, switch, remove, safe names)',
@@ -32,6 +35,25 @@ export const accountsSuite = {
32
35
  stateAfter.other === 1 &&
33
36
  new AccountStore(storage).activeFor(dataA) === 'work', 'switching swaps in the other credentials and account, keeps the rest of the sandbox state, and marks it active');
34
37
  t.includes(JSON.stringify(new AccountStore(storage).read('personal')?.credentials), 'personal-2', "the outgoing account's refreshed tokens are saved back before switching");
38
+ t.section('make default, as the menu runs it');
39
+ const menuCtx = {
40
+ packageRoot: t.dir('acct-menu', 'package'),
41
+ options: {},
42
+ loaded: { config: structuredClone(DEFAULTS), configPath: null, base: t.temp },
43
+ storageRoot: storage,
44
+ dataPath: dataA,
45
+ };
46
+ let said;
47
+ let error = null;
48
+ try {
49
+ said = await new MenuOperations({}, menuCtx).run(defaultAccount, { agent: 'claude', name: 'personal', noRestart: false }, []);
50
+ }
51
+ catch (e) {
52
+ error = e;
53
+ }
54
+ t.check(error === null &&
55
+ new AccountStore(storage).defaultName() === 'personal' &&
56
+ new AccountStore(storage).activeFor(dataA) === 'personal', 'make default from the menu sets the default and switches the sandbox to it', String(error instanceof Error ? error.stack : (error ?? said)));
35
57
  t.section('sharing plugins between data sets');
36
58
  const plugA = path.join(storage, 'data', 'p1', 'state', 'claude');
37
59
  const plugB = path.join(storage, 'data', 'p2', 'state', 'claude');
@@ -319,9 +319,9 @@ export const appSuite = {
319
319
  !sessionLines.some((l) => l.includes('--wide')), 'sessions on the command line: the table with the folder column (--wide), the marker and its note', sessionLines.join('\n'));
320
320
  const statusLines = pageLines(statusPage);
321
321
  t.check(statusLines.some((l) => /^State\s+\S/.test(l)) &&
322
- statusLines.some((l) => /^Helper\s+responding, 2 sessions running$/.test(l)) &&
322
+ statusLines.some((l) => /^Helper\s+Responding$/.test(l)) &&
323
323
  statusLines.includes('* api C:\\Projects\\api -> C:\\Projects\\api (started from here)') &&
324
- statusLines.some((l) => l.startsWith('Repo guard 1 folder(s) guarded')), 'status on the command line: facts as rows, projects with their details, the repo guard', statusLines.join('\n'));
324
+ statusLines.some((l) => l.startsWith('Repo guard Watching 1 folder.')), 'status on the command line: facts as rows, projects with their details, the repo guard', statusLines.join('\n'));
325
325
  t.equal(pageLines(sessionsPage({ ...list, sessions: [] })).at(-1), 'No sessions. Start one with: sandbox claude', 'an empty page prints its command-line text');
326
326
  const menu = menuView(sessions);
327
327
  const chosen = menu.resolve({ kind: 'pick', value: 'new' });
@@ -5,8 +5,8 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1" />
6
6
  <title>Sandbox</title>
7
7
  <link rel="icon" href="data:," />
8
- <link rel="stylesheet" href="/main-UAQPI5XD.css" />
9
- <script type="module" src="/main-7DJZ2M27.js"></script>
8
+ <link rel="stylesheet" href="/main-XT7T6ODM.css" />
9
+ <script type="module" src="/main-JTH3FT4O.js"></script>
10
10
  </head>
11
11
  <body>
12
12
  <div id="root"></div>