nextclaw 0.18.12-beta.6 → 0.18.12-beta.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/app/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { _ as
|
|
2
|
+
import { _ as resolveUiApiBase, a as compareNpmRuntimeVersions, b as waitForExit, c as findListeningProcessByPort, d as isProcessRunning, f as openBrowser, g as resolveServiceLogPath, h as resolvePublicIp, i as NpmRuntimeBundleService, l as getPackageVersion$1, m as prompt, n as NpmRuntimeUpdateSourceService, o as NpmRuntimeBundleLayoutStore, p as printAgentResponse, s as findExecutableOnPath, t as NpmRuntimeUpdateStateStore, u as isLoopbackHost, v as resolveUiConfig, y as resolveUiStaticDir } from "../../npm-runtime-update-state.store-uaYppWyO.js";
|
|
3
3
|
import { createRequire } from "node:module";
|
|
4
4
|
import * as NextclawCore from "@nextclaw/core";
|
|
5
5
|
import { APP_NAME, APP_TAGLINE, AgentRouteResolver, BUILTIN_MAIN_AGENT_ID, ChannelManager, CommandRegistry, ConfigSchema, ContextBuilder, CronService, CronTool, DEFAULT_WORKSPACE_DIR, DEFAULT_WORKSPACE_PATH, DisposableStore, EditFileTool, ExecTool, ExtensionToolAdapter, FileLogSink, GatewayTool, InputBudgetPruner, LLMProvider, ListDirTool, MemoryGetTool, MemorySearchTool, MessageBus, MessageTool, ProviderManager, ReadFileTool, RequestedSkillsMetadataReader, SessionManager, SessionsHistoryTool, SessionsListTool, SkillsLoader, Tool, ToolRegistry, WebFetchTool, WebSearchTool, WriteFileTool, buildConfigSchema, buildMinimalSystemExecutionPrompt, buildReloadPlan, buildToolCatalogEntries, createAgentProfile, createAssistantStreamDeltaControlMessage, createAssistantStreamResetControlMessage, createGlobalTypedEventBus, createTypedEventKey, createTypingStopControlMessage, diffConfigPaths, expandHome, findEffectiveAgentProfile, getAppLogger, getConfigPath, getDataDir, getDataPath, getLoggingRuntime, getWorkspacePath, hasSecretRef, loadConfig, normalizeInlineSecretRefs, parseAgentScopedSessionKey, parseThinkingLevel, readSessionProjectRoot, redactConfigObject, removeAgentProfile, resolveAppLogPath, resolveConfigSecrets, resolveDefaultAgentProfileId, resolveEffectiveAgentProfiles, resolveLocalUiBaseUrl, resolveProviderRuntime, resolveSessionWorkspacePath, resolveThinkingLevel, saveConfig, toDisposable, updateAgentProfile } from "@nextclaw/core";
|
|
@@ -16286,7 +16286,7 @@ async function startUiShell(params) {
|
|
|
16286
16286
|
if (!uiConfig.enabled) return null;
|
|
16287
16287
|
let publishUiEvent = null;
|
|
16288
16288
|
const deferredNcpAgent = createDeferredUiNcpAgent();
|
|
16289
|
-
const uiServer = startUiServer({
|
|
16289
|
+
const uiServer = await startUiServer({
|
|
16290
16290
|
host: uiConfig.host,
|
|
16291
16291
|
port: uiConfig.port,
|
|
16292
16292
|
configPath,
|
|
@@ -18477,7 +18477,7 @@ var MacosLaunchAgentAutostartService = class {
|
|
|
18477
18477
|
` <string>${this.escapeXml(homeDir)}</string>`,
|
|
18478
18478
|
" </dict>",
|
|
18479
18479
|
" <key>RunAtLoad</key><true/>",
|
|
18480
|
-
" <key>KeepAlive</key><
|
|
18480
|
+
" <key>KeepAlive</key><false/>",
|
|
18481
18481
|
` <key>StandardOutPath</key><string>${this.escapeXml(stdoutPath)}</string>`,
|
|
18482
18482
|
` <key>StandardErrorPath</key><string>${this.escapeXml(stderrPath)}</string>`,
|
|
18483
18483
|
"</dict>",
|
|
@@ -20413,17 +20413,31 @@ var RestartCommands = class {
|
|
|
20413
20413
|
uiPort: opts.uiPort,
|
|
20414
20414
|
forcedPublicHost: this.deps.forcedPublicHost
|
|
20415
20415
|
});
|
|
20416
|
+
const targetUi = resolveUiConfig(loadConfig(), uiOverrides);
|
|
20416
20417
|
const state = managedServiceStateStore.read();
|
|
20417
20418
|
if (state && isProcessRunning(state.pid)) {
|
|
20418
20419
|
console.log(`Restarting ${APP_NAME}...`);
|
|
20419
20420
|
await this.deps.runtimeCommandService.stopService();
|
|
20420
20421
|
} else {
|
|
20422
|
+
const foregroundRuntime = localUiRuntimeStore.read();
|
|
20423
|
+
const foregroundMatchesTarget = Boolean(foregroundRuntime && isProcessRunning(foregroundRuntime.pid) && foregroundRuntime.uiPort === targetUi.port);
|
|
20424
|
+
if (foregroundRuntime && foregroundMatchesTarget) {
|
|
20425
|
+
if (!await this.restartForegroundRuntime(foregroundRuntime.pid)) return;
|
|
20426
|
+
await this.deps.startCommands.run(opts);
|
|
20427
|
+
return;
|
|
20428
|
+
}
|
|
20421
20429
|
if (state) {
|
|
20422
20430
|
managedServiceStateStore.clear();
|
|
20423
20431
|
console.log("Service state was stale and has been cleaned up.");
|
|
20424
20432
|
}
|
|
20425
20433
|
const unmanagedHealthyServiceMessage = await describeUnmanagedHealthyTargetMessage({ uiOverrides });
|
|
20426
20434
|
if (unmanagedHealthyServiceMessage) {
|
|
20435
|
+
const adoptedRuntimePid = this.resolveAdoptableForegroundRuntimePid(targetUi.port);
|
|
20436
|
+
if (adoptedRuntimePid) {
|
|
20437
|
+
if (!await this.restartForegroundRuntime(adoptedRuntimePid)) return;
|
|
20438
|
+
await this.deps.startCommands.run(opts);
|
|
20439
|
+
return;
|
|
20440
|
+
}
|
|
20427
20441
|
console.error(`Error: Cannot restart ${APP_NAME} because the target UI/API port is already served by a healthy unmanaged instance.`);
|
|
20428
20442
|
console.error(unmanagedHealthyServiceMessage);
|
|
20429
20443
|
return;
|
|
@@ -20432,6 +20446,40 @@ var RestartCommands = class {
|
|
|
20432
20446
|
}
|
|
20433
20447
|
await this.deps.startCommands.run(opts);
|
|
20434
20448
|
};
|
|
20449
|
+
restartForegroundRuntime = async (pid) => {
|
|
20450
|
+
console.log(`Restarting ${APP_NAME} foreground runtime (PID ${pid})...`);
|
|
20451
|
+
try {
|
|
20452
|
+
process.kill(pid, "SIGTERM");
|
|
20453
|
+
} catch (error) {
|
|
20454
|
+
console.error(`Failed to stop foreground runtime: ${String(error)}`);
|
|
20455
|
+
return false;
|
|
20456
|
+
}
|
|
20457
|
+
if (!await waitForExit(pid, 3e3)) {
|
|
20458
|
+
try {
|
|
20459
|
+
process.kill(pid, "SIGKILL");
|
|
20460
|
+
} catch (error) {
|
|
20461
|
+
console.error(`Failed to force stop foreground runtime: ${String(error)}`);
|
|
20462
|
+
return false;
|
|
20463
|
+
}
|
|
20464
|
+
if (!await waitForExit(pid, 2e3)) {
|
|
20465
|
+
console.error(`Failed to stop foreground runtime PID ${pid}.`);
|
|
20466
|
+
return false;
|
|
20467
|
+
}
|
|
20468
|
+
}
|
|
20469
|
+
localUiRuntimeStore.clearIfOwnedByProcess(pid);
|
|
20470
|
+
console.log(`✓ ${APP_NAME} foreground runtime stopped`);
|
|
20471
|
+
return true;
|
|
20472
|
+
};
|
|
20473
|
+
resolveAdoptableForegroundRuntimePid = (port) => {
|
|
20474
|
+
const listeningProcess = findListeningProcessByPort(port);
|
|
20475
|
+
if (!listeningProcess || !isProcessRunning(listeningProcess.pid)) return null;
|
|
20476
|
+
return this.isAdoptableNextclawRuntimeCommand(listeningProcess.command) ? listeningProcess.pid : null;
|
|
20477
|
+
};
|
|
20478
|
+
isAdoptableNextclawRuntimeCommand = (command) => {
|
|
20479
|
+
const normalized = command?.trim() ?? "";
|
|
20480
|
+
if (!normalized) return false;
|
|
20481
|
+
return /\bserve\b/.test(normalized) && (normalized.includes("/dist/cli/app/index.js") || normalized.includes("/src/cli/app/index.js") || normalized.includes("/runtime/dist/cli/app/index.js"));
|
|
20482
|
+
};
|
|
20435
20483
|
};
|
|
20436
20484
|
//#endregion
|
|
20437
20485
|
//#region src/cli/commands/serve/index.ts
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { i as NpmRuntimeBundleService, l as getPackageVersion$1, o as NpmRuntimeBundleLayoutStore, r as inferDefaultNpmRuntimeReleaseChannel, t as NpmRuntimeUpdateStateStore } from "../../npm-runtime-update-state.store-uaYppWyO.js";
|
|
3
3
|
import { createExternalCommandEnv } from "@nextclaw/core";
|
|
4
4
|
import { dirname, resolve } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
@@ -2,7 +2,7 @@ import { createExternalCommandEnv, getDataDir, getLogsPath, getPackageVersion, r
|
|
|
2
2
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { dirname, join, resolve } from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
|
-
import { spawn } from "node:child_process";
|
|
5
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
6
6
|
import { isIP } from "node:net";
|
|
7
7
|
import { cp, readdir, rename, rm } from "node:fs/promises";
|
|
8
8
|
//#region src/cli/shared/utils/cli.utils.ts
|
|
@@ -71,6 +71,98 @@ async function waitForExit(pid, timeoutMs) {
|
|
|
71
71
|
}
|
|
72
72
|
return !isProcessRunning(pid);
|
|
73
73
|
}
|
|
74
|
+
function runLookupCommand(command, args) {
|
|
75
|
+
try {
|
|
76
|
+
const result = spawnSync(command, args, {
|
|
77
|
+
encoding: "utf8",
|
|
78
|
+
env: createExternalCommandEnv(process.env)
|
|
79
|
+
});
|
|
80
|
+
if (result.error || result.status !== 0) return {
|
|
81
|
+
ok: false,
|
|
82
|
+
stdout: ""
|
|
83
|
+
};
|
|
84
|
+
return {
|
|
85
|
+
ok: true,
|
|
86
|
+
stdout: result.stdout ?? ""
|
|
87
|
+
};
|
|
88
|
+
} catch {
|
|
89
|
+
return {
|
|
90
|
+
ok: false,
|
|
91
|
+
stdout: ""
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function parseListeningPidFromLsof(raw) {
|
|
96
|
+
for (const line of raw.split("\n")) {
|
|
97
|
+
if (!line.startsWith("p")) continue;
|
|
98
|
+
const pid = Number(line.slice(1));
|
|
99
|
+
if (Number.isFinite(pid) && pid > 0) return pid;
|
|
100
|
+
}
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
function parseListeningPidFromNetstat(raw, port) {
|
|
104
|
+
const portSuffix = `:${port}`;
|
|
105
|
+
for (const line of raw.split("\n")) {
|
|
106
|
+
const trimmed = line.trim();
|
|
107
|
+
if (!trimmed || !trimmed.includes("LISTEN")) continue;
|
|
108
|
+
const columns = trimmed.split(/\s+/);
|
|
109
|
+
if (columns.length < 5) continue;
|
|
110
|
+
if (!(columns[1] ?? "").endsWith(portSuffix)) continue;
|
|
111
|
+
const pid = Number(columns[4]);
|
|
112
|
+
if (Number.isFinite(pid) && pid > 0) return pid;
|
|
113
|
+
}
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
function lookupProcessCommandByPid(pid) {
|
|
117
|
+
if (!Number.isFinite(pid) || pid <= 0) return null;
|
|
118
|
+
if (process.platform === "win32") {
|
|
119
|
+
const result = runLookupCommand("powershell", [
|
|
120
|
+
"-NoProfile",
|
|
121
|
+
"-Command",
|
|
122
|
+
`(Get-CimInstance Win32_Process -Filter "ProcessId = ${pid}").CommandLine`
|
|
123
|
+
]);
|
|
124
|
+
if (!result.ok) return null;
|
|
125
|
+
const command = result.stdout.trim();
|
|
126
|
+
return command.length > 0 ? command : null;
|
|
127
|
+
}
|
|
128
|
+
const result = runLookupCommand("ps", [
|
|
129
|
+
"-p",
|
|
130
|
+
String(pid),
|
|
131
|
+
"-o",
|
|
132
|
+
"command="
|
|
133
|
+
]);
|
|
134
|
+
if (!result.ok) return null;
|
|
135
|
+
const command = result.stdout.trim();
|
|
136
|
+
return command.length > 0 ? command : null;
|
|
137
|
+
}
|
|
138
|
+
function findListeningProcessByPort(port) {
|
|
139
|
+
if (!Number.isFinite(port) || port <= 0) return null;
|
|
140
|
+
let pid = null;
|
|
141
|
+
if (process.platform === "win32") {
|
|
142
|
+
const result = runLookupCommand("netstat", [
|
|
143
|
+
"-ano",
|
|
144
|
+
"-p",
|
|
145
|
+
"tcp"
|
|
146
|
+
]);
|
|
147
|
+
if (!result.ok) return null;
|
|
148
|
+
pid = parseListeningPidFromNetstat(result.stdout, port);
|
|
149
|
+
} else {
|
|
150
|
+
const result = runLookupCommand("lsof", [
|
|
151
|
+
"-nP",
|
|
152
|
+
`-iTCP:${port}`,
|
|
153
|
+
"-sTCP:LISTEN",
|
|
154
|
+
"-F",
|
|
155
|
+
"p"
|
|
156
|
+
]);
|
|
157
|
+
if (!result.ok) return null;
|
|
158
|
+
pid = parseListeningPidFromLsof(result.stdout);
|
|
159
|
+
}
|
|
160
|
+
if (!pid) return null;
|
|
161
|
+
return {
|
|
162
|
+
pid,
|
|
163
|
+
command: lookupProcessCommandByPid(pid)
|
|
164
|
+
};
|
|
165
|
+
}
|
|
74
166
|
function findNearestPackageManifest(startDir, expectedName) {
|
|
75
167
|
let current = resolve(startDir);
|
|
76
168
|
while (current.length > 0) {
|
|
@@ -525,4 +617,4 @@ var NpmRuntimeUpdateStateStore = class {
|
|
|
525
617
|
};
|
|
526
618
|
};
|
|
527
619
|
//#endregion
|
|
528
|
-
export {
|
|
620
|
+
export { resolveUiApiBase as _, compareNpmRuntimeVersions as a, waitForExit as b, findListeningProcessByPort as c, isProcessRunning as d, openBrowser as f, resolveServiceLogPath as g, resolvePublicIp as h, NpmRuntimeBundleService as i, getPackageVersion$1 as l, prompt as m, NpmRuntimeUpdateSourceService as n, NpmRuntimeBundleLayoutStore as o, printAgentResponse as p, inferDefaultNpmRuntimeReleaseChannel as r, findExecutableOnPath as s, NpmRuntimeUpdateStateStore as t, isLoopbackHost as u, resolveUiConfig as v, resolveUiStaticDir as y };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nextclaw",
|
|
3
|
-
"version": "0.18.12-beta.
|
|
3
|
+
"version": "0.18.12-beta.7",
|
|
4
4
|
"description": "Lightweight personal AI assistant with CLI, multi-provider routing, and channel integrations.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
@@ -41,21 +41,21 @@
|
|
|
41
41
|
"commander": "^12.1.0",
|
|
42
42
|
"jszip": "^3.10.1",
|
|
43
43
|
"yaml": "^2.8.1",
|
|
44
|
-
"@nextclaw/
|
|
45
|
-
"@nextclaw/companion": "0.1.1-beta.0",
|
|
44
|
+
"@nextclaw/companion": "0.1.1-beta.1",
|
|
46
45
|
"@nextclaw/core": "0.12.13-beta.1",
|
|
47
|
-
"@nextclaw/
|
|
48
|
-
"@nextclaw/ncp-agent-runtime": "0.3.16-beta.0",
|
|
49
|
-
"@nextclaw/ncp-mcp": "0.1.80-beta.0",
|
|
50
|
-
"@nextclaw/nextclaw-hermes-acp-bridge": "0.1.5-beta.0",
|
|
51
|
-
"@nextclaw/ncp-toolkit": "0.5.11-beta.0",
|
|
52
|
-
"@nextclaw/openclaw-compat": "1.0.13-beta.0",
|
|
53
|
-
"@nextclaw/nextclaw-ncp-runtime-http-client": "0.1.5-beta.0",
|
|
46
|
+
"@nextclaw/mcp": "0.1.78-beta.1",
|
|
54
47
|
"@nextclaw/ncp": "0.5.6-beta.0",
|
|
55
|
-
"@nextclaw/runtime": "0.
|
|
56
|
-
"@nextclaw/
|
|
57
|
-
"@nextclaw/
|
|
58
|
-
"@nextclaw/
|
|
48
|
+
"@nextclaw/ncp-agent-runtime": "0.3.16-beta.1",
|
|
49
|
+
"@nextclaw/ncp-toolkit": "0.5.11-beta.0",
|
|
50
|
+
"@nextclaw/nextclaw-hermes-acp-bridge": "0.1.5-beta.1",
|
|
51
|
+
"@nextclaw/kernel": "0.1.2-beta.2",
|
|
52
|
+
"@nextclaw/nextclaw-ncp-runtime-http-client": "0.1.5-beta.1",
|
|
53
|
+
"@nextclaw/ncp-mcp": "0.1.80-beta.1",
|
|
54
|
+
"@nextclaw/server": "0.12.13-beta.1",
|
|
55
|
+
"@nextclaw/remote": "0.1.90-beta.1",
|
|
56
|
+
"@nextclaw/openclaw-compat": "1.0.13-beta.1",
|
|
57
|
+
"@nextclaw/nextclaw-ncp-runtime-stdio-client": "0.1.6-beta.1",
|
|
58
|
+
"@nextclaw/runtime": "0.2.45-beta.0"
|
|
59
59
|
},
|
|
60
60
|
"devDependencies": {
|
|
61
61
|
"@types/node": "^20.17.6",
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
"tsx": "^4.19.2",
|
|
64
64
|
"typescript": "^5.6.3",
|
|
65
65
|
"vitest": "^4.1.2",
|
|
66
|
-
"@nextclaw/ui": "0.12.20-beta.
|
|
66
|
+
"@nextclaw/ui": "0.12.20-beta.1"
|
|
67
67
|
},
|
|
68
68
|
"scripts": {
|
|
69
69
|
"dev": "tsx watch --tsconfig tsconfig.json src/cli/app/index.ts",
|
package/resources/USAGE.md
CHANGED
|
@@ -189,6 +189,7 @@ When the gateway is already running, config changes from the UI or `nextclaw con
|
|
|
189
189
|
- `agents.defaults.model`
|
|
190
190
|
- `agents.context.*`
|
|
191
191
|
- `tools.*`
|
|
192
|
+
- `companion.enabled`
|
|
192
193
|
- `plugins.*` (v1 hot plugin runtime: plugin registry/channel gateways/channels are hot-reloaded)
|
|
193
194
|
|
|
194
195
|
Restart is still required for:
|
|
@@ -211,6 +212,40 @@ Useful commands:
|
|
|
211
212
|
|
|
212
213
|
UI note: **Model** page save persists `agents.defaults.model` only.
|
|
213
214
|
|
|
215
|
+
### Companion feature
|
|
216
|
+
|
|
217
|
+
NextClaw companion is a persistent system feature backed by `companion.enabled`, not just a one-off background process.
|
|
218
|
+
|
|
219
|
+
Recommended control path:
|
|
220
|
+
|
|
221
|
+
- Open the runtime settings page in the UI.
|
|
222
|
+
- Toggle **Companion enabled**.
|
|
223
|
+
- Save the runtime settings.
|
|
224
|
+
|
|
225
|
+
Behavior:
|
|
226
|
+
|
|
227
|
+
- enabling starts the companion immediately when a local runtime is available
|
|
228
|
+
- disabling stops any running companion process
|
|
229
|
+
- if it remains enabled, it auto-starts again after the NextClaw runtime restarts
|
|
230
|
+
|
|
231
|
+
CLI equivalents:
|
|
232
|
+
|
|
233
|
+
```bash
|
|
234
|
+
nextclaw companion enable
|
|
235
|
+
nextclaw companion disable
|
|
236
|
+
nextclaw companion status --json
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
Lower-level process controls are also available:
|
|
240
|
+
|
|
241
|
+
```bash
|
|
242
|
+
nextclaw companion start
|
|
243
|
+
nextclaw companion stop
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
Use `enable/disable` when you want the feature state to persist in config.
|
|
247
|
+
Use `start/stop` only for direct process control or debugging.
|
|
248
|
+
|
|
214
249
|
### Multi-agent routing & session isolation (OpenClaw-aligned)
|
|
215
250
|
|
|
216
251
|
For agent identities themselves, do **not** create them through `Routing & Runtime` or direct `agents.list` edits.
|
|
@@ -464,6 +499,11 @@ Skill loading contract:
|
|
|
464
499
|
| `nextclaw start` | Start gateway + UI in the background |
|
|
465
500
|
| `nextclaw restart` | Restart the background service with optional start flags |
|
|
466
501
|
| `nextclaw stop` | Stop the background service |
|
|
502
|
+
| `nextclaw companion enable` | Persistently enable the companion feature and start it when a local runtime is available |
|
|
503
|
+
| `nextclaw companion disable` | Persistently disable the companion feature and stop any running companion process |
|
|
504
|
+
| `nextclaw companion status` | Show current companion process/config status (`--json`) |
|
|
505
|
+
| `nextclaw companion start` | Start the companion process without changing the persisted feature toggle |
|
|
506
|
+
| `nextclaw companion stop` | Stop the companion process without changing the persisted feature toggle (`--force` supported) |
|
|
467
507
|
| `nextclaw service install-systemd --user` | Install a user-level Linux `systemd` service for NextClaw |
|
|
468
508
|
| `sudo nextclaw service install-systemd --system` | Install a system-wide Linux `systemd` service for NextClaw |
|
|
469
509
|
| `nextclaw service uninstall-systemd --user` | Remove a user-level Linux `systemd` service |
|
|
@@ -531,7 +571,7 @@ Autostart notes:
|
|
|
531
571
|
|
|
532
572
|
- `npm i -g nextclaw` installs the CLI only. It does not register host autostart by itself.
|
|
533
573
|
- On Linux, use `nextclaw service install-systemd --user` for a user-level login autostart path.
|
|
534
|
-
- On macOS, use `nextclaw service install-launch-agent` for a LaunchAgent-based login autostart path.
|
|
574
|
+
- On macOS, use `nextclaw service install-launch-agent` for a LaunchAgent-based login autostart path. The agent runs once at login and does not supervise retries after the runtime exits.
|
|
535
575
|
- On Windows, use `nextclaw service install-task` for a Scheduled Task based login autostart path.
|
|
536
576
|
- For machine-wide Linux startup after boot, use `sudo nextclaw service install-systemd --system`.
|
|
537
577
|
- `nextclaw service autostart status` and `nextclaw service autostart doctor` are read-only inspection commands; add `--user` or `--system` only when you need an explicit Linux `systemd` scope.
|