windows-development-sandbox 1.27.1 → 1.27.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/dist/app/context.d.ts +5 -0
  2. package/dist/app/context.js +13 -0
  3. package/dist/app/features/accounts/accounts.def.d.ts +16 -0
  4. package/dist/app/features/accounts/accounts.run.d.ts +4 -0
  5. package/dist/app/features/accounts/services.def.d.ts +15 -1
  6. package/dist/app/features/accounts/services.def.js +67 -31
  7. package/dist/app/features/accounts/services.run.d.ts +7 -2
  8. package/dist/app/features/accounts/services.run.js +25 -6
  9. package/dist/app/features/defs.d.ts +17 -0
  10. package/dist/app/registry.d.ts +1 -1
  11. package/dist/app/registry.js +15 -2
  12. package/dist/bin/agent-sandbox.js +2 -2
  13. package/dist/bin/{chunk-QUHNXHVG.js → chunk-4M5SZZ5P.js} +46 -41
  14. package/dist/bin/{chunk-4TLSWPG7.js → chunk-FA5VWLP4.js} +1 -1
  15. package/dist/bin/{chunk-RSIX7C6D.js → chunk-RU7XRN62.js} +249 -101
  16. package/dist/bin/claude-host.js +2 -2
  17. package/dist/bin/claude-sbx.js +2 -2
  18. package/dist/bin/codex-sbx.js +2 -2
  19. package/dist/bin/gemini-sbx.js +2 -2
  20. package/dist/bin/sandbox.js +2 -2
  21. package/dist/bin/sbx-hide.js +2 -2
  22. package/dist/bin/sbx-show.js +2 -2
  23. package/dist/cli/menu/menu.js +6 -1
  24. package/dist/cli/menu/pages/accounts-page.js +9 -5
  25. package/dist/guest/apps/app-installer.js +7 -5
  26. package/dist/guest/apps/comet-unpack.js +2 -2
  27. package/dist/guest/runtime/pools.d.ts +4 -4
  28. package/dist/guest/runtime/pools.js +4 -15
  29. package/dist/guest/runtime/process.d.ts +5 -2
  30. package/dist/guest/runtime/process.js +23 -3
  31. package/dist/guest/toolchains/engines/epic-launcher.d.ts +6 -0
  32. package/dist/guest/toolchains/engines/epic-launcher.js +19 -1
  33. package/dist/guest/toolchains/engines/unreal-engines.js +2 -1
  34. package/dist/guest/toolchains/installers.d.ts +3 -3
  35. package/dist/guest/toolchains/installers.js +5 -5
  36. package/dist/guest/toolchains/native/vcruntime.js +2 -2
  37. package/dist/guest/toolchains/native/vs-persist.d.ts +2 -2
  38. package/dist/guest/toolchains/signature.d.ts +2 -0
  39. package/dist/guest/toolchains/signature.js +9 -2
  40. package/dist/guest/toolchains/step-scheduler.js +1 -1
  41. package/dist/host/accounts/deliver.js +15 -7
  42. package/dist/host/pc-installs/discover.js +5 -2
  43. package/dist/host/pc-installs/unreal.d.ts +3 -1
  44. package/dist/host/pc-installs/unreal.js +27 -1
  45. package/dist/runtime/agent.js +34 -22
  46. package/dist/runtime/daemon.js +96 -51
  47. package/dist/runtime/db.js +32 -16
  48. package/dist/runtime/main.js +98 -53
  49. package/dist/runtime/netproxy.js +26 -12
  50. package/dist/runtime/tool.js +32 -15
  51. package/dist/runtime/view.js +26 -12
  52. package/dist/selftest/app.test.js +20 -2
  53. package/dist/selftest/boot-runner.test.js +15 -1
  54. package/dist/selftest/engines.test.js +22 -1
  55. package/dist/selftest/menu-screens.js +52 -1
  56. package/dist/selftest/menu.test.js +29 -3
  57. package/dist/selftest/service-accounts.test.js +77 -2
  58. package/dist/selftest/ui-text.test.js +4 -0
  59. package/dist/shared/accounts/kinds.js +9 -9
  60. package/dist/shared/apps/catalogue-gamedev.js +1 -0
  61. package/dist/shared/apps/catalogue.d.ts +2 -0
  62. package/dist/shared/concurrency/pools.js +1 -1
  63. package/dist/shared/download/archive.js +4 -2
  64. package/dist/shared/download/index.d.ts +1 -1
  65. package/dist/shared/download/index.js +1 -1
  66. package/dist/shared/download/spawn.d.ts +7 -1
  67. package/dist/shared/download/spawn.js +16 -1
  68. package/dist/shared/paths/pc-installs.d.ts +8 -0
  69. package/dist/shared/unreal/launcher-files.d.ts +7 -0
  70. package/dist/shared/unreal/launcher-files.js +32 -1
  71. package/dist/web/index.html +1 -1
  72. package/dist/web/{main-VPMYTSUL.js → main-NKM7ECSB.js} +59 -59
  73. package/native/csharp/HcsHost/AssemblyInfo.cs +3 -3
  74. package/native/csharp/HostTools/AssemblyInfo.cs +3 -3
  75. package/native/csharp/StorageHelper/AssemblyInfo.cs +3 -3
  76. package/package.json +1 -1
  77. package/schemas/config.schema.json +9 -9
  78. package/schemas/sandbox-config.schema.json +9 -9
