@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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/cli.js +216 -17
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@magclaw/cli-core",
3
- "version": "0.1.22",
3
+ "version": "0.1.23",
4
4
  "description": "Shared local MagClaw CLI implementation used by daemon and computer packages.",
5
5
  "type": "module",
6
6
  "bin": {
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
- packageSpec: message.packageSpec || '',
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
- packageSpec: message.packageSpec || '',
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: env.MAGCLAW_DAEMON_PACKAGE_SPEC || previousService.packageSpec || '@magclaw/daemon@latest',
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, '--', 'magclaw', 'connect', '--profile', profile]",
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: { ...process.env, MAGCLAW_DAEMON_HOME: daemonHome, PATH: launchPath },',
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' ? `@magclaw/daemon@${target}` : '@magclaw/daemon@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@') || value === '@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 || `@magclaw/daemon@${previousVersion}`;
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`);