@magclaw/cli-core 0.1.22 → 0.1.23
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/package.json +1 -1
- package/src/cli.js +216 -17
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -39,6 +39,10 @@ const PACKAGE_JSON = (() => {
|
|
|
39
39
|
}
|
|
40
40
|
})();
|
|
41
41
|
export const DAEMON_VERSION = String(PACKAGE_JSON.version || '0.0.0');
|
|
42
|
+
export const CLI_CORE_VERSION = DAEMON_VERSION;
|
|
43
|
+
const DAEMON_PACKAGE_NAME = '@magclaw/daemon';
|
|
44
|
+
const COMPUTER_PACKAGE_NAME = '@magclaw/computer';
|
|
45
|
+
const KNOWN_ENTRY_PACKAGE_NAMES = new Set([DAEMON_PACKAGE_NAME, COMPUTER_PACKAGE_NAME]);
|
|
42
46
|
const SOURCE_CODEX_HOME = path.resolve(process.env.MAGCLAW_CODEX_HOME_SOURCE || process.env.CODEX_HOME || path.join(os.homedir(), '.codex'));
|
|
43
47
|
const CODEX_HOME_SHARED_ENTRIES = ['auth.json', 'plugins', 'vendor_imports'];
|
|
44
48
|
export const CAPABILITIES = [
|
|
@@ -56,6 +60,71 @@ function now() {
|
|
|
56
60
|
return new Date().toISOString();
|
|
57
61
|
}
|
|
58
62
|
|
|
63
|
+
function packageInfoFromSpec(packageSpec = '') {
|
|
64
|
+
const match = String(packageSpec || '').trim().match(/^(@magclaw\/(?:daemon|computer))(?:@(.+))?$/);
|
|
65
|
+
return {
|
|
66
|
+
name: match?.[1] || '',
|
|
67
|
+
version: match?.[2] || '',
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function normalizeEntryPackageName(value = '', fallback = DAEMON_PACKAGE_NAME) {
|
|
72
|
+
const clean = String(value || '').trim();
|
|
73
|
+
if (KNOWN_ENTRY_PACKAGE_NAMES.has(clean)) return clean;
|
|
74
|
+
return fallback;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function packageKindForPackageName(packageName = '') {
|
|
78
|
+
return normalizeEntryPackageName(packageName) === COMPUTER_PACKAGE_NAME ? 'computer' : 'daemon';
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function packageBinForPackageName(packageName = '') {
|
|
82
|
+
return packageKindForPackageName(packageName) === 'computer' ? 'magclaw-computer' : 'magclaw';
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function packageSpecForPackageName(packageName = DAEMON_PACKAGE_NAME, version = 'latest') {
|
|
86
|
+
const name = normalizeEntryPackageName(packageName);
|
|
87
|
+
const cleanVersion = String(version || '').trim() || 'latest';
|
|
88
|
+
return cleanVersion === 'latest' ? `${name}@latest` : `${name}@${cleanVersion}`;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function runtimePackageInfo(env = process.env, service = {}) {
|
|
92
|
+
const envSpec = String(env.MAGCLAW_DAEMON_PACKAGE_SPEC || '').trim();
|
|
93
|
+
const serviceSpec = String(service.packageSpec || '').trim();
|
|
94
|
+
const parsed = packageInfoFromSpec(envSpec || serviceSpec);
|
|
95
|
+
const packageName = normalizeEntryPackageName(
|
|
96
|
+
env.MAGCLAW_ENTRY_PACKAGE_NAME
|
|
97
|
+
|| env.MAGCLAW_DAEMON_PACKAGE_NAME
|
|
98
|
+
|| service.packageName
|
|
99
|
+
|| parsed.name,
|
|
100
|
+
);
|
|
101
|
+
const packageVersion = String(
|
|
102
|
+
env.MAGCLAW_ENTRY_PACKAGE_VERSION
|
|
103
|
+
|| env.MAGCLAW_DAEMON_PACKAGE_VERSION
|
|
104
|
+
|| service.packageVersion
|
|
105
|
+
|| parsed.version
|
|
106
|
+
|| DAEMON_VERSION,
|
|
107
|
+
).trim();
|
|
108
|
+
const packageKind = String(
|
|
109
|
+
env.MAGCLAW_DAEMON_PACKAGE_KIND
|
|
110
|
+
|| service.packageKind
|
|
111
|
+
|| packageKindForPackageName(packageName),
|
|
112
|
+
).trim().toLowerCase() === 'computer' ? 'computer' : 'daemon';
|
|
113
|
+
const packageBin = String(
|
|
114
|
+
env.MAGCLAW_DAEMON_PACKAGE_BIN
|
|
115
|
+
|| service.packageBin
|
|
116
|
+
|| packageBinForPackageName(packageName),
|
|
117
|
+
).trim() || packageBinForPackageName(packageName);
|
|
118
|
+
const packageSpec = envSpec || serviceSpec || packageSpecForPackageName(packageName, packageVersion || 'latest');
|
|
119
|
+
return {
|
|
120
|
+
name: packageName,
|
|
121
|
+
version: packageVersion,
|
|
122
|
+
kind: packageKind,
|
|
123
|
+
bin: packageBin,
|
|
124
|
+
spec: packageSpec,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
59
128
|
function localTimestamp(date = new Date()) {
|
|
60
129
|
const pad = (value) => String(value).padStart(2, '0');
|
|
61
130
|
return [
|
|
@@ -584,17 +653,25 @@ async function saveProfile(profile, config, env = process.env) {
|
|
|
584
653
|
async function readServiceState(profile = DEFAULT_PROFILE, env = process.env) {
|
|
585
654
|
const paths = profilePaths(profile, env);
|
|
586
655
|
const state = await readJsonFile(paths.service, {});
|
|
656
|
+
const parsed = packageInfoFromSpec(state.packageSpec || '');
|
|
657
|
+
const packageName = normalizeEntryPackageName(state.packageName || parsed.name || DAEMON_PACKAGE_NAME);
|
|
658
|
+
const packageVersion = String(state.packageVersion || parsed.version || state.installedPackageVersion || state.installedDaemonVersion || '').trim();
|
|
587
659
|
return {
|
|
660
|
+
...state,
|
|
588
661
|
version: 1,
|
|
589
662
|
profile: paths.profile,
|
|
590
663
|
mode: state.mode || 'foreground',
|
|
591
664
|
background: Boolean(state.background),
|
|
592
665
|
launcher: state.launcher || '',
|
|
593
666
|
packageSpec: state.packageSpec || '',
|
|
667
|
+
packageName,
|
|
668
|
+
packageVersion,
|
|
669
|
+
packageKind: String(state.packageKind || packageKindForPackageName(packageName)).toLowerCase() === 'computer' ? 'computer' : 'daemon',
|
|
670
|
+
packageBin: state.packageBin || packageBinForPackageName(packageName),
|
|
594
671
|
previousPackageSpec: state.previousPackageSpec || '',
|
|
595
672
|
installedDaemonVersion: state.installedDaemonVersion || DAEMON_VERSION,
|
|
673
|
+
installedPackageVersion: state.installedPackageVersion || packageVersion || state.installedDaemonVersion || DAEMON_VERSION,
|
|
596
674
|
updatedAt: state.updatedAt || '',
|
|
597
|
-
...state,
|
|
598
675
|
};
|
|
599
676
|
}
|
|
600
677
|
|
|
@@ -3232,6 +3309,9 @@ class MagClawDaemon {
|
|
|
3232
3309
|
this.upgradeWorkerStarting = true;
|
|
3233
3310
|
const targetVersion = String(message.targetVersion || message.version || 'latest').trim() || 'latest';
|
|
3234
3311
|
const previousVersion = String(message.previousVersion || DAEMON_VERSION).trim() || DAEMON_VERSION;
|
|
3312
|
+
const packageName = normalizeEntryPackageName(message.packageName || packageInfoFromSpec(message.packageSpec || '').name || this.env.MAGCLAW_ENTRY_PACKAGE_NAME || this.env.MAGCLAW_DAEMON_PACKAGE_NAME);
|
|
3313
|
+
const packageKind = packageKindForPackageName(packageName);
|
|
3314
|
+
const packageBin = String(message.packageBin || packageBinForPackageName(packageName)).trim() || packageBinForPackageName(packageName);
|
|
3235
3315
|
const service = await readServiceState(this.paths.profile, this.env);
|
|
3236
3316
|
const activeService = backgroundServiceStatus(this.paths.profile, this.env);
|
|
3237
3317
|
if (!service.background || !activeService.active) {
|
|
@@ -3245,6 +3325,10 @@ class MagClawDaemon {
|
|
|
3245
3325
|
message: error,
|
|
3246
3326
|
previousVersion,
|
|
3247
3327
|
targetVersion,
|
|
3328
|
+
packageName,
|
|
3329
|
+
packageKind,
|
|
3330
|
+
packageBin,
|
|
3331
|
+
packageSpec: message.packageSpec || packageSpecForPackageName(packageName, targetVersion),
|
|
3248
3332
|
error,
|
|
3249
3333
|
service: activeService,
|
|
3250
3334
|
}, this.env);
|
|
@@ -3257,6 +3341,9 @@ class MagClawDaemon {
|
|
|
3257
3341
|
message: error,
|
|
3258
3342
|
previousVersion,
|
|
3259
3343
|
targetVersion,
|
|
3344
|
+
packageName,
|
|
3345
|
+
packageKind,
|
|
3346
|
+
packageBin,
|
|
3260
3347
|
});
|
|
3261
3348
|
this.upgradeWorkerStarting = false;
|
|
3262
3349
|
return false;
|
|
@@ -3270,7 +3357,10 @@ class MagClawDaemon {
|
|
|
3270
3357
|
message: 'Upgrade worker is starting.',
|
|
3271
3358
|
previousVersion,
|
|
3272
3359
|
targetVersion,
|
|
3273
|
-
|
|
3360
|
+
packageName,
|
|
3361
|
+
packageKind,
|
|
3362
|
+
packageBin,
|
|
3363
|
+
packageSpec: message.packageSpec || packageSpecForPackageName(packageName, targetVersion),
|
|
3274
3364
|
startedAt: now(),
|
|
3275
3365
|
}, this.env);
|
|
3276
3366
|
this.send({
|
|
@@ -3281,6 +3371,9 @@ class MagClawDaemon {
|
|
|
3281
3371
|
progress: 1,
|
|
3282
3372
|
previousVersion,
|
|
3283
3373
|
targetVersion,
|
|
3374
|
+
packageName,
|
|
3375
|
+
packageKind,
|
|
3376
|
+
packageBin,
|
|
3284
3377
|
message: 'Upgrade worker is starting.',
|
|
3285
3378
|
});
|
|
3286
3379
|
const args = [
|
|
@@ -3296,6 +3389,8 @@ class MagClawDaemon {
|
|
|
3296
3389
|
previousVersion,
|
|
3297
3390
|
];
|
|
3298
3391
|
if (message.packageSpec) args.push('--package-spec', String(message.packageSpec));
|
|
3392
|
+
args.push('--package-name', packageName);
|
|
3393
|
+
args.push('--package-bin', packageBin);
|
|
3299
3394
|
const child = spawn(process.execPath, args, {
|
|
3300
3395
|
cwd: process.cwd(),
|
|
3301
3396
|
detached: true,
|
|
@@ -3315,6 +3410,9 @@ class MagClawDaemon {
|
|
|
3315
3410
|
const commandId = String(message.commandId || '').trim();
|
|
3316
3411
|
const targetVersion = String(message.targetVersion || message.version || 'latest').trim() || 'latest';
|
|
3317
3412
|
const previousVersion = String(message.previousVersion || DAEMON_VERSION).trim() || DAEMON_VERSION;
|
|
3413
|
+
const packageName = normalizeEntryPackageName(message.packageName || packageInfoFromSpec(message.packageSpec || '').name || this.env.MAGCLAW_ENTRY_PACKAGE_NAME || this.env.MAGCLAW_DAEMON_PACKAGE_NAME);
|
|
3414
|
+
const packageKind = packageKindForPackageName(packageName);
|
|
3415
|
+
const packageBin = String(message.packageBin || packageBinForPackageName(packageName)).trim() || packageBinForPackageName(packageName);
|
|
3318
3416
|
if (!commandId) {
|
|
3319
3417
|
this.send({ type: 'daemon:upgrade:ack', status: 'failed', error: 'Missing commandId.' });
|
|
3320
3418
|
return;
|
|
@@ -3327,7 +3425,10 @@ class MagClawDaemon {
|
|
|
3327
3425
|
message: this.daemonIsIdleForUpgrade() ? 'Daemon accepted upgrade command.' : 'Waiting for all Agent work to become idle.',
|
|
3328
3426
|
previousVersion,
|
|
3329
3427
|
targetVersion,
|
|
3330
|
-
|
|
3428
|
+
packageName,
|
|
3429
|
+
packageKind,
|
|
3430
|
+
packageBin,
|
|
3431
|
+
packageSpec: message.packageSpec || packageSpecForPackageName(packageName, targetVersion),
|
|
3331
3432
|
requestedAt: now(),
|
|
3332
3433
|
}, this.env);
|
|
3333
3434
|
if (!this.daemonIsIdleForUpgrade()) {
|
|
@@ -3340,6 +3441,9 @@ class MagClawDaemon {
|
|
|
3340
3441
|
progress: 0,
|
|
3341
3442
|
previousVersion,
|
|
3342
3443
|
targetVersion,
|
|
3444
|
+
packageName,
|
|
3445
|
+
packageKind,
|
|
3446
|
+
packageBin,
|
|
3343
3447
|
message: 'Waiting for all Agent work to become idle.',
|
|
3344
3448
|
});
|
|
3345
3449
|
this.scheduleUpgradeIdleCheck();
|
|
@@ -3354,6 +3458,7 @@ class MagClawDaemon {
|
|
|
3354
3458
|
const service = await readServiceState(this.paths.profile, this.env);
|
|
3355
3459
|
const serviceStatus = backgroundServiceStatus(this.paths.profile, this.env);
|
|
3356
3460
|
const upgrade = await readUpgradeHandoff(this.paths.profile, this.env);
|
|
3461
|
+
const packageInfo = runtimePackageInfo(this.env, service);
|
|
3357
3462
|
return {
|
|
3358
3463
|
type: 'ready',
|
|
3359
3464
|
computerId: this.config.computerId || null,
|
|
@@ -3363,7 +3468,13 @@ class MagClawDaemon {
|
|
|
3363
3468
|
hostname: os.hostname(),
|
|
3364
3469
|
os: `${os.platform()} ${os.release()}`,
|
|
3365
3470
|
arch: os.arch(),
|
|
3366
|
-
daemonVersion: DAEMON_VERSION,
|
|
3471
|
+
daemonVersion: packageInfo.version || DAEMON_VERSION,
|
|
3472
|
+
packageName: packageInfo.name,
|
|
3473
|
+
packageVersion: packageInfo.version,
|
|
3474
|
+
packageKind: packageInfo.kind,
|
|
3475
|
+
packageSpec: packageInfo.spec,
|
|
3476
|
+
packageBin: packageInfo.bin,
|
|
3477
|
+
cliCoreVersion: CLI_CORE_VERSION,
|
|
3367
3478
|
service: {
|
|
3368
3479
|
mode: service.mode || serviceStatus.mode || 'foreground',
|
|
3369
3480
|
background: Boolean(service.background),
|
|
@@ -3372,7 +3483,12 @@ class MagClawDaemon {
|
|
|
3372
3483
|
serviceName: serviceStatus.serviceName || '',
|
|
3373
3484
|
taskName: serviceStatus.taskName || '',
|
|
3374
3485
|
launcher: service.launcher || '',
|
|
3375
|
-
packageSpec: service.packageSpec || '',
|
|
3486
|
+
packageSpec: service.packageSpec || packageInfo.spec || '',
|
|
3487
|
+
packageName: service.packageName || packageInfo.name,
|
|
3488
|
+
packageVersion: service.packageVersion || packageInfo.version,
|
|
3489
|
+
packageKind: service.packageKind || packageInfo.kind,
|
|
3490
|
+
packageBin: service.packageBin || packageInfo.bin,
|
|
3491
|
+
cliCoreVersion: CLI_CORE_VERSION,
|
|
3376
3492
|
},
|
|
3377
3493
|
upgrade: upgrade || null,
|
|
3378
3494
|
runtimes: runtimes.filter((runtime) => runtime.installed).map((runtime) => runtime.id),
|
|
@@ -3392,11 +3508,18 @@ class MagClawDaemon {
|
|
|
3392
3508
|
}
|
|
3393
3509
|
|
|
3394
3510
|
sendHeartbeat() {
|
|
3511
|
+
const packageInfo = runtimePackageInfo(this.env);
|
|
3395
3512
|
const sent = this.send({
|
|
3396
3513
|
type: 'heartbeat',
|
|
3397
3514
|
time: now(),
|
|
3398
3515
|
computerId: this.config.computerId || null,
|
|
3399
|
-
daemonVersion: DAEMON_VERSION,
|
|
3516
|
+
daemonVersion: packageInfo.version || DAEMON_VERSION,
|
|
3517
|
+
packageName: packageInfo.name,
|
|
3518
|
+
packageVersion: packageInfo.version,
|
|
3519
|
+
packageKind: packageInfo.kind,
|
|
3520
|
+
packageSpec: packageInfo.spec,
|
|
3521
|
+
packageBin: packageInfo.bin,
|
|
3522
|
+
cliCoreVersion: CLI_CORE_VERSION,
|
|
3400
3523
|
runningAgents: [...this.sessions.keys()],
|
|
3401
3524
|
});
|
|
3402
3525
|
logInfo('daemon', `Sent heartbeat (runningAgents=${this.sessions.size}, sent=${sent}).`);
|
|
@@ -4020,13 +4143,38 @@ async function writeLauncher(profile, env = process.env) {
|
|
|
4020
4143
|
const launcher = path.join(paths.runDir, 'launcher.js');
|
|
4021
4144
|
const fallbackBin = executablePath();
|
|
4022
4145
|
const previousService = await readServiceState(paths.profile, env);
|
|
4146
|
+
const preferPersistedPackage = Boolean(previousService.pendingCommandId);
|
|
4147
|
+
const envPackageInfo = runtimePackageInfo(env, {});
|
|
4148
|
+
const persistedPackageInfo = runtimePackageInfo({}, previousService);
|
|
4149
|
+
const packageInfo = preferPersistedPackage ? persistedPackageInfo : runtimePackageInfo(env, previousService);
|
|
4150
|
+
const packageSpec = preferPersistedPackage
|
|
4151
|
+
? (previousService.packageSpec || persistedPackageInfo.spec)
|
|
4152
|
+
: (env.MAGCLAW_DAEMON_PACKAGE_SPEC || previousService.packageSpec || packageInfo.spec || packageSpecForPackageName(packageInfo.name, 'latest'));
|
|
4153
|
+
const packageName = normalizeEntryPackageName(
|
|
4154
|
+
packageInfoFromSpec(packageSpec).name
|
|
4155
|
+
|| packageInfo.name
|
|
4156
|
+
|| envPackageInfo.name
|
|
4157
|
+
|| previousService.packageName,
|
|
4158
|
+
);
|
|
4159
|
+
const packageVersion = String(packageInfoFromSpec(packageSpec).version || packageInfo.version || previousService.packageVersion || '').trim();
|
|
4160
|
+
const packageKind = packageKindForPackageName(packageName);
|
|
4161
|
+
const packageBin = String(
|
|
4162
|
+
(preferPersistedPackage ? previousService.packageBin : env.MAGCLAW_DAEMON_PACKAGE_BIN)
|
|
4163
|
+
|| previousService.packageBin
|
|
4164
|
+
|| packageBinForPackageName(packageName),
|
|
4165
|
+
).trim() || packageBinForPackageName(packageName);
|
|
4023
4166
|
const service = await writeServiceState(paths.profile, {
|
|
4024
4167
|
mode: process.platform === 'darwin' ? 'launchd' : process.platform === 'linux' ? 'systemd' : process.platform === 'win32' ? 'schtasks' : 'foreground',
|
|
4025
4168
|
background: true,
|
|
4026
4169
|
launcher,
|
|
4027
|
-
packageSpec
|
|
4170
|
+
packageSpec,
|
|
4171
|
+
packageName,
|
|
4172
|
+
packageVersion,
|
|
4173
|
+
packageKind,
|
|
4174
|
+
packageBin,
|
|
4028
4175
|
previousPackageSpec: previousService.previousPackageSpec || '',
|
|
4029
|
-
installedDaemonVersion: DAEMON_VERSION,
|
|
4176
|
+
installedDaemonVersion: packageVersion || DAEMON_VERSION,
|
|
4177
|
+
installedPackageVersion: packageVersion || DAEMON_VERSION,
|
|
4030
4178
|
commandMode: useNpmLauncher ? 'npm' : 'local',
|
|
4031
4179
|
}, env);
|
|
4032
4180
|
const code = [
|
|
@@ -4045,14 +4193,31 @@ async function writeLauncher(profile, env = process.env) {
|
|
|
4045
4193
|
"let service = {};",
|
|
4046
4194
|
"try { service = JSON.parse(fs.readFileSync(serviceFile, 'utf8')); } catch {}",
|
|
4047
4195
|
"const packageSpec = String(service.packageSpec || defaultPackageSpec || '@magclaw/daemon@latest');",
|
|
4196
|
+
"const packageName = String(service.packageName || (packageSpec.startsWith('@magclaw/computer@') || packageSpec === '@magclaw/computer' ? '@magclaw/computer' : '@magclaw/daemon'));",
|
|
4197
|
+
"const packageKind = String(service.packageKind || (packageName === '@magclaw/computer' ? 'computer' : 'daemon'));",
|
|
4198
|
+
"const packageBin = String(service.packageBin || (packageKind === 'computer' ? 'magclaw-computer' : 'magclaw'));",
|
|
4199
|
+
"const packageVersionMatch = packageSpec.match(/^@magclaw\\/(?:daemon|computer)@(.+)$/);",
|
|
4200
|
+
"const packageVersion = String(service.packageVersion || (packageVersionMatch ? packageVersionMatch[1] : ''));",
|
|
4048
4201
|
'const command = useNpmLauncher ? npmPath : process.execPath;',
|
|
4049
4202
|
"const args = useNpmLauncher",
|
|
4050
|
-
" ? ['exec', '--yes', '--package', packageSpec, '--',
|
|
4203
|
+
" ? ['exec', '--yes', '--package', packageSpec, '--', packageBin, 'connect', '--profile', profile]",
|
|
4051
4204
|
" : [fallbackBin, 'connect', '--profile', profile];",
|
|
4052
4205
|
"const launchPath = [nodeDir, npmDir, process.env.PATH || '/usr/bin:/bin:/usr/sbin:/sbin'].filter(Boolean).join(':');",
|
|
4206
|
+
"const childEnv = {",
|
|
4207
|
+
" ...process.env,",
|
|
4208
|
+
" MAGCLAW_DAEMON_HOME: daemonHome,",
|
|
4209
|
+
" MAGCLAW_ENTRY_PACKAGE_NAME: packageName,",
|
|
4210
|
+
" MAGCLAW_ENTRY_PACKAGE_VERSION: packageVersion,",
|
|
4211
|
+
" MAGCLAW_DAEMON_PACKAGE_NAME: packageName,",
|
|
4212
|
+
" MAGCLAW_DAEMON_PACKAGE_SPEC: packageSpec,",
|
|
4213
|
+
" MAGCLAW_DAEMON_PACKAGE_KIND: packageKind,",
|
|
4214
|
+
" MAGCLAW_DAEMON_PACKAGE_BIN: packageBin,",
|
|
4215
|
+
" PATH: launchPath,",
|
|
4216
|
+
"};",
|
|
4217
|
+
"if (packageKind === 'computer') childEnv.MAGCLAW_COMPUTER_DAEMON = '1';",
|
|
4053
4218
|
'const child = spawn(command, args, {',
|
|
4054
4219
|
" stdio: 'inherit',",
|
|
4055
|
-
' env:
|
|
4220
|
+
' env: childEnv,',
|
|
4056
4221
|
'});',
|
|
4057
4222
|
"child.on('exit', (code, signal) => {",
|
|
4058
4223
|
' if (signal) process.kill(process.pid, signal);',
|
|
@@ -4573,13 +4738,24 @@ async function openUpgradeProgressSocket(url) {
|
|
|
4573
4738
|
function packageSpecForUpgrade(targetVersion, flags = {}, env = process.env) {
|
|
4574
4739
|
const explicit = String(flags.packageSpec || env.MAGCLAW_DAEMON_UPGRADE_PACKAGE_SPEC || '').trim();
|
|
4575
4740
|
if (explicit) return explicit;
|
|
4741
|
+
const packageName = normalizeEntryPackageName(
|
|
4742
|
+
flags.packageName
|
|
4743
|
+
|| flags.package
|
|
4744
|
+
|| env.MAGCLAW_DAEMON_UPGRADE_PACKAGE_NAME
|
|
4745
|
+
|| env.MAGCLAW_ENTRY_PACKAGE_NAME
|
|
4746
|
+
|| env.MAGCLAW_DAEMON_PACKAGE_NAME
|
|
4747
|
+
|| packageInfoFromSpec(env.MAGCLAW_DAEMON_PACKAGE_SPEC || '').name,
|
|
4748
|
+
);
|
|
4576
4749
|
const target = String(targetVersion || '').trim();
|
|
4577
|
-
return target && target !== 'latest' ?
|
|
4750
|
+
return packageSpecForPackageName(packageName, target && target !== 'latest' ? target : 'latest');
|
|
4578
4751
|
}
|
|
4579
4752
|
|
|
4580
4753
|
function npmPackageLooksRemote(packageSpec) {
|
|
4581
4754
|
const value = String(packageSpec || '').trim();
|
|
4582
|
-
return value.startsWith('@magclaw/daemon@')
|
|
4755
|
+
return value.startsWith('@magclaw/daemon@')
|
|
4756
|
+
|| value === '@magclaw/daemon'
|
|
4757
|
+
|| value.startsWith('@magclaw/computer@')
|
|
4758
|
+
|| value === '@magclaw/computer';
|
|
4583
4759
|
}
|
|
4584
4760
|
|
|
4585
4761
|
function preflightPackage(packageSpec, env = process.env) {
|
|
@@ -4642,12 +4818,15 @@ async function runUpgradeWorker(flags, env = process.env) {
|
|
|
4642
4818
|
const targetVersion = String(flags.targetVersion || flags.version || flags.to || flags.tag || 'latest').trim() || 'latest';
|
|
4643
4819
|
const previousVersion = String(flags.previousVersion || DAEMON_VERSION).trim() || DAEMON_VERSION;
|
|
4644
4820
|
const packageSpec = packageSpecForUpgrade(targetVersion, flags, env);
|
|
4821
|
+
const packageName = normalizeEntryPackageName(packageInfoFromSpec(packageSpec).name || flags.packageName || env.MAGCLAW_ENTRY_PACKAGE_NAME || env.MAGCLAW_DAEMON_PACKAGE_NAME);
|
|
4822
|
+
const packageKind = packageKindForPackageName(packageName);
|
|
4823
|
+
const packageBin = String(flags.packageBin || env.MAGCLAW_DAEMON_UPGRADE_PACKAGE_BIN || packageBinForPackageName(packageName)).trim() || packageBinForPackageName(packageName);
|
|
4645
4824
|
const progressIntervalMs = Math.max(100, Math.min(5000, Number(flags.progressIntervalMs || env.MAGCLAW_DAEMON_UPGRADE_PROGRESS_MS || 500) || 500));
|
|
4646
4825
|
const readyTimeoutMs = Math.max(5000, Math.min(10 * 60_000, Number(flags.readyTimeoutMs || env.MAGCLAW_DAEMON_UPGRADE_READY_TIMEOUT_MS || 120_000) || 120_000));
|
|
4647
4826
|
const localOnly = Boolean(flags.localOnly || flags.local || flags.noWaitCloud);
|
|
4648
4827
|
const assumeReady = Boolean(flags.assumeReady || env.MAGCLAW_DAEMON_UPGRADE_ASSUME_READY === '1');
|
|
4649
4828
|
const serviceBefore = await readServiceState(profile, env);
|
|
4650
|
-
const previousPackageSpec = serviceBefore.packageSpec ||
|
|
4829
|
+
const previousPackageSpec = serviceBefore.packageSpec || packageSpecForPackageName(packageName, previousVersion);
|
|
4651
4830
|
const dryRunPlan = {
|
|
4652
4831
|
ok: true,
|
|
4653
4832
|
dryRun: Boolean(flags.dryRun),
|
|
@@ -4657,6 +4836,9 @@ async function runUpgradeWorker(flags, env = process.env) {
|
|
|
4657
4836
|
previousVersion,
|
|
4658
4837
|
targetVersion,
|
|
4659
4838
|
packageSpec,
|
|
4839
|
+
packageName,
|
|
4840
|
+
packageKind,
|
|
4841
|
+
packageBin,
|
|
4660
4842
|
previousPackageSpec,
|
|
4661
4843
|
service: serviceBefore,
|
|
4662
4844
|
localOnly,
|
|
@@ -4731,6 +4913,10 @@ async function runUpgradeWorker(flags, env = process.env) {
|
|
|
4731
4913
|
mode: serviceBefore.mode || (process.platform === 'darwin' ? 'launchd' : process.platform === 'linux' ? 'systemd' : process.platform === 'win32' ? 'schtasks' : 'foreground'),
|
|
4732
4914
|
background: true,
|
|
4733
4915
|
packageSpec,
|
|
4916
|
+
packageName,
|
|
4917
|
+
packageVersion: targetVersion === 'latest' ? '' : targetVersion,
|
|
4918
|
+
packageKind,
|
|
4919
|
+
packageBin,
|
|
4734
4920
|
previousPackageSpec,
|
|
4735
4921
|
pendingCommandId: commandId,
|
|
4736
4922
|
pendingTargetVersion: targetVersion,
|
|
@@ -4749,19 +4935,19 @@ async function runUpgradeWorker(flags, env = process.env) {
|
|
|
4749
4935
|
const complete = progressSocket ? await progressSocket.waitForComplete(readyTimeoutMs) : null;
|
|
4750
4936
|
if (complete?.status === 'succeeded') {
|
|
4751
4937
|
await emitProgress({ status: 'succeeded', phase: 'ready', progress: 100, message: 'Daemon upgrade completed.' });
|
|
4752
|
-
await writeServiceState(profile, { installedDaemonVersion: targetVersion, pendingCommandId: '', pendingTargetVersion: '' }, env);
|
|
4938
|
+
await writeServiceState(profile, { installedDaemonVersion: targetVersion, installedPackageVersion: targetVersion, packageVersion: targetVersion, pendingCommandId: '', pendingTargetVersion: '' }, env);
|
|
4753
4939
|
return { ok: true, commandId, targetVersion, packageSpec };
|
|
4754
4940
|
}
|
|
4755
4941
|
if (localOnly) {
|
|
4756
4942
|
const ready = await waitForLocalDaemonReady(profile, readyTimeoutMs, env);
|
|
4757
4943
|
if (!ready.ok) throw new Error(ready.error || 'Timed out waiting for the local daemon to become ready.');
|
|
4758
4944
|
await emitProgress({ status: 'succeeded', phase: 'ready', progress: 100, message: 'Daemon upgrade completed locally.' });
|
|
4759
|
-
await writeServiceState(profile, { installedDaemonVersion: targetVersion, pendingCommandId: '', pendingTargetVersion: '' }, env);
|
|
4945
|
+
await writeServiceState(profile, { installedDaemonVersion: targetVersion, installedPackageVersion: targetVersion, packageVersion: targetVersion, pendingCommandId: '', pendingTargetVersion: '' }, env);
|
|
4760
4946
|
return { ok: true, commandId, targetVersion, packageSpec, localReady: ready };
|
|
4761
4947
|
}
|
|
4762
4948
|
if (!progressSocket && assumeReady) {
|
|
4763
4949
|
await emitProgress({ status: 'succeeded', phase: 'ready', progress: 100, message: 'Daemon upgrade completed locally.' });
|
|
4764
|
-
await writeServiceState(profile, { installedDaemonVersion: targetVersion, pendingCommandId: '', pendingTargetVersion: '' }, env);
|
|
4950
|
+
await writeServiceState(profile, { installedDaemonVersion: targetVersion, installedPackageVersion: targetVersion, packageVersion: targetVersion, pendingCommandId: '', pendingTargetVersion: '' }, env);
|
|
4765
4951
|
return { ok: true, commandId, targetVersion, packageSpec, assumedReady: true };
|
|
4766
4952
|
}
|
|
4767
4953
|
throw new Error('Timed out waiting for upgraded daemon ready acknowledgement.');
|
|
@@ -4774,8 +4960,14 @@ async function runUpgradeWorker(flags, env = process.env) {
|
|
|
4774
4960
|
await emitProgress({ status: 'rollback', phase: 'rollback', progress: 82, message: `Rolling back: ${upgradeError}`, error: upgradeError });
|
|
4775
4961
|
let rollbackError = '';
|
|
4776
4962
|
try {
|
|
4963
|
+
const previousPackageInfo = packageInfoFromSpec(previousPackageSpec);
|
|
4964
|
+
const rollbackPackageName = normalizeEntryPackageName(previousPackageInfo.name || serviceBefore.packageName || packageName);
|
|
4777
4965
|
await writeServiceState(profile, {
|
|
4778
4966
|
packageSpec: previousPackageSpec,
|
|
4967
|
+
packageName: rollbackPackageName,
|
|
4968
|
+
packageVersion: previousPackageInfo.version || serviceBefore.packageVersion || previousVersion,
|
|
4969
|
+
packageKind: packageKindForPackageName(rollbackPackageName),
|
|
4970
|
+
packageBin: serviceBefore.packageBin || packageBinForPackageName(rollbackPackageName),
|
|
4779
4971
|
previousPackageSpec: packageSpec,
|
|
4780
4972
|
pendingCommandId: '',
|
|
4781
4973
|
pendingTargetVersion: '',
|
|
@@ -4838,6 +5030,7 @@ async function runComputerSetup(flags, env = process.env) {
|
|
|
4838
5030
|
const profile = safeProfileName(flags.profile && flags.profile !== DEFAULT_PROFILE ? flags.profile : serverSlug);
|
|
4839
5031
|
const owner = await ensureMachineFingerprint(profile, env);
|
|
4840
5032
|
const displayName = String(flags.displayName || flags.name || os.hostname()).trim();
|
|
5033
|
+
const packageInfo = runtimePackageInfo(env);
|
|
4841
5034
|
const started = await postSetupJson(serverUrl, '/api/cloud/computer/setup/start', {
|
|
4842
5035
|
serverSlug,
|
|
4843
5036
|
machineFingerprint: owner.fingerprint,
|
|
@@ -4845,7 +5038,13 @@ async function runComputerSetup(flags, env = process.env) {
|
|
|
4845
5038
|
hostname: os.hostname(),
|
|
4846
5039
|
os: os.platform(),
|
|
4847
5040
|
arch: os.arch(),
|
|
4848
|
-
daemonVersion: DAEMON_VERSION,
|
|
5041
|
+
daemonVersion: packageInfo.version || DAEMON_VERSION,
|
|
5042
|
+
packageName: packageInfo.name,
|
|
5043
|
+
packageVersion: packageInfo.version,
|
|
5044
|
+
packageKind: packageInfo.kind,
|
|
5045
|
+
packageSpec: packageInfo.spec,
|
|
5046
|
+
packageBin: packageInfo.bin,
|
|
5047
|
+
cliCoreVersion: CLI_CORE_VERSION,
|
|
4849
5048
|
});
|
|
4850
5049
|
process.stdout.write(`To finish login, open: ${started.verificationUri}\n`);
|
|
4851
5050
|
process.stdout.write(`and enter the code: ${started.userCode}\n`);
|