@@ -4,5 +4,10 @@ import { type OperationIO } from './kernel/index.js';
4
4
  export type AppContext = Context;
5
5
  /** Attaches a run function to a definition: `implement(def, async (ctx, input, io) => output)`. */
6
6
  export declare const implement: <D extends import("./kernel/define.js").AnyDefinition>(def: D, run: (ctx: Context, input: import("zod").infer<D["input"]>, io: OperationIO) => import("zod").infer<D["output"]> | Promise<import("zod").infer<D["output"]>>) => import("./kernel/define.js").Implementation<D, Context>;
7
+ /**
8
+ * Reads the settings again after an operation, so a long-lived context (the menu, the web server) shows what it saved.
9
+ * A file that cannot be read keeps the settings the context had: the settings page reports the problem.
10
+ */
11
+ export declare function reloadSettings(ctx: AppContext): void;
7
12
  /** Another user's sandbox asks for their name to be typed: through `io`, so every surface can ask it. */
8
13
  export declare function typedConfirm(io: OperationIO): TypedConfirm;
@@ -1,6 +1,19 @@
1
+ import { loadConfig } from '../core/config/index.js';
1
2
  import { implementer } from './kernel/index.js';
2
3
  /** Attaches a run function to a definition: `implement(def, async (ctx, input, io) => output)`. */
3
4
  export const implement = implementer();
5
+ /**
6
+ * Reads the settings again after an operation, so a long-lived context (the menu, the web server) shows what it saved.
7
+ * A file that cannot be read keeps the settings the context had: the settings page reports the problem.
8
+ */
9
+ export function reloadSettings(ctx) {
10
+ try {
11
+ ctx.loaded = loadConfig(ctx.loaded.configPath ?? ctx.options.config);
12
+ }
13
+ catch {
14
+ // Broken settings file: keep what is loaded.
15
+ }
16
+ }
4
17
  /** Another user's sandbox asks for their name to be typed: through `io`, so every surface can ask it. */
5
18
  export function typedConfirm(io) {
6
19
  return (prompt, expected) => io.confirm({
@@ -76,6 +76,10 @@ export declare const AccountsListSchema: z.ZodObject<{
76
76
  canImport: z.ZodBoolean;
77
77
  canLogin: z.ZodBoolean;
78
78
  needsFile: z.ZodBoolean;
79
+ fromPc: z.ZodBoolean;
80
+ hasFileSource: z.ZodBoolean;
81
+ onPc: z.ZodNullable<z.ZodBoolean>;
82
+ sandboxOnly: z.ZodBoolean;
79
83
  ready: z.ZodBoolean;
80
84
  }, z.core.$strip>>;
81
85
  }, z.core.$strip>;
@@ -125,6 +129,10 @@ export declare const listAccounts: import("../../kernel/define.js").QueryDef<z.Z
125
129
  canImport: z.ZodBoolean;
126
130
  canLogin: z.ZodBoolean;
127
131
  needsFile: z.ZodBoolean;
132
+ fromPc: z.ZodBoolean;
133
+ hasFileSource: z.ZodBoolean;
134
+ onPc: z.ZodNullable<z.ZodBoolean>;
135
+ sandboxOnly: z.ZodBoolean;
128
136
  ready: z.ZodBoolean;
129
137
  }, z.core.$strip>>;
130
138
  }, z.core.$strip>, "accounts.list">;
@@ -215,6 +223,10 @@ export declare const accountsView: import("../../kernel/define.js").ViewDef<{
215
223
  canImport: z.ZodBoolean;
216
224
  canLogin: z.ZodBoolean;
217
225
  needsFile: z.ZodBoolean;
226
+ fromPc: z.ZodBoolean;
227
+ hasFileSource: z.ZodBoolean;
228
+ onPc: z.ZodNullable<z.ZodBoolean>;
229
+ sandboxOnly: z.ZodBoolean;
218
230
  ready: z.ZodBoolean;
219
231
  }, z.core.$strip>>;
220
232
  }, z.core.$strip>, "accounts.list">;
