windows-development-sandbox 1.27.0 → 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.
- package/dist/app/context.d.ts +5 -0
- package/dist/app/context.js +13 -0
- package/dist/app/features/accounts/accounts.def.d.ts +16 -0
- package/dist/app/features/accounts/accounts.run.d.ts +4 -0
- package/dist/app/features/accounts/services.def.d.ts +15 -1
- package/dist/app/features/accounts/services.def.js +67 -31
- package/dist/app/features/accounts/services.run.d.ts +7 -2
- package/dist/app/features/accounts/services.run.js +25 -6
- package/dist/app/features/defs.d.ts +17 -0
- package/dist/app/features/unreal/unreal.run.js +13 -3
- package/dist/app/registry.d.ts +1 -1
- package/dist/app/registry.js +15 -2
- package/dist/bin/agent-sandbox.js +2 -2
- package/dist/bin/{chunk-JXL6ZOO3.js → chunk-4M5SZZ5P.js} +46 -41
- package/dist/bin/{chunk-ZXCAVVS4.js → chunk-FA5VWLP4.js} +1 -1
- package/dist/bin/{chunk-6WWMBCT2.js → chunk-RU7XRN62.js} +303 -112
- package/dist/bin/claude-host.js +2 -2
- package/dist/bin/claude-sbx.js +2 -2
- package/dist/bin/codex-sbx.js +2 -2
- package/dist/bin/gemini-sbx.js +2 -2
- package/dist/bin/sandbox.js +2 -2
- package/dist/bin/sbx-hide.js +2 -2
- package/dist/bin/sbx-show.js +2 -2
- package/dist/cli/commands/launch/launch.js +2 -1
- package/dist/cli/menu/menu.js +6 -1
- package/dist/cli/menu/pages/accounts-page.js +9 -5
- package/dist/guest/apps/app-installer.js +7 -5
- package/dist/guest/apps/comet-unpack.js +2 -2
- package/dist/guest/runtime/pools.d.ts +4 -4
- package/dist/guest/runtime/pools.js +4 -15
- package/dist/guest/runtime/process.d.ts +5 -2
- package/dist/guest/runtime/process.js +23 -3
- package/dist/guest/setup/unity-agent.js +4 -1
- package/dist/guest/toolchains/engines/epic-launcher.d.ts +7 -1
- package/dist/guest/toolchains/engines/epic-launcher.js +30 -8
- package/dist/guest/toolchains/engines/unreal-engines.js +2 -1
- package/dist/guest/toolchains/engines/unreal-status.js +1 -1
- package/dist/guest/toolchains/installers.d.ts +3 -3
- package/dist/guest/toolchains/installers.js +5 -5
- package/dist/guest/toolchains/native/mesa.js +35 -11
- package/dist/guest/toolchains/native/unreal.js +1 -0
- package/dist/guest/toolchains/native/vcruntime.js +2 -2
- package/dist/guest/toolchains/native/vs-persist.d.ts +2 -2
- package/dist/guest/toolchains/signature.d.ts +2 -0
- package/dist/guest/toolchains/signature.js +9 -2
- package/dist/guest/toolchains/step-scheduler.js +1 -1
- package/dist/host/accounts/deliver.d.ts +3 -0
- package/dist/host/accounts/deliver.js +27 -0
- package/dist/host/accounts/source.d.ts +2 -0
- package/dist/host/accounts/source.js +2 -0
- package/dist/host/pc-installs/discover.js +8 -4
- package/dist/host/pc-installs/epic.js +3 -0
- package/dist/host/pc-installs/unity.d.ts +2 -0
- package/dist/host/pc-installs/unity.js +12 -3
- package/dist/host/pc-installs/unreal.d.ts +5 -1
- package/dist/host/pc-installs/unreal.js +51 -9
- package/dist/runtime/agent.js +34 -22
- package/dist/runtime/daemon.js +134 -65
- package/dist/runtime/db.js +32 -16
- package/dist/runtime/main.js +136 -67
- package/dist/runtime/netproxy.js +26 -12
- package/dist/runtime/tool.js +32 -15
- package/dist/runtime/view.js +26 -12
- package/dist/selftest/app.test.js +20 -2
- package/dist/selftest/boot-runner.test.js +15 -1
- package/dist/selftest/engines.test.js +52 -2
- package/dist/selftest/menu-screens.js +52 -1
- package/dist/selftest/menu.test.js +29 -3
- package/dist/selftest/mixamo.test.js +1 -1
- package/dist/selftest/service-accounts.test.js +88 -2
- package/dist/selftest/ui-text.test.js +4 -0
- package/dist/shared/accounts/kinds.js +9 -9
- package/dist/shared/apps/catalogue-gamedev.js +1 -0
- package/dist/shared/apps/catalogue.d.ts +2 -0
- package/dist/shared/concurrency/pools.js +1 -1
- package/dist/shared/download/archive.js +4 -2
- package/dist/shared/download/index.d.ts +1 -1
- package/dist/shared/download/index.js +1 -1
- package/dist/shared/download/spawn.d.ts +7 -1
- package/dist/shared/download/spawn.js +16 -1
- package/dist/shared/paths/pc-installs.d.ts +8 -0
- package/dist/shared/unreal/launcher-files.d.ts +7 -0
- package/dist/shared/unreal/launcher-files.js +32 -1
- package/dist/shared/unreal/status.d.ts +1 -0
- package/dist/shared/unreal/status.js +2 -0
- package/dist/web/index.html +1 -1
- package/dist/web/{main-VPMYTSUL.js → main-NKM7ECSB.js} +59 -59
- package/native/csharp/HcsHost/AssemblyInfo.cs +3 -3
- package/native/csharp/HostTools/AssemblyInfo.cs +3 -3
- package/native/csharp/StorageHelper/AssemblyInfo.cs +3 -3
- package/package.json +1 -1
- package/schemas/config.schema.json +9 -9
- package/schemas/sandbox-config.schema.json +9 -9
package/dist/app/context.d.ts
CHANGED
|
@@ -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;
|
package/dist/app/context.js
CHANGED
|
@@ -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:
|
|
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
|
|
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: '
|
|
53
|
+
pc: 'from this PC',
|
|
47
54
|
file: 'from a file',
|
|
48
|
-
typed: '
|
|
49
|
-
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
|
|
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.
|
|
126
|
-
|
|
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
|
-
/**
|
|
133
|
-
function
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
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:
|
|
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
|
|
182
|
+
label: 'Shared',
|
|
145
183
|
value: s.share ? 'on' : 'off',
|
|
146
184
|
tone: s.share ? 'ok' : 'muted',
|
|
147
|
-
description: '
|
|
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 === '
|
|
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.
|
|
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: '
|
|
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
|
-
|
|
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">;
|
|
@@ -19,7 +19,11 @@ import { ConfigFile } from '../settings/index.js';
|
|
|
19
19
|
import { forgetEngineFolder, openEpicLauncher, registerEngineFolder, unrealOverview, } from './unreal.def.js';
|
|
20
20
|
const win = path.win32;
|
|
21
21
|
/** The launcher the sandbox installs (the 64-bit one of launcher 17 and later). */
|
|
22
|
-
|
|
22
|
+
/** Where the launcher installs in the sandbox: Program Files for newer launchers, (x86) for older ones. */
|
|
23
|
+
const GUEST_LAUNCHERS = [
|
|
24
|
+
'C:\\Program Files\\Epic Games\\Launcher\\Portal\\Binaries\\Win64\\EpicGamesLauncher.exe',
|
|
25
|
+
'C:\\Program Files (x86)\\Epic Games\\Launcher\\Portal\\Binaries\\Win64\\EpicGamesLauncher.exe',
|
|
26
|
+
];
|
|
23
27
|
const MAX_PACKS_SHOWN = 40;
|
|
24
28
|
const s = (text) => safeText(text);
|
|
25
29
|
const sn = (text) => (text === null ? null : safeText(text));
|
|
@@ -204,10 +208,16 @@ export const openEpicLauncherRun = implement(openEpicLauncher, async (ctx, _inpu
|
|
|
204
208
|
const status = new RunDir(ctx.dataPath).readSandboxStatus();
|
|
205
209
|
if (status?.phase !== 'ready')
|
|
206
210
|
fail('The sandbox is not ready yet.', 'Try again in a moment.');
|
|
207
|
-
|
|
211
|
+
const launcher = readStatus(ctx.dataPath)?.launcher;
|
|
212
|
+
if (!launcher?.installed)
|
|
208
213
|
fail('The Epic Games Launcher is not installed in this sandbox.', 'It is installed when the sandbox is set up for Unreal with the licence accepted (Unreal set-up settings).');
|
|
209
214
|
io.progress('Opening the Epic Games Launcher in the sandbox…');
|
|
210
|
-
const result = await new SandboxChannel(ctx.dataPath).exec(
|
|
215
|
+
const result = await new SandboxChannel(ctx.dataPath).exec(
|
|
216
|
+
// The guest's status only picks one of the known paths.
|
|
217
|
+
[
|
|
218
|
+
'C:\\Windows\\explorer.exe',
|
|
219
|
+
GUEST_LAUNCHERS.find((p) => p.toLowerCase() === launcher.exe?.toLowerCase()) ?? GUEST_LAUNCHERS[0],
|
|
220
|
+
], status.guestWorkspace ?? 'C:\\', 30);
|
|
211
221
|
// explorer.exe returns 1 even when it started the program.
|
|
212
222
|
if (!result.ok && result.exitCode !== 1)
|
|
213
223
|
fail('The sandbox could not open it: ' + safeText(result.message));
|
package/dist/app/registry.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
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;
|
package/dist/app/registry.js
CHANGED
|
@@ -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
|
|
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
|
|
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-
|
|
6
|
-
import "./chunk-
|
|
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));
|