@magclaw/cli-core 0.1.24 → 0.1.26
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 +169 -25
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -404,6 +404,12 @@ export function profilePaths(profile = DEFAULT_PROFILE, env = process.env) {
|
|
|
404
404
|
};
|
|
405
405
|
}
|
|
406
406
|
|
|
407
|
+
function sleepSync(ms) {
|
|
408
|
+
const timeout = Math.max(0, Number(ms) || 0);
|
|
409
|
+
if (!timeout) return;
|
|
410
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, timeout);
|
|
411
|
+
}
|
|
412
|
+
|
|
407
413
|
function machineFingerprintValue() {
|
|
408
414
|
return `mfp_${crypto.createHash('sha256')
|
|
409
415
|
.update([
|
|
@@ -673,6 +679,10 @@ async function readServiceState(profile = DEFAULT_PROFILE, env = process.env) {
|
|
|
673
679
|
previousPackageSpec: state.previousPackageSpec || '',
|
|
674
680
|
installedDaemonVersion: state.installedDaemonVersion || DAEMON_VERSION,
|
|
675
681
|
installedPackageVersion: state.installedPackageVersion || packageVersion || state.installedDaemonVersion || DAEMON_VERSION,
|
|
682
|
+
remoteClosed: Boolean(state.remoteClosed),
|
|
683
|
+
remoteClosedAt: state.remoteClosedAt || '',
|
|
684
|
+
remoteCloseReason: state.remoteCloseReason || '',
|
|
685
|
+
remoteCloseCommandId: state.remoteCloseCommandId || '',
|
|
676
686
|
updatedAt: state.updatedAt || '',
|
|
677
687
|
};
|
|
678
688
|
}
|
|
@@ -691,6 +701,31 @@ async function writeServiceState(profile = DEFAULT_PROFILE, patch = {}, env = pr
|
|
|
691
701
|
return next;
|
|
692
702
|
}
|
|
693
703
|
|
|
704
|
+
async function clearRemoteClosedServiceState(profile = DEFAULT_PROFILE, env = process.env) {
|
|
705
|
+
return writeServiceState(profile, {
|
|
706
|
+
remoteClosed: false,
|
|
707
|
+
remoteClosedAt: '',
|
|
708
|
+
remoteCloseReason: '',
|
|
709
|
+
remoteCloseCommandId: '',
|
|
710
|
+
}, env);
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
async function markForegroundServiceState(profile = DEFAULT_PROFILE, env = process.env) {
|
|
714
|
+
const packageInfo = runtimePackageInfo(env);
|
|
715
|
+
return writeServiceState(profile, {
|
|
716
|
+
mode: 'foreground',
|
|
717
|
+
background: false,
|
|
718
|
+
launcher: 'foreground',
|
|
719
|
+
packageSpec: packageInfo.spec,
|
|
720
|
+
packageName: packageInfo.name,
|
|
721
|
+
packageVersion: packageInfo.version,
|
|
722
|
+
packageKind: packageInfo.kind,
|
|
723
|
+
packageBin: packageInfo.bin,
|
|
724
|
+
installedDaemonVersion: packageInfo.version || DAEMON_VERSION,
|
|
725
|
+
installedPackageVersion: packageInfo.version || DAEMON_VERSION,
|
|
726
|
+
}, env);
|
|
727
|
+
}
|
|
728
|
+
|
|
694
729
|
async function readUpgradeHandoff(profile = DEFAULT_PROFILE, env = process.env) {
|
|
695
730
|
const paths = profilePaths(profile, env);
|
|
696
731
|
const handoff = await readJsonFile(paths.upgradeHandoff, null);
|
|
@@ -1187,6 +1222,27 @@ export function toWebSocketUrl(serverUrl, config = {}) {
|
|
|
1187
1222
|
if (/^mfp_[a-f0-9]{64}$/i.test(machineFingerprint)) {
|
|
1188
1223
|
url.searchParams.set('machine_fingerprint', machineFingerprint.toLowerCase());
|
|
1189
1224
|
}
|
|
1225
|
+
const packageName = String(config.packageName || '').trim();
|
|
1226
|
+
const packageVersion = String(config.packageVersion || config.daemonVersion || '').trim();
|
|
1227
|
+
const packageKind = String(config.packageKind || '').trim();
|
|
1228
|
+
const packageSpec = String(config.packageSpec || '').trim();
|
|
1229
|
+
const packageBin = String(config.packageBin || '').trim();
|
|
1230
|
+
const cliCoreVersion = String(config.cliCoreVersion || '').trim();
|
|
1231
|
+
const serviceMode = String(config.serviceMode || '').trim();
|
|
1232
|
+
const serviceBackground = Boolean(config.serviceBackground);
|
|
1233
|
+
const serviceActive = Boolean(config.serviceActive);
|
|
1234
|
+
if (packageName) url.searchParams.set('package_name', packageName);
|
|
1235
|
+
if (packageVersion) {
|
|
1236
|
+
url.searchParams.set('package_version', packageVersion);
|
|
1237
|
+
url.searchParams.set('daemon_version', packageVersion);
|
|
1238
|
+
}
|
|
1239
|
+
if (packageKind) url.searchParams.set('package_kind', packageKind);
|
|
1240
|
+
if (packageSpec) url.searchParams.set('package_spec', packageSpec);
|
|
1241
|
+
if (packageBin) url.searchParams.set('package_bin', packageBin);
|
|
1242
|
+
if (cliCoreVersion) url.searchParams.set('cli_core_version', cliCoreVersion);
|
|
1243
|
+
if (serviceMode) url.searchParams.set('service_mode', serviceMode);
|
|
1244
|
+
url.searchParams.set('service_background', String(serviceBackground));
|
|
1245
|
+
url.searchParams.set('service_active', String(serviceActive));
|
|
1190
1246
|
return url;
|
|
1191
1247
|
}
|
|
1192
1248
|
|
|
@@ -3098,6 +3154,12 @@ class ClaudeAgentSession {
|
|
|
3098
3154
|
}
|
|
3099
3155
|
}
|
|
3100
3156
|
|
|
3157
|
+
function readOnlySessionAckStatus(session) {
|
|
3158
|
+
const status = String(session?.status || '').toLowerCase();
|
|
3159
|
+
if ((!status || status === 'offline') && !session?.started) return 'idle';
|
|
3160
|
+
return session?.status || 'idle';
|
|
3161
|
+
}
|
|
3162
|
+
|
|
3101
3163
|
class MagClawDaemon {
|
|
3102
3164
|
constructor(config, env = process.env) {
|
|
3103
3165
|
this.env = env;
|
|
@@ -3458,14 +3520,15 @@ class MagClawDaemon {
|
|
|
3458
3520
|
const commandId = String(message.commandId || '').trim();
|
|
3459
3521
|
const service = await readServiceState(this.paths.profile, this.env);
|
|
3460
3522
|
const serviceStatus = backgroundServiceStatus(this.paths.profile, this.env);
|
|
3523
|
+
const serviceRunMode = daemonServiceRunMode(service, serviceStatus);
|
|
3461
3524
|
const packageInfo = runtimePackageInfo(this.env, service);
|
|
3462
3525
|
const runMode = {
|
|
3463
|
-
mode:
|
|
3464
|
-
background:
|
|
3465
|
-
active:
|
|
3466
|
-
label: serviceStatus.label || '',
|
|
3467
|
-
serviceName: serviceStatus.serviceName || '',
|
|
3468
|
-
taskName: serviceStatus.taskName || '',
|
|
3526
|
+
mode: serviceRunMode.mode,
|
|
3527
|
+
background: serviceRunMode.background,
|
|
3528
|
+
active: serviceRunMode.active,
|
|
3529
|
+
label: serviceRunMode.background ? serviceStatus.label || '' : '',
|
|
3530
|
+
serviceName: serviceRunMode.background ? serviceStatus.serviceName || '' : '',
|
|
3531
|
+
taskName: serviceRunMode.background ? serviceStatus.taskName || '' : '',
|
|
3469
3532
|
packageName: packageInfo.name,
|
|
3470
3533
|
packageVersion: packageInfo.version,
|
|
3471
3534
|
packageKind: packageInfo.kind,
|
|
@@ -3473,6 +3536,12 @@ class MagClawDaemon {
|
|
|
3473
3536
|
packageBin: packageInfo.bin,
|
|
3474
3537
|
};
|
|
3475
3538
|
logWarning('daemon', `Remote close requested (${message.reason || 'closed_from_cloud'}).`);
|
|
3539
|
+
await writeServiceState(this.paths.profile, {
|
|
3540
|
+
remoteClosed: true,
|
|
3541
|
+
remoteClosedAt: now(),
|
|
3542
|
+
remoteCloseReason: message.reason || 'closed_from_cloud',
|
|
3543
|
+
remoteCloseCommandId: commandId,
|
|
3544
|
+
}, this.env);
|
|
3476
3545
|
for (const session of this.sessions.values()) session.stop();
|
|
3477
3546
|
this.sessions.clear();
|
|
3478
3547
|
this.send({ type: 'daemon:close:ack',
|
|
@@ -3482,8 +3551,15 @@ class MagClawDaemon {
|
|
|
3482
3551
|
service: runMode,
|
|
3483
3552
|
at: now(),
|
|
3484
3553
|
});
|
|
3485
|
-
const
|
|
3486
|
-
|
|
3554
|
+
const shouldStopBackground = Boolean(runMode.background);
|
|
3555
|
+
const background = shouldStopBackground
|
|
3556
|
+
? stopBackground(this.paths.profile, this.env, { disable: message.disableBackground !== false })
|
|
3557
|
+
: { ok: true, mode: runMode.mode || 'foreground', skipped: true };
|
|
3558
|
+
if (shouldStopBackground) {
|
|
3559
|
+
logInfo('daemon', `Close request stopped background service mode=${background.mode || 'foreground'} ok=${Boolean(background.ok)}.`);
|
|
3560
|
+
} else {
|
|
3561
|
+
logInfo('daemon', 'Foreground close request did not stop background service.');
|
|
3562
|
+
}
|
|
3487
3563
|
this.close();
|
|
3488
3564
|
process.exitCode = 0;
|
|
3489
3565
|
setTimeout(() => process.exit(0), 50).unref?.();
|
|
@@ -3494,6 +3570,7 @@ class MagClawDaemon {
|
|
|
3494
3570
|
const owner = await ensureMachineFingerprint(this.paths.profile, this.env);
|
|
3495
3571
|
const service = await readServiceState(this.paths.profile, this.env);
|
|
3496
3572
|
const serviceStatus = backgroundServiceStatus(this.paths.profile, this.env);
|
|
3573
|
+
const serviceRunMode = daemonServiceRunMode(service, serviceStatus);
|
|
3497
3574
|
const upgrade = await readUpgradeHandoff(this.paths.profile, this.env);
|
|
3498
3575
|
const packageInfo = runtimePackageInfo(this.env, service);
|
|
3499
3576
|
return {
|
|
@@ -3513,12 +3590,12 @@ class MagClawDaemon {
|
|
|
3513
3590
|
packageBin: packageInfo.bin,
|
|
3514
3591
|
cliCoreVersion: CLI_CORE_VERSION,
|
|
3515
3592
|
service: {
|
|
3516
|
-
mode:
|
|
3517
|
-
background:
|
|
3518
|
-
active:
|
|
3519
|
-
label: serviceStatus.label || '',
|
|
3520
|
-
serviceName: serviceStatus.serviceName || '',
|
|
3521
|
-
taskName: serviceStatus.taskName || '',
|
|
3593
|
+
mode: serviceRunMode.mode,
|
|
3594
|
+
background: serviceRunMode.background,
|
|
3595
|
+
active: serviceRunMode.active,
|
|
3596
|
+
label: serviceRunMode.background ? serviceStatus.label || '' : '',
|
|
3597
|
+
serviceName: serviceRunMode.background ? serviceStatus.serviceName || '' : '',
|
|
3598
|
+
taskName: serviceRunMode.background ? serviceStatus.taskName || '' : '',
|
|
3522
3599
|
launcher: service.launcher || '',
|
|
3523
3600
|
packageSpec: service.packageSpec || packageInfo.spec || '',
|
|
3524
3601
|
packageName: service.packageName || packageInfo.name,
|
|
@@ -3746,7 +3823,7 @@ class MagClawDaemon {
|
|
|
3746
3823
|
? await session.listSkills()
|
|
3747
3824
|
: { agent: { id: agent.id, name: agent.name || agent.id }, global: [], workspace: [], plugin: [], tools: [] };
|
|
3748
3825
|
this.send({ type: 'agent:skills:list_result', commandId: message.commandId, agentId: agent.id, skills });
|
|
3749
|
-
this.send({ type: 'agent:ack', commandId: message.commandId, agentId: agent.id, status: session
|
|
3826
|
+
this.send({ type: 'agent:ack', commandId: message.commandId, agentId: agent.id, status: readOnlySessionAckStatus(session) });
|
|
3750
3827
|
} catch (error) {
|
|
3751
3828
|
this.send({ type: 'agent:skills:list_result', commandId: message.commandId, agentId: agent.id, skills: { agent: { id: agent.id, name: agent.name || agent.id }, global: [], workspace: [], plugin: [], tools: [], error: error.message } });
|
|
3752
3829
|
this.send({ type: 'agent:error', commandId: message.commandId, agentId: agent.id, error: error.message });
|
|
@@ -3771,7 +3848,7 @@ class MagClawDaemon {
|
|
|
3771
3848
|
const session = await this.sessionForWorkspaceRequest(message);
|
|
3772
3849
|
const tree = await session.listWorkspace(message.path || message.payload?.path || '');
|
|
3773
3850
|
this.send({ type: 'agent:workspace:list_result', commandId: message.commandId, agentId: session.agent.id, tree });
|
|
3774
|
-
this.send({ type: 'agent:ack', commandId: message.commandId, agentId: session.agent.id, status: session
|
|
3851
|
+
this.send({ type: 'agent:ack', commandId: message.commandId, agentId: session.agent.id, status: readOnlySessionAckStatus(session) });
|
|
3775
3852
|
} catch (error) {
|
|
3776
3853
|
this.send({ type: 'agent:error', commandId: message.commandId, agentId: message.agentId || null, error: error.message });
|
|
3777
3854
|
}
|
|
@@ -3782,7 +3859,7 @@ class MagClawDaemon {
|
|
|
3782
3859
|
const session = await this.sessionForWorkspaceRequest(message);
|
|
3783
3860
|
const file = await session.readWorkspaceFile(message.path || message.payload?.path || 'MEMORY.md');
|
|
3784
3861
|
this.send({ type: 'agent:workspace:file_result', commandId: message.commandId, agentId: session.agent.id, file: file.file });
|
|
3785
|
-
this.send({ type: 'agent:ack', commandId: message.commandId, agentId: session.agent.id, status: session
|
|
3862
|
+
this.send({ type: 'agent:ack', commandId: message.commandId, agentId: session.agent.id, status: readOnlySessionAckStatus(session) });
|
|
3786
3863
|
} catch (error) {
|
|
3787
3864
|
this.send({ type: 'agent:error', commandId: message.commandId, agentId: message.agentId || null, error: error.message });
|
|
3788
3865
|
}
|
|
@@ -4032,11 +4109,25 @@ class MagClawDaemon {
|
|
|
4032
4109
|
async connectOnce() {
|
|
4033
4110
|
if (this.closed) return;
|
|
4034
4111
|
await this.refreshConfigFromDisk();
|
|
4035
|
-
const
|
|
4112
|
+
const service = await readServiceState(this.paths.profile, this.env);
|
|
4113
|
+
const serviceStatus = backgroundServiceStatus(this.paths.profile, this.env);
|
|
4114
|
+
const serviceRunMode = daemonServiceRunMode(service, serviceStatus);
|
|
4115
|
+
const packageInfo = runtimePackageInfo(this.env, service);
|
|
4116
|
+
const url = toWebSocketUrl(this.config.serverUrl, {
|
|
4117
|
+
...this.config,
|
|
4118
|
+
packageName: packageInfo.name,
|
|
4119
|
+
packageVersion: packageInfo.version,
|
|
4120
|
+
packageKind: packageInfo.kind,
|
|
4121
|
+
packageSpec: packageInfo.spec,
|
|
4122
|
+
packageBin: packageInfo.bin,
|
|
4123
|
+
cliCoreVersion: CLI_CORE_VERSION,
|
|
4124
|
+
serviceMode: serviceRunMode.mode,
|
|
4125
|
+
serviceBackground: serviceRunMode.background,
|
|
4126
|
+
serviceActive: serviceRunMode.active,
|
|
4127
|
+
});
|
|
4036
4128
|
const requestModule = url.protocol === 'wss:' ? https : http;
|
|
4037
4129
|
const requestUrl = new URL(url.href.replace(/^ws/, 'http'));
|
|
4038
4130
|
const key = crypto.randomBytes(16).toString('base64');
|
|
4039
|
-
const packageInfo = runtimePackageInfo(this.env);
|
|
4040
4131
|
logInfo('daemon', `Connecting MagClaw daemon v${packageInfo.version || DAEMON_VERSION} profile "${this.paths.profile}" to ${this.config.serverUrl}...`);
|
|
4041
4132
|
if (this.closed) return;
|
|
4042
4133
|
return new Promise((resolve, reject) => {
|
|
@@ -4218,6 +4309,10 @@ async function writeLauncher(profile, env = process.env) {
|
|
|
4218
4309
|
installedDaemonVersion: packageVersion || DAEMON_VERSION,
|
|
4219
4310
|
installedPackageVersion: packageVersion || DAEMON_VERSION,
|
|
4220
4311
|
commandMode: useNpmLauncher ? 'npm' : 'local',
|
|
4312
|
+
remoteClosed: false,
|
|
4313
|
+
remoteClosedAt: '',
|
|
4314
|
+
remoteCloseReason: '',
|
|
4315
|
+
remoteCloseCommandId: '',
|
|
4221
4316
|
}, env);
|
|
4222
4317
|
const code = [
|
|
4223
4318
|
'#!/usr/bin/env node',
|
|
@@ -4234,6 +4329,11 @@ async function writeLauncher(profile, env = process.env) {
|
|
|
4234
4329
|
`const defaultPackageSpec = ${JSON.stringify(service.packageSpec)};`,
|
|
4235
4330
|
"let service = {};",
|
|
4236
4331
|
"try { service = JSON.parse(fs.readFileSync(serviceFile, 'utf8')); } catch {}",
|
|
4332
|
+
"if (service.remoteClosed) {",
|
|
4333
|
+
" const stamp = new Date().toISOString().replace('T', ' ').slice(0, 19);",
|
|
4334
|
+
" console.error(`${stamp} INFO DAEMON profile ${profile} is closed from MagClaw Cloud; run magclaw start/restart/connect to reconnect.`);",
|
|
4335
|
+
" process.exit(0);",
|
|
4336
|
+
"}",
|
|
4237
4337
|
"const packageSpec = String(service.packageSpec || defaultPackageSpec || '@magclaw/daemon@latest');",
|
|
4238
4338
|
"const packageName = String(service.packageName || (packageSpec.startsWith('@magclaw/computer@') || packageSpec === '@magclaw/computer' ? '@magclaw/computer' : '@magclaw/daemon'));",
|
|
4239
4339
|
"const packageKind = String(service.packageKind || (packageName === '@magclaw/computer' ? 'computer' : 'daemon'));",
|
|
@@ -4446,6 +4546,38 @@ function backgroundServiceStatus(profile, env = process.env) {
|
|
|
4446
4546
|
return { mode: 'foreground', active: false };
|
|
4447
4547
|
}
|
|
4448
4548
|
|
|
4549
|
+
function daemonServiceRunMode(service = {}, serviceStatus = {}) {
|
|
4550
|
+
const background = service.background === true;
|
|
4551
|
+
return {
|
|
4552
|
+
mode: background ? (service.mode || serviceStatus.mode || 'foreground') : 'foreground',
|
|
4553
|
+
background,
|
|
4554
|
+
active: background ? Boolean(serviceStatus.active) : false,
|
|
4555
|
+
};
|
|
4556
|
+
}
|
|
4557
|
+
|
|
4558
|
+
function launchctlResultIsNotLoaded(result) {
|
|
4559
|
+
const detail = String(result?.stderr || result?.stdout || '').toLowerCase();
|
|
4560
|
+
return detail.includes('no such process') || detail.includes('could not find service') || detail.includes('not found');
|
|
4561
|
+
}
|
|
4562
|
+
|
|
4563
|
+
function launchctlServiceIsStopped(serviceTarget) {
|
|
4564
|
+
const result = spawnSync('launchctl', ['print', serviceTarget], { encoding: 'utf8' });
|
|
4565
|
+
if (result.status !== 0) return launchctlResultIsNotLoaded(result);
|
|
4566
|
+
const output = String(result.stdout || result.stderr || '');
|
|
4567
|
+
if (/\bstate\s*=\s*running\b/i.test(output)) return false;
|
|
4568
|
+
if (/\bpid\s*=\s*[1-9][0-9]*\b/i.test(output)) return false;
|
|
4569
|
+
return true;
|
|
4570
|
+
}
|
|
4571
|
+
|
|
4572
|
+
function waitForBackgroundServiceStopped(serviceTarget, timeoutMs = 2000) {
|
|
4573
|
+
const deadline = Date.now() + Math.max(250, Number(timeoutMs) || 2000);
|
|
4574
|
+
while (Date.now() < deadline) {
|
|
4575
|
+
if (launchctlServiceIsStopped(serviceTarget)) return true;
|
|
4576
|
+
sleepSync(100);
|
|
4577
|
+
}
|
|
4578
|
+
return launchctlServiceIsStopped(serviceTarget);
|
|
4579
|
+
}
|
|
4580
|
+
|
|
4449
4581
|
async function waitForPidExit(pid, timeoutMs = 2000) {
|
|
4450
4582
|
const deadline = Date.now() + timeoutMs;
|
|
4451
4583
|
while (Date.now() < deadline) {
|
|
@@ -4498,23 +4630,32 @@ function stopBackground(profile, env = process.env, options = {}) {
|
|
|
4498
4630
|
if (process.platform === 'darwin') {
|
|
4499
4631
|
const paths = profilePaths(profile, env);
|
|
4500
4632
|
const label = launchAgentLabel(paths.profile);
|
|
4633
|
+
const serviceTarget = `gui/${process.getuid()}/${label}`;
|
|
4501
4634
|
const plist = path.join(os.homedir(), 'Library', 'LaunchAgents', `${label}.plist`);
|
|
4502
4635
|
if (options.disable) {
|
|
4503
|
-
const disabled = spawnSync('launchctl', ['disable',
|
|
4636
|
+
const disabled = spawnSync('launchctl', ['disable', serviceTarget], { encoding: 'utf8' });
|
|
4504
4637
|
const stopped = spawnSync('launchctl', ['stop', label], { encoding: 'utf8' });
|
|
4638
|
+
const stoppedConfirmed = stopped.status === 0 && waitForBackgroundServiceStopped(serviceTarget);
|
|
4639
|
+
const needsBootout = disabled.status !== 0 || !stoppedConfirmed;
|
|
4640
|
+
const bootout = needsBootout
|
|
4641
|
+
? spawnSync('launchctl', ['bootout', serviceTarget], { encoding: 'utf8' })
|
|
4642
|
+
: null;
|
|
4643
|
+
const bootoutOk = bootout ? (bootout.status === 0 || launchctlResultIsNotLoaded(bootout)) : false;
|
|
4505
4644
|
if (stopped.status !== 0) spawnSync('launchctl', ['bootout', `gui/${process.getuid()}`, plist], { stdio: 'ignore' });
|
|
4506
4645
|
return {
|
|
4507
|
-
ok: disabled.status === 0
|
|
4646
|
+
ok: disabled.status === 0 && (stoppedConfirmed || bootoutOk),
|
|
4508
4647
|
mode: 'launchd',
|
|
4509
4648
|
label,
|
|
4649
|
+
serviceTarget,
|
|
4510
4650
|
file: plist,
|
|
4511
4651
|
disabled: disabled.status === 0,
|
|
4512
|
-
stopped:
|
|
4513
|
-
|
|
4652
|
+
stopped: stoppedConfirmed || bootoutOk,
|
|
4653
|
+
bootout: bootout ? bootout.status === 0 : false,
|
|
4654
|
+
error: disabled.status === 0 && (stoppedConfirmed || bootoutOk) ? '' : String(bootout?.stderr || stopped.stderr || disabled.stderr || bootout?.stdout || stopped.stdout || disabled.stdout || '').trim(),
|
|
4514
4655
|
};
|
|
4515
4656
|
}
|
|
4516
|
-
spawnSync('launchctl', ['bootout',
|
|
4517
|
-
return { ok:
|
|
4657
|
+
const bootout = spawnSync('launchctl', ['bootout', serviceTarget], { encoding: 'utf8' });
|
|
4658
|
+
return { ok: bootout.status === 0 || launchctlResultIsNotLoaded(bootout), mode: 'launchd', label, serviceTarget, file: plist, error: bootout.stderr || bootout.stdout || '' };
|
|
4518
4659
|
}
|
|
4519
4660
|
if (process.platform === 'linux') {
|
|
4520
4661
|
const paths = profilePaths(profile, env);
|
|
@@ -5141,6 +5282,7 @@ async function runComputerSetup(flags, env = process.env) {
|
|
|
5141
5282
|
fingerprint: owner.fingerprint,
|
|
5142
5283
|
}, env);
|
|
5143
5284
|
await saveProfile(config.profile, config, env);
|
|
5285
|
+
await clearRemoteClosedServiceState(config.profile, env);
|
|
5144
5286
|
const cli = await tryInstallCliShim(flags, env);
|
|
5145
5287
|
const result = await startBackground(config.profile, env);
|
|
5146
5288
|
printJson({
|
|
@@ -5179,6 +5321,7 @@ async function buildConfig(flags, env = process.env) {
|
|
|
5179
5321
|
|
|
5180
5322
|
async function runForegroundDaemon(config, env = process.env) {
|
|
5181
5323
|
const releaseLock = await acquireDaemonLock(config.profile, config, env);
|
|
5324
|
+
await markForegroundServiceState(config.profile, env);
|
|
5182
5325
|
const daemon = new MagClawDaemon(config, env);
|
|
5183
5326
|
let forceExitTimer = null;
|
|
5184
5327
|
const shutdown = (signal) => {
|
|
@@ -5205,6 +5348,7 @@ async function runConnect(flags, env = process.env) {
|
|
|
5205
5348
|
throw new Error('Run connect with --api-key, --pair-token for legacy pairing, or use a saved profile with a machine token.');
|
|
5206
5349
|
}
|
|
5207
5350
|
await saveProfile(config.profile, config, env);
|
|
5351
|
+
await clearRemoteClosedServiceState(config.profile, env);
|
|
5208
5352
|
const cli = await tryInstallCliShim(flags, env);
|
|
5209
5353
|
if (flags.background) {
|
|
5210
5354
|
const result = await startBackground(config.profile, env);
|