run-spaceapp 1.0.2 → 1.0.31
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/README.md +23 -5
- package/package.json +4 -4
- package/src/cli.mjs +634 -288
- package/src/index.mjs +90 -28
- package/src/prerequisites.mjs +36 -11
- package/src/prompt-controller.mjs +344 -0
- package/src/storage.mjs +159 -0
- package/src/support-manifest.json +147 -0
- package/templates/compose.yml +11 -1
package/src/index.mjs
CHANGED
|
@@ -20,17 +20,41 @@ import {
|
|
|
20
20
|
import { UNIVERSAL_COMMAND } from "./package-info.mjs";
|
|
21
21
|
|
|
22
22
|
const CONFIG_SCHEMA_VERSION = 4;
|
|
23
|
-
const MIN_INSTALL_CPU_COUNT = 4;
|
|
24
|
-
const MIN_INSTALL_MEMORY_BYTES = 7 * 1024 ** 3;
|
|
25
|
-
const MIN_INSTALL_MEMORY_LABEL = "7 GiB usable (8 GB-class system)";
|
|
26
23
|
const CONTAINER_SECRET_MODE = 0o644;
|
|
27
|
-
const
|
|
24
|
+
export const MIN_INSTALL_FREE_DISK_BYTES_STANDARD = 15 * 1024 ** 3;
|
|
25
|
+
export const MIN_INSTALL_FREE_DISK_BYTES_LIGHT = 7 * 1024 ** 3;
|
|
26
|
+
export const MIN_INSTALL_FREE_DISK_BYTES = MIN_INSTALL_FREE_DISK_BYTES_STANDARD;
|
|
28
27
|
const PROFILE_RUNTIME_SETTINGS = Object.freeze({
|
|
28
|
+
small: Object.freeze({
|
|
29
|
+
browserEnabled: false, temporalEnabled: false,
|
|
30
|
+
coreMemoryLimit: "2g", coreCpuLimit: "2.0",
|
|
31
|
+
cliMemoryLimit: "2g", cliCpuLimit: "1.5",
|
|
32
|
+
browserMemoryLimit: "1536m", browserCpuLimit: "1.5",
|
|
33
|
+
postgresMemoryLimit: "768m", postgresCpuLimit: "1.0",
|
|
34
|
+
temporalMemoryLimit: "768m", temporalCpuLimit: "1.0"
|
|
35
|
+
}),
|
|
36
|
+
medium: Object.freeze({
|
|
37
|
+
browserEnabled: false, temporalEnabled: true,
|
|
38
|
+
coreMemoryLimit: "3g", coreCpuLimit: "3.0",
|
|
39
|
+
cliMemoryLimit: "3g", cliCpuLimit: "2.0",
|
|
40
|
+
browserMemoryLimit: "1536m", browserCpuLimit: "1.5",
|
|
41
|
+
postgresMemoryLimit: "1g", postgresCpuLimit: "1.0",
|
|
42
|
+
temporalMemoryLimit: "768m", temporalCpuLimit: "1.0"
|
|
43
|
+
}),
|
|
44
|
+
large: Object.freeze({
|
|
45
|
+
browserEnabled: true, temporalEnabled: true,
|
|
46
|
+
coreMemoryLimit: "4g", coreCpuLimit: "4.0",
|
|
47
|
+
cliMemoryLimit: "4g", cliCpuLimit: "3.0",
|
|
48
|
+
browserMemoryLimit: "2g", browserCpuLimit: "2.0",
|
|
49
|
+
postgresMemoryLimit: "1g", postgresCpuLimit: "2.0",
|
|
50
|
+
temporalMemoryLimit: "1g", temporalCpuLimit: "2.0"
|
|
51
|
+
}),
|
|
29
52
|
light: Object.freeze({
|
|
30
53
|
browserEnabled: false,
|
|
54
|
+
temporalEnabled: true,
|
|
31
55
|
coreMemoryLimit: "2g",
|
|
32
56
|
coreCpuLimit: "2.0",
|
|
33
|
-
cliMemoryLimit: "
|
|
57
|
+
cliMemoryLimit: "2g",
|
|
34
58
|
cliCpuLimit: "1.5",
|
|
35
59
|
browserMemoryLimit: "1536m",
|
|
36
60
|
browserCpuLimit: "1.5",
|
|
@@ -41,6 +65,7 @@ const PROFILE_RUNTIME_SETTINGS = Object.freeze({
|
|
|
41
65
|
}),
|
|
42
66
|
standard: Object.freeze({
|
|
43
67
|
browserEnabled: true,
|
|
68
|
+
temporalEnabled: true,
|
|
44
69
|
coreMemoryLimit: "4g",
|
|
45
70
|
coreCpuLimit: "4.0",
|
|
46
71
|
cliMemoryLimit: "3g",
|
|
@@ -53,12 +78,27 @@ const PROFILE_RUNTIME_SETTINGS = Object.freeze({
|
|
|
53
78
|
temporalCpuLimit: "2.0"
|
|
54
79
|
})
|
|
55
80
|
});
|
|
81
|
+
export const INSTALL_PROFILES = Object.freeze(["small", "medium", "large", "light", "standard"]);
|
|
82
|
+
export const PROFILE_REQUIREMENTS = Object.freeze({
|
|
83
|
+
small: { cpuCount: 4, memoryBytes: 7 * 1024 ** 3, freshDiskBytes: 6.5 * 1024 ** 3 },
|
|
84
|
+
medium: { cpuCount: 4, memoryBytes: 11 * 1024 ** 3, freshDiskBytes: 8 * 1024 ** 3 },
|
|
85
|
+
large: { cpuCount: 8, memoryBytes: 15 * 1024 ** 3, freshDiskBytes: 11 * 1024 ** 3 },
|
|
86
|
+
// Existing installations keep their settings and background integrations.
|
|
87
|
+
light: { cpuCount: 4, memoryBytes: 7 * 1024 ** 3, freshDiskBytes: 8 * 1024 ** 3 },
|
|
88
|
+
standard: { cpuCount: 4, memoryBytes: 7 * 1024 ** 3, freshDiskBytes: 11 * 1024 ** 3 }
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
export function profileRuntimeSettings(profile) {
|
|
92
|
+
const settings = PROFILE_RUNTIME_SETTINGS[profile];
|
|
93
|
+
if (!settings) throw new Error("Install profile must be small, medium, large, light, or standard.");
|
|
94
|
+
return settings;
|
|
95
|
+
}
|
|
56
96
|
const SECRET_FIELD = /password|secret|token|api.?key|credential/i;
|
|
57
97
|
const VERSION_PATTERN = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/;
|
|
58
98
|
const BACKUP_ID_PATTERN = /^spaceapp-backup-\d{8}T\d{9}Z$/;
|
|
59
99
|
const PROVIDERS = Object.freeze({
|
|
60
|
-
bundled: Object.freeze(["opencode"
|
|
61
|
-
ownerInstalled: Object.freeze(["claude"]),
|
|
100
|
+
bundled: Object.freeze(["opencode"]),
|
|
101
|
+
ownerInstalled: Object.freeze(["codex", "gemini", "qwen", "kimi", "grok", "autohand", "cursor", "copilot", "claude"]),
|
|
62
102
|
experimental: Object.freeze(["deepseek"])
|
|
63
103
|
});
|
|
64
104
|
const ALL_PROVIDERS = new Set(Object.values(PROVIDERS).flat());
|
|
@@ -99,17 +139,23 @@ export function resolveSpaceAppHome({
|
|
|
99
139
|
return resolve(env.XDG_CONFIG_HOME || join(home, ".config"), "spaceapp");
|
|
100
140
|
}
|
|
101
141
|
|
|
102
|
-
export function resolveInstallProfile(requestedProfile,
|
|
103
|
-
if (requestedProfile
|
|
142
|
+
export function resolveInstallProfile(requestedProfile, resources) {
|
|
143
|
+
if (INSTALL_PROFILES.includes(requestedProfile)) {
|
|
104
144
|
return requestedProfile;
|
|
105
145
|
}
|
|
106
146
|
if (requestedProfile !== "auto") {
|
|
107
|
-
throw new Error("Install profile must be auto, light, or standard.");
|
|
147
|
+
throw new Error("Install profile must be auto, small, medium, large, light, or standard.");
|
|
108
148
|
}
|
|
149
|
+
const totalMemoryBytes = typeof resources === "number" ? resources : resources?.totalMemoryBytes;
|
|
109
150
|
if (!Number.isFinite(totalMemoryBytes) || totalMemoryBytes <= 0) {
|
|
110
151
|
throw new Error("Total system memory must be available for automatic profile selection.");
|
|
111
152
|
}
|
|
112
|
-
|
|
153
|
+
// Memory-only callers cannot safely choose a larger footprint.
|
|
154
|
+
if (typeof resources === "number") return "small";
|
|
155
|
+
for (const profile of ["large", "medium", "small"]) {
|
|
156
|
+
if (installResourceChecks(resources, profile).every((check) => check.ok)) return profile;
|
|
157
|
+
}
|
|
158
|
+
return "small";
|
|
113
159
|
}
|
|
114
160
|
|
|
115
161
|
export function resolveInstallAccessMode(requestedMode, existingMode = "isolated") {
|
|
@@ -198,29 +244,37 @@ export async function inspectSystemResources(root) {
|
|
|
198
244
|
};
|
|
199
245
|
}
|
|
200
246
|
|
|
201
|
-
export function installResourceChecks(resources) {
|
|
247
|
+
export function installResourceChecks(resources, profile = "small", { requiredDiskBytes, operation = "fresh" } = {}) {
|
|
202
248
|
const cpuCount = Number(resources?.cpuCount);
|
|
203
249
|
const totalMemoryBytes = Number(resources?.totalMemoryBytes);
|
|
204
250
|
const freeDiskBytes = Number(resources?.freeDiskBytes);
|
|
205
251
|
if (![cpuCount, totalMemoryBytes, freeDiskBytes].every((value) => Number.isFinite(value) && value >= 0)) {
|
|
206
252
|
throw new Error("System CPU, memory, and free-disk information is required.");
|
|
207
253
|
}
|
|
254
|
+
const requirements = PROFILE_REQUIREMENTS[profile];
|
|
255
|
+
if (!requirements) throw new Error("Unknown installation profile.");
|
|
256
|
+
const minFreeDisk = requiredDiskBytes ?? (operation === "fresh" ? requirements.freshDiskBytes : 512 * 1024 ** 2);
|
|
257
|
+
const engineMemoryBytes = { small: 3, medium: 5, large: 7, light: 3, standard: 3 }[profile] * 1024 ** 3;
|
|
208
258
|
return [
|
|
209
259
|
{
|
|
210
260
|
name: "CPU",
|
|
211
|
-
ok: cpuCount >=
|
|
212
|
-
detail: `${cpuCount} available; ${
|
|
261
|
+
ok: cpuCount >= requirements.cpuCount,
|
|
262
|
+
detail: `${cpuCount} available; ${requirements.cpuCount} required`
|
|
213
263
|
},
|
|
214
264
|
{
|
|
215
265
|
name: "Memory",
|
|
216
|
-
ok: totalMemoryBytes >=
|
|
217
|
-
detail: `${formatGibibytes(totalMemoryBytes)} GiB available; ${
|
|
266
|
+
ok: totalMemoryBytes >= requirements.memoryBytes,
|
|
267
|
+
detail: `${formatGibibytes(totalMemoryBytes)} GiB available; ${formatGibibytes(requirements.memoryBytes)} GiB usable${requirements.memoryBytes === 7 * 1024 ** 3 ? " (8 GB-class system)" : ""} required`
|
|
218
268
|
},
|
|
219
269
|
{
|
|
220
270
|
name: "Free disk",
|
|
221
|
-
ok: freeDiskBytes >=
|
|
222
|
-
detail: `${formatGibibytes(freeDiskBytes)} GiB available; ${formatGibibytes(
|
|
223
|
-
}
|
|
271
|
+
ok: freeDiskBytes >= minFreeDisk,
|
|
272
|
+
detail: `${formatGibibytes(freeDiskBytes)} GiB available; ${formatGibibytes(minFreeDisk)} GiB required${operation === "fresh" ? "" : " additionally for " + operation}`
|
|
273
|
+
},
|
|
274
|
+
...(resources.engineMemoryBytes > 0 ? [{ name: "Docker memory", ok: resources.engineMemoryBytes >= engineMemoryBytes,
|
|
275
|
+
detail: `${formatGibibytes(resources.engineMemoryBytes)} GiB allocated; ${formatGibibytes(engineMemoryBytes)} GiB required for ${profile}` }] : []),
|
|
276
|
+
...(resources.engineCpuCount > 0 ? [{ name: "Docker CPU", ok: resources.engineCpuCount >= 4,
|
|
277
|
+
detail: `${resources.engineCpuCount} allocated; 4 required` }] : [])
|
|
224
278
|
];
|
|
225
279
|
}
|
|
226
280
|
|
|
@@ -231,8 +285,8 @@ export function createDefaultConfig({
|
|
|
231
285
|
companionsEnabled = false
|
|
232
286
|
}) {
|
|
233
287
|
assertVersion(version);
|
|
234
|
-
if (profile
|
|
235
|
-
throw new Error("Default config requires a resolved
|
|
288
|
+
if (!INSTALL_PROFILES.includes(profile)) {
|
|
289
|
+
throw new Error("Default config requires a resolved installation profile.");
|
|
236
290
|
}
|
|
237
291
|
const resolvedAccessMode = resolveInstallAccessMode(accessMode);
|
|
238
292
|
return {
|
|
@@ -277,8 +331,8 @@ export function validateConfig(config) {
|
|
|
277
331
|
if (typeof config.telemetry !== "boolean") {
|
|
278
332
|
throw new Error("telemetry must be boolean.");
|
|
279
333
|
}
|
|
280
|
-
if (!
|
|
281
|
-
throw new Error("profile must be light or standard.");
|
|
334
|
+
if (!INSTALL_PROFILES.includes(config.profile)) {
|
|
335
|
+
throw new Error("profile must be small, medium, large, light, or standard.");
|
|
282
336
|
}
|
|
283
337
|
if (config.accessMode !== "isolated" && config.accessMode !== "host-root") {
|
|
284
338
|
throw new Error("accessMode must be isolated or host-root.");
|
|
@@ -487,6 +541,7 @@ export function renderRuntimeEnv(config) {
|
|
|
487
541
|
`SPACEAPP_TELEMETRY=${config.telemetry}`,
|
|
488
542
|
`SPACEAPP_PROFILE=${safeEnv(config.profile)}`,
|
|
489
543
|
`SPACEAPP_BROWSER_ENABLED=${settings.browserEnabled}`,
|
|
544
|
+
`SPACEAPP_WORKFLOWS_ENABLED=${settings.temporalEnabled}`,
|
|
490
545
|
`SPACEAPP_CORE_MEMORY_LIMIT=${settings.coreMemoryLimit}`,
|
|
491
546
|
`SPACEAPP_CORE_CPU_LIMIT=${settings.coreCpuLimit}`,
|
|
492
547
|
`SPACEAPP_CLI_MEMORY_LIMIT=${settings.cliMemoryLimit}`,
|
|
@@ -557,9 +612,7 @@ export function composeCommand(action, root, options = {}) {
|
|
|
557
612
|
const stateRoot = options.stateRoot ?? root;
|
|
558
613
|
validateHome(stateRoot);
|
|
559
614
|
const profile = options.profile ?? "standard";
|
|
560
|
-
|
|
561
|
-
throw new Error("Compose profile must be light or standard.");
|
|
562
|
-
}
|
|
615
|
+
const settings = profileRuntimeSettings(profile);
|
|
563
616
|
const base = [
|
|
564
617
|
"compose",
|
|
565
618
|
"--project-name", composeProjectName(root),
|
|
@@ -568,7 +621,8 @@ export function composeCommand(action, root, options = {}) {
|
|
|
568
621
|
"-f", join(stateRoot, "compose.yml"),
|
|
569
622
|
"-f", join(stateRoot, "compose.workspaces.yml"),
|
|
570
623
|
"-f", join(stateRoot, "compose.host-access.yml"),
|
|
571
|
-
...(
|
|
624
|
+
...(settings.browserEnabled ? ["--profile", "standard"] : []),
|
|
625
|
+
...(settings.temporalEnabled ? ["--profile", "workflows"] : []),
|
|
572
626
|
...(options.companionsEnabled ? ["--profile", "companions"] : [])
|
|
573
627
|
];
|
|
574
628
|
const actions = {
|
|
@@ -617,11 +671,19 @@ export function composeCommand(action, root, options = {}) {
|
|
|
617
671
|
"--stdin"
|
|
618
672
|
],
|
|
619
673
|
removeBrowser: ["rm", "--stop", "--force", "spaceapp-browser"],
|
|
674
|
+
removeTemporal: ["rm", "--stop", "--force", "temporal"],
|
|
675
|
+
enableTemporal: ["up", "-d", "--force-recreate", "temporal"],
|
|
676
|
+
databaseSize: ["exec", "-T", "postgres", "psql", "-U", "spaceapp", "-d", "spaceapp", "-Atc", "SELECT pg_database_size(current_database())"],
|
|
677
|
+
storageFree: ["exec", "-T", "spaceapp-core", "node", "-e", "const fs=require('node:fs');const s=fs.statfsSync('/var/lib/spaceapp');console.log(Number(s.bavail)*Number(s.bsize))"],
|
|
620
678
|
purge: ["down", "--volumes", "--remove-orphans"],
|
|
621
679
|
repair: ["up", "-d", "--remove-orphans", "--force-recreate"],
|
|
622
680
|
checkpointDump: ["exec", "-T", "postgres", "pg_dump", "-c", "--if-exists", "-U", "spaceapp", "-d", "spaceapp"],
|
|
623
681
|
checkpointRestore: ["exec", "-T", "postgres", "psql", "-v", "ON_ERROR_STOP=1", "-U", "spaceapp", "-d", "spaceapp"]
|
|
624
682
|
};
|
|
683
|
+
if (action === "installProvider") {
|
|
684
|
+
assertProvider(options.provider);
|
|
685
|
+
actions.installProvider = ["exec", "-T", "--user", "10001:10001", "spaceapp-cli", "node", "/app/deploy/docker/provider-install.mjs", options.provider];
|
|
686
|
+
}
|
|
625
687
|
let selected = actions[action];
|
|
626
688
|
if (action === "restore") {
|
|
627
689
|
if (typeof options.backupId !== "string" || !BACKUP_ID_PATTERN.test(options.backupId)) {
|
|
@@ -697,7 +759,7 @@ export async function prepareInstallation(root, {
|
|
|
697
759
|
]);
|
|
698
760
|
const resolvedProfile = profile === undefined
|
|
699
761
|
? undefined
|
|
700
|
-
: resolveInstallProfile(profile, totalmem());
|
|
762
|
+
: resolveInstallProfile(profile, profile === "auto" ? await inspectSystemResources(root) : totalmem());
|
|
701
763
|
let config;
|
|
702
764
|
try {
|
|
703
765
|
config = await loadConfig(root);
|
package/src/prerequisites.mjs
CHANGED
|
@@ -73,6 +73,8 @@ export function windowsPowerShellArgs(operation) {
|
|
|
73
73
|
"exit $process.ExitCode"
|
|
74
74
|
].join("; "),
|
|
75
75
|
"start-docker-desktop": [
|
|
76
|
+
"Set-Service -Name 'com.docker.service' -StartupType Automatic -ErrorAction SilentlyContinue",
|
|
77
|
+
"Start-Service -Name 'com.docker.service' -ErrorAction SilentlyContinue",
|
|
76
78
|
"$path = $env:SPACEAPP_DOCKER_DESKTOP_PATH",
|
|
77
79
|
"if ([string]::IsNullOrWhiteSpace($path)) { exit 1 }",
|
|
78
80
|
"Start-Process -FilePath $path"
|
|
@@ -164,7 +166,8 @@ export async function ensureDockerAvailable({
|
|
|
164
166
|
launch,
|
|
165
167
|
download,
|
|
166
168
|
pathExists,
|
|
167
|
-
sleep
|
|
169
|
+
sleep,
|
|
170
|
+
installArgs
|
|
168
171
|
});
|
|
169
172
|
}
|
|
170
173
|
return ensureLinuxDocker({
|
|
@@ -201,7 +204,12 @@ async function ensureWindowsDocker({
|
|
|
201
204
|
|
|
202
205
|
if (!application) {
|
|
203
206
|
stdout.write("Docker Desktop is required and is not installed.\n");
|
|
204
|
-
const accepted = await confirmDesktopInstall({
|
|
207
|
+
const accepted = await confirmDesktopInstall({
|
|
208
|
+
platformName: "Windows",
|
|
209
|
+
stdin,
|
|
210
|
+
stdout,
|
|
211
|
+
autoConfirm: Boolean(installArgs?.autoConfirm)
|
|
212
|
+
});
|
|
205
213
|
if (!accepted) {
|
|
206
214
|
stderr.write("Docker Desktop installation was cancelled.\n");
|
|
207
215
|
return { code: 1, reexecuted: false };
|
|
@@ -404,7 +412,8 @@ async function ensureMacDocker({
|
|
|
404
412
|
launch,
|
|
405
413
|
download,
|
|
406
414
|
pathExists,
|
|
407
|
-
sleep
|
|
415
|
+
sleep,
|
|
416
|
+
installArgs
|
|
408
417
|
}) {
|
|
409
418
|
const application = "/Applications/Docker.app";
|
|
410
419
|
const cliDirectory = posix.join(application, "Contents", "Resources", "bin");
|
|
@@ -412,7 +421,12 @@ async function ensureMacDocker({
|
|
|
412
421
|
|
|
413
422
|
if (!installed) {
|
|
414
423
|
stdout.write("Docker Desktop is required and is not installed.\n");
|
|
415
|
-
const accepted = await confirmDesktopInstall({
|
|
424
|
+
const accepted = await confirmDesktopInstall({
|
|
425
|
+
platformName: "macOS",
|
|
426
|
+
stdin,
|
|
427
|
+
stdout,
|
|
428
|
+
autoConfirm: Boolean(installArgs?.autoConfirm)
|
|
429
|
+
});
|
|
416
430
|
if (!accepted) {
|
|
417
431
|
stderr.write("Docker Desktop installation was cancelled.\n");
|
|
418
432
|
return { code: 1, reexecuted: false };
|
|
@@ -508,12 +522,12 @@ async function ensureMacDocker({
|
|
|
508
522
|
stderr.write("Docker Desktop is installed, but SpaceApp could not start it.\n");
|
|
509
523
|
return { code: launchCode, reexecuted: false };
|
|
510
524
|
}
|
|
511
|
-
if (await waitForDocker({ execute, sleep })) {
|
|
525
|
+
if (await waitForDocker({ execute, sleep, attempts: 300 })) {
|
|
512
526
|
stdout.write("Docker Desktop is ready.\n");
|
|
513
527
|
return { code: 0, reexecuted: false };
|
|
514
528
|
}
|
|
515
529
|
stderr.write(
|
|
516
|
-
`Docker Desktop
|
|
530
|
+
`Docker Desktop did not become ready after 10 minutes. Complete any Docker Desktop prompt, then run "${UNIVERSAL_INSTALL_COMMAND}" again.\n`
|
|
517
531
|
);
|
|
518
532
|
return { code: 1, reexecuted: false };
|
|
519
533
|
}
|
|
@@ -526,6 +540,7 @@ async function ensureLinuxDocker({
|
|
|
526
540
|
stderr,
|
|
527
541
|
execute,
|
|
528
542
|
download,
|
|
543
|
+
sleep,
|
|
529
544
|
osRelease,
|
|
530
545
|
installArgs
|
|
531
546
|
}) {
|
|
@@ -557,7 +572,7 @@ async function ensureLinuxDocker({
|
|
|
557
572
|
stderr.write("Docker Engine was installed, but its system service could not be started.\n");
|
|
558
573
|
return { code: startCode, reexecuted: false };
|
|
559
574
|
}
|
|
560
|
-
if (await
|
|
575
|
+
if (await waitForDocker({ execute, sleep, attempts: 15 })) {
|
|
561
576
|
stdout.write("Docker Engine is ready.\n");
|
|
562
577
|
return { code: 0, reexecuted: false };
|
|
563
578
|
}
|
|
@@ -596,7 +611,11 @@ async function ensureLinuxDocker({
|
|
|
596
611
|
return { code: reentryCode, reexecuted: true };
|
|
597
612
|
}
|
|
598
613
|
|
|
599
|
-
async function confirmDesktopInstall({ platformName, stdin, stdout }) {
|
|
614
|
+
async function confirmDesktopInstall({ platformName, stdin, stdout, autoConfirm = false }) {
|
|
615
|
+
if (autoConfirm) {
|
|
616
|
+
stdout.write(`Installing Docker Desktop for ${platformName} automatically as approved in setup plan...\n`);
|
|
617
|
+
return true;
|
|
618
|
+
}
|
|
600
619
|
return confirmQuestion({
|
|
601
620
|
stdin,
|
|
602
621
|
stdout,
|
|
@@ -832,7 +851,7 @@ function linuxReentryCommand({
|
|
|
832
851
|
requestedAccessMode,
|
|
833
852
|
noOpen = false
|
|
834
853
|
} = {}) {
|
|
835
|
-
if (!["auto", "light", "standard"].includes(requestedProfile)) {
|
|
854
|
+
if (!["auto", "small", "medium", "large", "light", "standard"].includes(requestedProfile)) {
|
|
836
855
|
throw new Error("Invalid SpaceApp profile for Docker group re-entry.");
|
|
837
856
|
}
|
|
838
857
|
const entrypoint = fileURLToPath(new URL("../bin/spaceapp.mjs", import.meta.url));
|
|
@@ -856,7 +875,7 @@ export function windowsResumeScript({
|
|
|
856
875
|
requestedAccessMode,
|
|
857
876
|
noOpen = false
|
|
858
877
|
} = {}) {
|
|
859
|
-
if (!["auto", "light", "standard"].includes(requestedProfile)) {
|
|
878
|
+
if (!["auto", "small", "medium", "large", "light", "standard"].includes(requestedProfile)) {
|
|
860
879
|
throw new Error("Invalid SpaceApp profile for Windows resume.");
|
|
861
880
|
}
|
|
862
881
|
if (typeof noOpen !== "boolean") {
|
|
@@ -1050,7 +1069,7 @@ function launchDetachedCommand(spec) {
|
|
|
1050
1069
|
if (spec.operation === "start-docker-desktop") {
|
|
1051
1070
|
child = spawn("powershell.exe", windowsPowerShellArgs(spec.operation), options);
|
|
1052
1071
|
} else if (spec.operation === "open-docker-desktop") {
|
|
1053
|
-
child = spawn("open", ["-a", "Docker"], options);
|
|
1072
|
+
child = spawn("open", ["-a", "/Applications/Docker.app"], options);
|
|
1054
1073
|
} else {
|
|
1055
1074
|
resolve(1);
|
|
1056
1075
|
return;
|
|
@@ -1061,9 +1080,15 @@ function launchDetachedCommand(spec) {
|
|
|
1061
1080
|
}
|
|
1062
1081
|
child.once("error", () => resolve(1));
|
|
1063
1082
|
child.once("spawn", () => {
|
|
1083
|
+
// macOS open exits after Launch Services accepts or rejects the launch.
|
|
1084
|
+
// Spawning the utility alone is not evidence that it opened Docker.
|
|
1085
|
+
if (spec.operation === "open-docker-desktop") return;
|
|
1064
1086
|
child.unref();
|
|
1065
1087
|
resolve(0);
|
|
1066
1088
|
});
|
|
1089
|
+
if (spec.operation === "open-docker-desktop") {
|
|
1090
|
+
child.once("exit", (code, signal) => resolve(code ?? (signal ? 1 : 0)));
|
|
1091
|
+
}
|
|
1067
1092
|
});
|
|
1068
1093
|
}
|
|
1069
1094
|
|