@@ -240,6 +252,10 @@ export declare const accountsView: import("../../kernel/define.js").ViewDef<{
240
252
  canImport: z.ZodBoolean;
241
253
  canLogin: z.ZodBoolean;
242
254
  needsFile: z.ZodBoolean;
255
+ fromPc: z.ZodBoolean;
256
+ hasFileSource: z.ZodBoolean;
257
+ onPc: z.ZodNullable<z.ZodBoolean>;
258
+ sandboxOnly: z.ZodBoolean;
243
259
  ready: z.ZodBoolean;
244
260
  }, z.core.$strip>>;
245
261
  }, z.core.$strip>, "accounts.services">;
@@ -40,6 +40,10 @@ export declare const listAccountsRun: import("../../kernel/define.js").Implement
40
40
  canImport: import("zod").ZodBoolean;
41
41
  canLogin: import("zod").ZodBoolean;
42
42
  needsFile: import("zod").ZodBoolean;
43
+ fromPc: import("zod").ZodBoolean;
44
+ hasFileSource: import("zod").ZodBoolean;
45
+ onPc: import("zod").ZodNullable<import("zod").ZodBoolean>;
46
+ sandboxOnly: import("zod").ZodBoolean;
43
47
  ready: import("zod").ZodBoolean;
44
48
  }, import("zod/v4/core").$strip>>;
45
49
  }, import("zod/v4/core").$strip>, "accounts.list">, import("../../../cli/commands/context.js").Context>;
@@ -21,6 +21,10 @@ export declare const ServiceRowSchema: z.ZodObject<{
21
21
  canImport: z.ZodBoolean;
22
22
  canLogin: z.ZodBoolean;
23
23
  needsFile: z.ZodBoolean;
24
+ fromPc: z.ZodBoolean;
25
+ hasFileSource: z.ZodBoolean;
26
+ onPc: z.ZodNullable<z.ZodBoolean>;
27
+ sandboxOnly: z.ZodBoolean;
24
28
  ready: z.ZodBoolean;
25
29
  }, z.core.$strip>;
26
30
  export type ServiceRow = z.output<typeof ServiceRowSchema>;
@@ -44,6 +48,10 @@ export declare const ServicesListSchema: z.ZodObject<{
44
48
  canImport: z.ZodBoolean;
45
49
  canLogin: z.ZodBoolean;
46
50
  needsFile: z.ZodBoolean;
51
+ fromPc: z.ZodBoolean;
52
+ hasFileSource: z.ZodBoolean;
53
+ onPc: z.ZodNullable<z.ZodBoolean>;
54
+ sandboxOnly: z.ZodBoolean;
47
55
  ready: z.ZodBoolean;
48
56
  }, z.core.$strip>>;
49
57
  }, z.core.$strip>;
@@ -72,12 +80,17 @@ export declare const listServices: import("../../kernel/define.js").QueryDef<z.Z
72
80
  canImport: z.ZodBoolean;
73
81
  canLogin: z.ZodBoolean;
74
82
  needsFile: z.ZodBoolean;
83
+ fromPc: z.ZodBoolean;
84
+ hasFileSource: z.ZodBoolean;
85
+ onPc: z.ZodNullable<z.ZodBoolean>;
86
+ sandboxOnly: z.ZodBoolean;
75
87
  ready: z.ZodBoolean;
76
88
  }, z.core.$strip>>;
77
89
  }, z.core.$strip>, "accounts.services">;
78
90
  export declare const serviceImport: import("../../kernel/define.js").OperationDef<z.ZodObject<{
79
91
  name: z.ZodOptional<z.ZodString>;
80
92
  from: z.ZodOptional<z.ZodString>;
93
+ file: z.ZodOptional<z.ZodBoolean>;
81
94
  }, z.core.$strip>, z.ZodObject<{
82
95
  message: z.ZodString;
83
96
  }, z.core.$strip>, "accounts.service.import">;
@@ -95,6 +108,7 @@ export declare const serviceLogout: import("../../kernel/define.js").OperationDe
95
108
  export declare const SERVICE_OPERATIONS: readonly [import("../../kernel/define.js").OperationDef<z.ZodObject<{
96
109
  name: z.ZodOptional<z.ZodString>;
97
110
  from: z.ZodOptional<z.ZodString>;
111
+ file: z.ZodOptional<z.ZodBoolean>;
98
112
  }, z.core.$strip>, z.ZodObject<{
99
113
  message: z.ZodString;
100
114
  }, z.core.$strip>, "accounts.service.import">, import("../../kernel/define.js").OperationDef<z.ZodObject<{
@@ -109,7 +123,7 @@ export declare const SERVICE_OPERATIONS: readonly [import("../../kernel/define.j
109
123
  }, z.core.$strip>, "accounts.service.logout">];
110
124
  /** The category id of a service in the menu and its section id on the web. */
111
125
  export declare function serviceCategory(kind: string): string;
112
- /** One service's section: its facts, its warnings and its actions. */
126
+ /** One service's section: a few short facts and its actions. The full warnings are asked before saving. */
113
127
  export declare function serviceSection(s: ServiceRow): Section;
114
128
  /** The sections of every service (appended to the accounts page on the web). */
115
129
  export declare function serviceSections(o: ServicesList): Section[];
@@ -29,12 +29,19 @@ export const ServiceRowSchema = z.object({
29
29
  canImport: z.boolean().describe('It can be taken from this PC or a file (sandbox account import)'),
30
30
  canLogin: z.boolean().describe('It can be typed (sandbox account login)'),
31
31
  needsFile: z.boolean().describe('Importing it needs --from <file>'),
32
+ fromPc: z.boolean().describe("This PC's own program can give it (the next launch takes it when sharing is on)"),
33
+ hasFileSource: z.boolean().describe('It can be read from a file you name'),
34
+ onPc: z
35
+ .boolean()
36
+ .nullable()
37
+ .describe('This PC has one to take (null: it cannot be taken from this PC). Only the presence, never the sign-in'),
38
+ sandboxOnly: z.boolean().describe('Only a sandbox can give it: sign in once inside one and it is kept'),
32
39
  ready: z.boolean().describe('Its sign-in is written; false: the menu and the web leave it out (the CLI says coming)'),
33
40
  });
34
41
  export const ServicesListSchema = z.object({ services: z.array(ServiceRowSchema) });
35
42
  const STATE_TEXT = {
36
43
  none: 'not saved',
37
- saved: 'saved (encrypted)',
44
+ saved: 'saved',
38
45
  unreadable: 'cannot be read',
39
46
  };
40
47
  const STATE_TONE = {
@@ -43,10 +50,10 @@ const STATE_TONE = {
43
50
  unreadable: 'danger',
44
51
  };
45
52
  const SOURCE_TEXT = {
46
- pc: 'taken from this PC',
53
+ pc: 'from this PC',
47
54
  file: 'from a file',
48
- typed: 'typed in',
49
- sandbox: 'signed in (or renewed) in a sandbox',
55
+ typed: 'by typing it',
56
+ sandbox: 'from a sandbox',
50
57
  };
51
58
  const day = (iso) => (iso ? iso.slice(0, 10) : '');
52
59
  /** `sandbox account status`: one line per service. */
@@ -84,6 +91,7 @@ export const serviceImport = defineOperation({
84
91
  input: z.object({
85
92
  name: KindSchema,
86
93
  from: z.string().max(1000).optional().describe('(import) the file or folder to take the sign-in from'),
94
+ file: z.boolean().optional().describe('(import) ask for a file even when this PC has one to take'),
87
95
  }),
88
96
  output: MessageSchema,
89
97
  text: (o) => o.message,
@@ -113,7 +121,14 @@ export const SERVICE_OPERATIONS = [serviceImport, serviceLogin, serviceLogout];
113
121
  export function serviceCategory(kind) {
114
122
  return 'service:' + kind;
115
123
  }
116
- /** What can be done with one service: share or stop, import, sign in, forget. */
124
+ /** What to do when this PC has no sign-in to take, by kind (short). */
125
+ const PC_HELP = {
126
+ epic: 'Sign in to the Epic launcher here, with Remember me on.',
127
+ steam: 'Log in once with SteamCMD in C:\\steamcmd.',
128
+ unity: 'Sign in to the Unity Hub here.',
129
+ perplexity: 'Set PERPLEXITY_API_KEY for your Windows user.',
130
+ };
131
+ /** What can be done with one service: share or stop, take or import, type it, forget. Every one works from the menu. */
117
132
  function serviceActions(s) {
118
133
  return [
119
134
  action(setSetting, { scope: 'user', key: 'accounts.' + s.kind + '.share', value: !s.share }, {
@@ -122,61 +137,82 @@ function serviceActions(s) {
122
137
  label: s.share ? 'Stop sharing' : 'Share with sandboxes',
123
138
  importance: 1,
124
139
  }),
125
- ...(s.canImport ? [action(serviceImport, { name: s.kind }, { key: 'i', verb: 'import', importance: 1 })] : []),
126
- ...(s.canLogin ? [action(serviceLogin, { name: s.kind }, { key: 'n', verb: 'sign in', importance: 1 })] : []),
140
+ ...(s.fromPc
141
+ ? [
142
+ action(serviceImport, { name: s.kind }, { key: 'i', verb: 'take from this PC', label: "Take this PC's sign-in", importance: 1 }),
143
+ ]
144
+ : []),
145
+ ...(s.hasFileSource
146
+ ? [
147
+ action(serviceImport, { name: s.kind, file: true }, {
148
+ key: s.fromPc ? 'f' : 'i',
149
+ verb: 'import a file',
150
+ label: 'Import from a file…',
151
+ importance: s.fromPc ? 2 : 1,
152
+ }),
153
+ ]
154
+ : []),
155
+ ...(s.canLogin
156
+ ? [
157
+ action(serviceLogin, { name: s.kind }, { key: 'n', verb: 'type it in', label: 'Type the sign-in…', importance: 1 }),
158
+ ]
159
+ : []),
127
160
  ...(s.state !== 'none'
128
161
  ? [action(serviceLogout, { name: s.kind }, { key: 'x', verb: 'forget', importance: 2 })]
129
162
  : []),
130
163
  ];
131
164
  }
132
- /** How to get a first sign-in, in words (the actions below do it). */
133
- function howTo(s) {
134
- const ways = [...(s.canImport ? ['import it'] : []), ...(s.canLogin ? ['type it in'] : [])];
135
- return ways.length
136
- ? ways.join(' or ')
137
- : "sign in once inside a sandbox (it can't be copied from this PC: Windows locks it to your PC account)";
165
+ /** The one line of help for a service: how to get its first sign-in, or what happens next. Short. */
166
+ function helpLine(s) {
167
+ if (s.state !== 'none')
168
+ return undefined;
169
+ if (s.sandboxOnly)
170
+ return 'Sign in once inside a sandbox; it is kept for the next ones.';
171
+ if (s.fromPc && s.onPc)
172
+ return s.share ? "The next sandbox takes this PC's." : "Share it and the next sandbox takes this PC's.";
173
+ if (s.fromPc)
174
+ return PC_HELP[s.kind] ?? 'Not found on this PC.';
175
+ return s.canLogin ? 'Type it in, or import it from a file.' : 'Import it from a file.';
138
176
  }
139
- /** One service's section: its facts, its warnings and its actions. */
177
+ /** One service's section: a few short facts and its actions. The full warnings are asked before saving. */
140
178
  export function serviceSection(s) {
141
179
  const facts = [
142
180
  {
143
181
  id: s.kind + ':share',
144
- label: 'Shared with sandboxes',
182
+ label: 'Shared',
145
183
  value: s.share ? 'on' : 'off',
146
184
  tone: s.share ? 'ok' : 'muted',
147
- description: 'For sandboxes that use ' + s.usedFor + '. ' + s.warnings.join(' '),
185
+ description: 'The agent can use it.',
148
186
  },
149
187
  {
150
188
  id: s.kind + ':state',
151
189
  label: 'Sign-in',
152
190
  value: STATE_TEXT[s.state] + (s.savedAt ? ' ' + day(s.savedAt) : ''),
153
191
  tone: STATE_TONE[s.state],
154
- description: s.state === 'none' ? 'Save one: ' + howTo(s) + '.' : undefined,
192
+ description: s.state === 'unreadable'
193
+ ? 'Only the Windows user that saved it can read it: save it again.'
194
+ : (helpLine(s) ?? (s.source ? 'Saved ' + (SOURCE_TEXT[s.source] ?? s.source) + '.' : undefined)),
155
195
  },
156
- ...(s.source ? [{ id: s.kind + ':source', label: 'From', value: SOURCE_TEXT[s.source] ?? s.source }] : []),
196
+ ...(s.onPc === null
197
+ ? []
198
+ : [
199
+ {
200
+ id: s.kind + ':pc',
201
+ label: 'On this PC',
202
+ value: s.onPc ? 'found' : 'not found',
203
+ tone: s.onPc ? 'ok' : 'muted',
204
+ },
205
+ ]),
157
206
  ...(s.summary ? [{ id: s.kind + ':summary', label: 'Account', value: s.summary }] : []),
158
207
  ...(s.heldBy
159
208
  ? [{ id: s.kind + ':held', label: 'In use by', value: 'sandbox ' + s.heldBy, tone: 'accent' }]
160
209
  : []),
161
210
  ];
162
- const warnings = s.warnings.map((w, i) => ({
163
- id: s.kind + ':warning:' + i,
164
- label: w,
165
- tone: 'warn',
166
- group: 'Before you share it',
167
- }));
168
- const notes = [
169
- ...(s.state === 'unreadable'
170
- ? [{ text: 'Only the Windows user that saved it can read it: save it again.', tone: 'muted' }]
171
- : []),
172
- ];
173
211
  return {
174
212
  id: serviceCategory(s.kind),
175
213
  title: s.label,
176
214
  summary: (s.share ? 'shared' : 'not shared') + ' · ' + STATE_TEXT[s.state],
177
215
  facts,
178
- items: warnings,
179
- notes,
180
216
  actions: serviceActions(s),
181
217
  };
182
218
  }
@@ -4,14 +4,14 @@ import { type ServiceKind } from '../../../shared/accounts/index.js';
4
4
  import { type OperationIO } from '../../kernel/index.js';
5
5
  import { type ServiceRow } from './services.def.js';
6
6
  /** One service's row: never the sign-in, and the store's summary through safeText. */
7
- export declare function serviceRow(kind: ServiceKind, config: Config, store: ServiceAccountStore, leases: AccountLeases): ServiceRow;
7
+ export declare function serviceRow(kind: ServiceKind, config: Config, store: ServiceAccountStore, leases: AccountLeases, pc?: PcEnv): ServiceRow;
8
8
  export interface ImportDeps {
9
9
  readonly store: ServiceAccountStore;
10
10
  readonly pc: PcEnv;
11
11
  readonly share: boolean;
12
12
  }
13
13
  /** Saves a kind's sign-in from this PC's program or from a file, after a confirmation (--yes answers it). */
14
- export declare function importServiceFlow(io: OperationIO, kind: ServiceKind, from: string | undefined, deps: ImportDeps): Promise<{
14
+ export declare function importServiceFlow(io: OperationIO, kind: ServiceKind, from: string | undefined, deps: ImportDeps, wantFile?: boolean): Promise<{
15
15
  message: string;
16
16
  }>;
17
17
  /** Saves what you type for a kind (a key, a token). */
@@ -45,12 +45,17 @@ export declare const listServicesRun: import("../../kernel/define.js").Implement
45
45
  canImport: import("zod").ZodBoolean;
46
46
  canLogin: import("zod").ZodBoolean;
47
47
  needsFile: import("zod").ZodBoolean;
48
+ fromPc: import("zod").ZodBoolean;
49
+ hasFileSource: import("zod").ZodBoolean;
50
+ onPc: import("zod").ZodNullable<import("zod").ZodBoolean>;
51
+ sandboxOnly: import("zod").ZodBoolean;
48
52
  ready: import("zod").ZodBoolean;
49
53
  }, import("zod/v4/core").$strip>>;
50
54
  }, import("zod/v4/core").$strip>, "accounts.services">, import("../../../cli/commands/context.js").Context>;
51
55
  export declare const serviceImportRun: import("../../kernel/define.js").Implementation<import("../../kernel/define.js").OperationDef<import("zod").ZodObject<{
52
56
  name: import("zod").ZodOptional<import("zod").ZodString>;
53
57
  from: import("zod").ZodOptional<import("zod").ZodString>;
58
+ file: import("zod").ZodOptional<import("zod").ZodBoolean>;
54
59
  }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
55
60
  message: import("zod").ZodString;
56
61
  }, import("zod/v4/core").$strip>, "accounts.service.import">, import("../../../cli/commands/context.js").Context>;
@@ -12,8 +12,22 @@ import { implement } from '../../context.js';
12
12
  import { cancelled } from '../../kernel/index.js';
13
13
  import { isKindReady } from '../../../shared/readiness.js';
14
14
  import { listServices, serviceImport, serviceLogin, serviceLogout } from './services.def.js';
15
+ /** Does this PC have a sign-in to take? Only whether, never what: the payload is dropped at once. */
16
+ function presentOnPc(kind, pc) {
17
+ const source = HOST_SOURCES[kind];
18
+ if (!source.fromPc)
19
+ return null;
20
+ if (!pc)
21
+ return false;
22
+ try {
23
+ return source.fromPc(pc) !== null;
24
+ }
25
+ catch {
26
+ return false; // This PC's program has no usable sign-in.
27
+ }
28
+ }
15
29
  /** One service's row: never the sign-in, and the store's summary through safeText. */
16
- export function serviceRow(kind, config, store, leases) {
30
+ export function serviceRow(kind, config, store, leases, pc) {
17
31
  const spec = SERVICE_SPECS[kind];
18
32
  const source = HOST_SOURCES[kind];
19
33
  const state = store.state(kind);
@@ -34,6 +48,10 @@ export function serviceRow(kind, config, store, leases) {
34
48
  canImport: Boolean(source.fromPc || source.fromFile),
35
49
  canLogin: Boolean(source.fromAnswers),
36
50
  needsFile: Boolean(source.fromFile && !source.fromPc),
51
+ fromPc: Boolean(source.fromPc),
52
+ hasFileSource: Boolean(source.fromFile),
53
+ onPc: presentOnPc(kind, pc),
54
+ sandboxOnly: !source.fromPc && !source.fromFile && !source.fromAnswers,
37
55
  ready: isKindReady(kind),
38
56
  };
39
57
  }
@@ -93,7 +111,7 @@ function savedMessage(kind, share) {
93
111
  (share ? '' : ' Sandboxes get it once you turn on Share with sandboxes.'));
94
112
  }
95
113
  /** Saves a kind's sign-in from this PC's program or from a file, after a confirmation (--yes answers it). */
96
- export async function importServiceFlow(io, kind, from, deps) {
114
+ export async function importServiceFlow(io, kind, from, deps, wantFile = false) {
97
115
  const spec = SERVICE_SPECS[kind];
98
116
  const source = HOST_SOURCES[kind];
99
117
  if (!source.fromPc && !source.fromFile)
@@ -102,7 +120,7 @@ export async function importServiceFlow(io, kind, from, deps) {
102
120
  failWith(spec.label + ' cannot be imported.', 'Type it instead.', {
103
121
  op: 'accounts.service.login',
104
122
  input: { name: kind },
105
- label: 'Sign in…',
123
+ label: 'Type the sign-in…',
106
124
  cli: 'sandbox account login ' + kind,
107
125
  });
108
126
  let file = from === undefined ? undefined : path.resolve(from);
@@ -114,7 +132,7 @@ export async function importServiceFlow(io, kind, from, deps) {
114
132
  return false;
115
133
  }
116
134
  };
117
- const useFile = Boolean(source.fromFile && (file ? isFile(file) : !source.fromPc));
135
+ const useFile = Boolean(source.fromFile && (file ? isFile(file) : wantFile || !source.fromPc));
118
136
  if (useFile && !file) {
119
137
  const asked = await io.path({ title: 'The ' + spec.label + ' file:', pathKind: 'file' });
120
138
  if (asked === undefined)
@@ -214,7 +232,8 @@ export const listServicesRun = implement(listServices, async (ctx, input) => {
214
232
  if (name !== undefined && name !== '' && !isServiceKind(name))
215
233
  fail('There is no service called "' + safeText(input.name ?? '') + '".', 'Use one of: ' + SERVICE_KINDS.join(', '));
216
234
  const kinds = name ? [name] : [...SERVICE_KINDS];
217
- return { services: kinds.map((k) => serviceRow(k, ctx.loaded.config, store, leases)) };
235
+ const pc = pcEnv();
236
+ return { services: kinds.map((k) => serviceRow(k, ctx.loaded.config, store, leases, pc)) };
218
237
  });
219
238
  export const serviceImportRun = implement(serviceImport, async (ctx, input, io) => {
220
239
  requireOnPc('account import');
@@ -223,7 +242,7 @@ export const serviceImportRun = implement(serviceImport, async (ctx, input, io)
223
242
  store: new ServiceAccountStore(ctx.storageRoot),
224
243
  pc: pcEnv(),
225
244
  share: ctx.loaded.config.accounts[kind].share,
226
- });
245
+ }, input.file ?? false);
227
246
  });
228
247
  export const serviceLoginRun = implement(serviceLogin, async (ctx, input, io) => {
229
248
  requireOnPc('account login');
@@ -308,6 +308,7 @@ export declare const OPERATIONS: readonly [OperationDef<import("zod").ZodObject<
308
308
  }, import("zod/v4/core").$strip>, "github.logout">, OperationDef<import("zod").ZodObject<{
309
309
  name: import("zod").ZodOptional<import("zod").ZodString>;
310
310
  from: import("zod").ZodOptional<import("zod").ZodString>;
311
+ file: import("zod").ZodOptional<import("zod").ZodBoolean>;
311
312
  }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
312
313
  message: import("zod").ZodString;
313
314
  }, import("zod/v4/core").$strip>, "accounts.service.import">, OperationDef<import("zod").ZodObject<{
@@ -615,6 +616,10 @@ export declare const QUERIES: readonly [QueryDef<import("zod").ZodObject<{}, imp
615
616
  canImport: import("zod").ZodBoolean;
616
617
  canLogin: import("zod").ZodBoolean;
617
618
  needsFile: import("zod").ZodBoolean;
619
+ fromPc: import("zod").ZodBoolean;
620
+ hasFileSource: import("zod").ZodBoolean;
621
+ onPc: import("zod").ZodNullable<import("zod").ZodBoolean>;
622
+ sandboxOnly: import("zod").ZodBoolean;
618
623
  ready: import("zod").ZodBoolean;
619
624
  }, import("zod/v4/core").$strip>>;
620
625
  }, import("zod/v4/core").$strip>, "accounts.list">, QueryDef<import("zod").ZodObject<{
@@ -1084,6 +1089,10 @@ export declare const QUERIES: readonly [QueryDef<import("zod").ZodObject<{}, imp
1084
1089
  canImport: import("zod").ZodBoolean;
1085
1090
  canLogin: import("zod").ZodBoolean;
1086
1091
  needsFile: import("zod").ZodBoolean;
1092
+ fromPc: import("zod").ZodBoolean;
1093
+ hasFileSource: import("zod").ZodBoolean;
1094
+ onPc: import("zod").ZodNullable<import("zod").ZodBoolean>;
1095
+ sandboxOnly: import("zod").ZodBoolean;
1087
1096
  ready: import("zod").ZodBoolean;
1088
1097
  }, import("zod/v4/core").$strip>>;
1089
1098
  }, import("zod/v4/core").$strip>, "accounts.services">, QueryDef<import("zod").ZodObject<{}, import("zod/v4/core").$strip>, import("zod").ZodObject<{
@@ -1532,6 +1541,10 @@ export declare const VIEWS: readonly [import("../kernel/define.js").ViewDef<{
1532
1541
  canImport: import("zod").ZodBoolean;
1533
1542
  canLogin: import("zod").ZodBoolean;
1534
1543
  needsFile: import("zod").ZodBoolean;
1544
+ fromPc: import("zod").ZodBoolean;
1545
+ hasFileSource: import("zod").ZodBoolean;
1546
+ onPc: import("zod").ZodNullable<import("zod").ZodBoolean>;
1547
+ sandboxOnly: import("zod").ZodBoolean;
1535
1548
  ready: import("zod").ZodBoolean;
1536
1549
  }, import("zod/v4/core").$strip>>;
1537
1550
  }, import("zod/v4/core").$strip>, "accounts.list">;
@@ -1557,6 +1570,10 @@ export declare const VIEWS: readonly [import("../kernel/define.js").ViewDef<{
1557
1570
  canImport: import("zod").ZodBoolean;
1558
1571
  canLogin: import("zod").ZodBoolean;
1559
1572
  needsFile: import("zod").ZodBoolean;
1573
+ fromPc: import("zod").ZodBoolean;
1574
+ hasFileSource: import("zod").ZodBoolean;
1575
+ onPc: import("zod").ZodNullable<import("zod").ZodBoolean>;
1576
+ sandboxOnly: import("zod").ZodBoolean;
1560
1577
  ready: import("zod").ZodBoolean;
1561
1578
  }, import("zod/v4/core").$strip>>;
1562
1579
  }, import("zod/v4/core").$strip>, "accounts.services">;
@@ -1,4 +1,4 @@
1
- import type { AppContext } from './context.js';
1
+ import { type AppContext } from './context.js';
2
2
  import { type AnyDefinition, type Implementation, type OperationIO, type OutputOf, type QueryDef, type RpcHost } from './kernel/index.js';
3
3
  export declare const IMPLEMENTATIONS: readonly Implementation<AnyDefinition, AppContext>[];
4
4
  export declare function implementationOf(id: string): Implementation<AnyDefinition, AppContext> | undefined;
@@ -1,3 +1,6 @@
1
+ // Every feature's implementation, by id (Node side; features/defs.ts is the pure half the browser uses). Surfaces run
2
+ // definitions only through here, so input and output are checked the same way everywhere.
3
+ import { reloadSettings } from './context.js';
1
4
  import { fail } from '../core/util/errors.js';
2
5
  import { accessNeeded, allows, invoke, OWNER, refusalText, NO_QUESTIONS, onSurface, } from './kernel/index.js';
3
6
  import { addAgentRun, agentEnvRun, agentsEnvSetRun, listAgentsRun, makeDefaultAgentRun, removeAgentRun, startAgentRun, } from './features/agents/index.js';
@@ -129,7 +132,17 @@ export function runDefinition(id, ctx, input, io) {
129
132
  const impl = implementationOf(id);
130
133
  if (!impl)
131
134
  throw new Error('No implementation for ' + id);
132
- return invoke(impl, ctx, input, io);
135
+ return invokeAndRefresh(impl, ctx, input, io);
136
+ }
137
+ /** invoke(), then reads the settings again after an operation: the next read of any page sees what it saved. */
138
+ async function invokeAndRefresh(impl, ctx, input, io) {
139
+ try {
140
+ return await invoke(impl, ctx, input, io);
141
+ }
142
+ finally {
143
+ if (impl.def.kind === 'operation')
144
+ reloadSettings(ctx);
145
+ }
133
146
  }
134
147
  /** Reads a query in-process (the menu): typed output, and no questions. */
135
148
  export async function readQuery(def, ctx, input = {}) {
@@ -161,7 +174,7 @@ export function createRpcHost(ctx) {
161
174
  const need = accessNeeded(impl.def, input);
162
175
  if (!allows(caller, need))
163
176
  fail(refusalText(caller, need));
164
- return invoke(impl, ctx, input, io);
177
+ return invokeAndRefresh(impl, ctx, input, io);
165
178
  },
166
179
  };
167
180
  }
@@ -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-QUHNXHVG.js";
6
- import "./chunk-RSIX7C6D.js";
5
+ } from "./chunk-4M5SZZ5P.js";
6
+ import "./chunk-RU7XRN62.js";
7
7
 
8
8
  // src/bin/agent-sandbox.ts
9
9
  run(process.argv.slice(2));