nextclaw 0.13.5 → 0.13.6
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/index.js +641 -579
- package/package.json +10 -9
- package/templates/USAGE.md +15 -5
package/dist/cli/index.js
CHANGED
|
@@ -2,13 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
// src/cli/index.ts
|
|
4
4
|
import { Command } from "commander";
|
|
5
|
-
import { APP_NAME as
|
|
5
|
+
import { APP_NAME as APP_NAME6, APP_TAGLINE } from "@nextclaw/core";
|
|
6
|
+
import { registerRemoteCommands } from "@nextclaw/remote";
|
|
6
7
|
|
|
7
8
|
// src/cli/runtime.ts
|
|
8
9
|
import {
|
|
9
|
-
loadConfig as
|
|
10
|
-
saveConfig as
|
|
11
|
-
getConfigPath as
|
|
10
|
+
loadConfig as loadConfig14,
|
|
11
|
+
saveConfig as saveConfig10,
|
|
12
|
+
getConfigPath as getConfigPath7,
|
|
12
13
|
getDataDir as getDataDir9,
|
|
13
14
|
ConfigSchema as ConfigSchema2,
|
|
14
15
|
getWorkspacePath as getWorkspacePath10,
|
|
@@ -17,17 +18,18 @@ import {
|
|
|
17
18
|
AgentLoop,
|
|
18
19
|
ProviderManager as ProviderManager2,
|
|
19
20
|
resolveConfigSecrets as resolveConfigSecrets3,
|
|
20
|
-
APP_NAME as
|
|
21
|
+
APP_NAME as APP_NAME5,
|
|
21
22
|
DEFAULT_WORKSPACE_DIR,
|
|
22
23
|
DEFAULT_WORKSPACE_PATH
|
|
23
24
|
} from "@nextclaw/core";
|
|
25
|
+
import { RemoteRuntimeActions } from "@nextclaw/remote";
|
|
24
26
|
import {
|
|
25
27
|
getPluginChannelBindings as getPluginChannelBindings4,
|
|
26
28
|
resolvePluginChannelMessageToolHints as resolvePluginChannelMessageToolHints2,
|
|
27
29
|
setPluginRuntimeBridge as setPluginRuntimeBridge2
|
|
28
30
|
} from "@nextclaw/openclaw-compat";
|
|
29
|
-
import { existsSync as
|
|
30
|
-
import { join as
|
|
31
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync7, readFileSync as readFileSync10, writeFileSync as writeFileSync6 } from "fs";
|
|
32
|
+
import { join as join8, resolve as resolve12 } from "path";
|
|
31
33
|
import { createInterface as createInterface3 } from "readline";
|
|
32
34
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
33
35
|
import { spawn as spawn3 } from "child_process";
|
|
@@ -74,9 +76,9 @@ var RestartCoordinator = class {
|
|
|
74
76
|
message: "Restart already scheduled; skipping duplicate request."
|
|
75
77
|
};
|
|
76
78
|
}
|
|
77
|
-
const
|
|
79
|
+
const delay = typeof request.delayMs === "number" && Number.isFinite(request.delayMs) ? Math.max(0, Math.floor(request.delayMs)) : 100;
|
|
78
80
|
this.exitScheduled = true;
|
|
79
|
-
this.deps.scheduleProcessExit(
|
|
81
|
+
this.deps.scheduleProcessExit(delay, reason);
|
|
80
82
|
return {
|
|
81
83
|
status: "exit-scheduled",
|
|
82
84
|
message: `Restart scheduled (${reason}).`
|
|
@@ -822,6 +824,15 @@ function writeServiceState(state) {
|
|
|
822
824
|
mkdirSync3(resolve4(path2, ".."), { recursive: true });
|
|
823
825
|
writeFileSync3(path2, JSON.stringify(state, null, 2));
|
|
824
826
|
}
|
|
827
|
+
function updateServiceState(updater) {
|
|
828
|
+
const current = readServiceState();
|
|
829
|
+
if (!current) {
|
|
830
|
+
return null;
|
|
831
|
+
}
|
|
832
|
+
const next = updater(current);
|
|
833
|
+
writeServiceState(next);
|
|
834
|
+
return next;
|
|
835
|
+
}
|
|
825
836
|
function clearServiceState() {
|
|
826
837
|
const path2 = resolveServiceStatePath();
|
|
827
838
|
if (existsSync4(path2)) {
|
|
@@ -3025,285 +3036,311 @@ var PlatformAuthCommands = class {
|
|
|
3025
3036
|
};
|
|
3026
3037
|
|
|
3027
3038
|
// src/cli/commands/remote.ts
|
|
3039
|
+
import { getConfigPath as getConfigPath4, loadConfig as loadConfig9, saveConfig as saveConfig7 } from "@nextclaw/core";
|
|
3040
|
+
import "@nextclaw/remote";
|
|
3041
|
+
import { hostname } from "os";
|
|
3042
|
+
|
|
3043
|
+
// src/cli/commands/remote-runtime-support.ts
|
|
3028
3044
|
import { getConfigPath as getConfigPath3, getDataDir as getDataDir4, loadConfig as loadConfig8 } from "@nextclaw/core";
|
|
3029
|
-
import {
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3045
|
+
import {
|
|
3046
|
+
RemoteConnector,
|
|
3047
|
+
RemotePlatformClient,
|
|
3048
|
+
RemoteStatusStore,
|
|
3049
|
+
buildConfiguredRemoteState,
|
|
3050
|
+
resolveRemoteStatusSnapshot
|
|
3051
|
+
} from "@nextclaw/remote";
|
|
3052
|
+
function createNextclawRemotePlatformClient() {
|
|
3053
|
+
return new RemotePlatformClient({
|
|
3054
|
+
loadConfig: () => loadConfig8(getConfigPath3()),
|
|
3055
|
+
getDataDir: getDataDir4,
|
|
3056
|
+
getPackageVersion,
|
|
3057
|
+
resolvePlatformBase: (rawApiBase) => resolvePlatformApiBase({
|
|
3058
|
+
explicitApiBase: rawApiBase,
|
|
3059
|
+
requireConfigured: true
|
|
3060
|
+
}).platformBase,
|
|
3061
|
+
readManagedServiceState: () => {
|
|
3062
|
+
const state = readServiceState();
|
|
3063
|
+
if (!state) {
|
|
3064
|
+
return null;
|
|
3065
|
+
}
|
|
3066
|
+
return {
|
|
3067
|
+
pid: state.pid,
|
|
3068
|
+
uiPort: state.uiPort
|
|
3069
|
+
};
|
|
3070
|
+
},
|
|
3071
|
+
isProcessRunning
|
|
3072
|
+
});
|
|
3035
3073
|
}
|
|
3036
|
-
function
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3074
|
+
function createNextclawRemoteConnector(params = {}) {
|
|
3075
|
+
return new RemoteConnector({
|
|
3076
|
+
platformClient: createNextclawRemotePlatformClient(),
|
|
3077
|
+
logger: params.logger
|
|
3078
|
+
});
|
|
3041
3079
|
}
|
|
3042
|
-
function
|
|
3043
|
-
return new
|
|
3080
|
+
function createNextclawRemoteStatusStore(mode) {
|
|
3081
|
+
return new RemoteStatusStore(mode, {
|
|
3082
|
+
writeRemoteState: (next) => {
|
|
3083
|
+
updateServiceState((state) => ({
|
|
3084
|
+
...state,
|
|
3085
|
+
remote: next
|
|
3086
|
+
}));
|
|
3087
|
+
}
|
|
3088
|
+
});
|
|
3044
3089
|
}
|
|
3045
|
-
function
|
|
3046
|
-
|
|
3090
|
+
function buildNextclawConfiguredRemoteState(config2) {
|
|
3091
|
+
return buildConfiguredRemoteState(config2);
|
|
3047
3092
|
}
|
|
3048
|
-
function
|
|
3049
|
-
|
|
3050
|
-
return null;
|
|
3051
|
-
}
|
|
3052
|
-
try {
|
|
3053
|
-
return JSON.parse(readFileSync6(path2, "utf-8"));
|
|
3054
|
-
} catch {
|
|
3055
|
-
return null;
|
|
3056
|
-
}
|
|
3093
|
+
function readCurrentNextclawRemoteRuntimeState() {
|
|
3094
|
+
return readServiceState()?.remote ?? null;
|
|
3057
3095
|
}
|
|
3058
|
-
function
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3096
|
+
function resolveNextclawRemoteStatusSnapshot(config2) {
|
|
3097
|
+
return resolveRemoteStatusSnapshot({
|
|
3098
|
+
config: config2,
|
|
3099
|
+
currentRemoteState: readCurrentNextclawRemoteRuntimeState()
|
|
3100
|
+
});
|
|
3062
3101
|
}
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
if (existing?.deviceInstallId?.trim()) {
|
|
3069
|
-
return existing.deviceInstallId.trim();
|
|
3070
|
-
}
|
|
3071
|
-
const deviceInstallId = crypto.randomUUID();
|
|
3072
|
-
ensureDir(this.remoteDir);
|
|
3073
|
-
writeJsonFile(this.devicePath, { deviceInstallId });
|
|
3074
|
-
return deviceInstallId;
|
|
3075
|
-
}
|
|
3076
|
-
resolvePlatformAccess(opts) {
|
|
3077
|
-
const config2 = loadConfig8(getConfigPath3());
|
|
3078
|
-
const providers = config2.providers;
|
|
3079
|
-
const nextclawProvider = providers.nextclaw;
|
|
3080
|
-
const token = typeof nextclawProvider?.apiKey === "string" ? nextclawProvider.apiKey.trim() : "";
|
|
3081
|
-
if (!token) {
|
|
3082
|
-
throw new Error('NextClaw platform token is missing. Run "nextclaw login" first.');
|
|
3083
|
-
}
|
|
3084
|
-
const configuredApiBase = typeof nextclawProvider?.apiBase === "string" ? nextclawProvider.apiBase.trim() : "";
|
|
3085
|
-
const rawApiBase = typeof opts.apiBase === "string" && opts.apiBase.trim().length > 0 ? opts.apiBase.trim() : configuredApiBase;
|
|
3086
|
-
if (!rawApiBase) {
|
|
3087
|
-
throw new Error("Platform API base is missing. Pass --api-base or run nextclaw login.");
|
|
3088
|
-
}
|
|
3089
|
-
const { platformBase } = resolvePlatformApiBase({
|
|
3090
|
-
explicitApiBase: rawApiBase,
|
|
3091
|
-
requireConfigured: true
|
|
3092
|
-
});
|
|
3093
|
-
return { platformBase, token, config: config2 };
|
|
3102
|
+
|
|
3103
|
+
// src/cli/commands/remote.ts
|
|
3104
|
+
function normalizeOptionalString3(value) {
|
|
3105
|
+
if (typeof value !== "string") {
|
|
3106
|
+
return void 0;
|
|
3094
3107
|
}
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
}
|
|
3103
|
-
const configuredPort = typeof config2.ui?.port === "number" && Number.isFinite(config2.ui.port) ? config2.ui.port : 18791;
|
|
3104
|
-
return `http://127.0.0.1:${configuredPort}`;
|
|
3108
|
+
const trimmed = value.trim();
|
|
3109
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
3110
|
+
}
|
|
3111
|
+
function resolveConfiguredLocalOrigin(config2) {
|
|
3112
|
+
const state = readServiceState();
|
|
3113
|
+
if (state && isProcessRunning(state.pid) && Number.isFinite(state.uiPort)) {
|
|
3114
|
+
return `http://127.0.0.1:${state.uiPort}`;
|
|
3105
3115
|
}
|
|
3106
|
-
|
|
3116
|
+
const port = typeof config2.ui.port === "number" && Number.isFinite(config2.ui.port) ? config2.ui.port : 18791;
|
|
3117
|
+
return `http://127.0.0.1:${port}`;
|
|
3118
|
+
}
|
|
3119
|
+
async function probeLocalUi(localOrigin) {
|
|
3120
|
+
try {
|
|
3107
3121
|
const response = await fetch(`${localOrigin}/api/health`);
|
|
3108
3122
|
if (!response.ok) {
|
|
3109
|
-
|
|
3123
|
+
return { ok: false, detail: `health returned ${response.status}` };
|
|
3110
3124
|
}
|
|
3125
|
+
return { ok: true, detail: "health endpoint returned ok" };
|
|
3126
|
+
} catch (error) {
|
|
3127
|
+
return {
|
|
3128
|
+
ok: false,
|
|
3129
|
+
detail: error instanceof Error ? error.message : String(error)
|
|
3130
|
+
};
|
|
3111
3131
|
}
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
}
|
|
3131
|
-
return payload.data.device;
|
|
3132
|
+
}
|
|
3133
|
+
var RemoteCommands = class {
|
|
3134
|
+
enableConfig(opts = {}) {
|
|
3135
|
+
const config2 = loadConfig9(getConfigPath4());
|
|
3136
|
+
const next = {
|
|
3137
|
+
...config2,
|
|
3138
|
+
remote: {
|
|
3139
|
+
...config2.remote,
|
|
3140
|
+
enabled: true,
|
|
3141
|
+
...normalizeOptionalString3(opts.apiBase) ? { platformApiBase: normalizeOptionalString3(opts.apiBase) ?? "" } : {},
|
|
3142
|
+
...normalizeOptionalString3(opts.name) ? { deviceName: normalizeOptionalString3(opts.name) ?? "" } : {}
|
|
3143
|
+
}
|
|
3144
|
+
};
|
|
3145
|
+
saveConfig7(next);
|
|
3146
|
+
return {
|
|
3147
|
+
changed: config2.remote.enabled !== next.remote.enabled || config2.remote.platformApiBase !== next.remote.platformApiBase || config2.remote.deviceName !== next.remote.deviceName,
|
|
3148
|
+
config: next
|
|
3149
|
+
};
|
|
3132
3150
|
}
|
|
3133
|
-
|
|
3134
|
-
const
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3151
|
+
disableConfig() {
|
|
3152
|
+
const config2 = loadConfig9(getConfigPath4());
|
|
3153
|
+
const next = {
|
|
3154
|
+
...config2,
|
|
3155
|
+
remote: {
|
|
3156
|
+
...config2.remote,
|
|
3157
|
+
enabled: false
|
|
3138
3158
|
}
|
|
3159
|
+
};
|
|
3160
|
+
saveConfig7(next);
|
|
3161
|
+
return {
|
|
3162
|
+
changed: config2.remote.enabled !== next.remote.enabled,
|
|
3163
|
+
config: next
|
|
3164
|
+
};
|
|
3165
|
+
}
|
|
3166
|
+
async connect(opts = {}) {
|
|
3167
|
+
const connector = createNextclawRemoteConnector();
|
|
3168
|
+
await connector.run({
|
|
3169
|
+
...opts,
|
|
3170
|
+
mode: "foreground"
|
|
3139
3171
|
});
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
const bridgeCookie = await this.requestBridgeCookie(params.localOrigin);
|
|
3148
|
-
const url = new URL(params.frame.path, params.localOrigin);
|
|
3149
|
-
const headers = new Headers();
|
|
3150
|
-
for (const [key, value] of params.frame.headers) {
|
|
3151
|
-
const lower = key.toLowerCase();
|
|
3152
|
-
if ([
|
|
3153
|
-
"host",
|
|
3154
|
-
"connection",
|
|
3155
|
-
"content-length",
|
|
3156
|
-
"cookie",
|
|
3157
|
-
"x-forwarded-for",
|
|
3158
|
-
"x-forwarded-proto",
|
|
3159
|
-
"cf-connecting-ip"
|
|
3160
|
-
].includes(lower)) {
|
|
3161
|
-
continue;
|
|
3162
|
-
}
|
|
3163
|
-
headers.set(key, value);
|
|
3172
|
+
}
|
|
3173
|
+
async status(opts = {}) {
|
|
3174
|
+
const config2 = loadConfig9(getConfigPath4());
|
|
3175
|
+
const snapshot = resolveNextclawRemoteStatusSnapshot(config2);
|
|
3176
|
+
if (opts.json) {
|
|
3177
|
+
console.log(JSON.stringify(snapshot, null, 2));
|
|
3178
|
+
return;
|
|
3164
3179
|
}
|
|
3165
|
-
|
|
3166
|
-
|
|
3180
|
+
const runtime2 = snapshot.runtime;
|
|
3181
|
+
console.log("NextClaw Remote Status");
|
|
3182
|
+
console.log(`Enabled: ${snapshot.configuredEnabled ? "yes" : "no"}`);
|
|
3183
|
+
console.log(`Mode: ${runtime2?.mode ?? "service"}`);
|
|
3184
|
+
console.log(`State: ${runtime2?.state ?? "disabled"}`);
|
|
3185
|
+
console.log(`Device: ${runtime2?.deviceName ?? normalizeOptionalString3(config2.remote.deviceName) ?? hostname()}`);
|
|
3186
|
+
console.log(
|
|
3187
|
+
`Platform: ${runtime2?.platformBase ?? normalizeOptionalString3(config2.remote.platformApiBase) ?? normalizeOptionalString3(config2.providers.nextclaw?.apiBase) ?? "not set"}`
|
|
3188
|
+
);
|
|
3189
|
+
console.log(`Local origin: ${runtime2?.localOrigin ?? resolveConfiguredLocalOrigin(config2)}`);
|
|
3190
|
+
if (runtime2?.deviceId) {
|
|
3191
|
+
console.log(`Device ID: ${runtime2.deviceId}`);
|
|
3167
3192
|
}
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
|
|
3173
|
-
});
|
|
3174
|
-
const responseHeaders = Array.from(response.headers.entries()).filter(([key]) => {
|
|
3175
|
-
const lower = key.toLowerCase();
|
|
3176
|
-
return !["content-length", "connection", "transfer-encoding", "set-cookie"].includes(lower);
|
|
3177
|
-
});
|
|
3178
|
-
const contentType = response.headers.get("content-type")?.toLowerCase() ?? "";
|
|
3179
|
-
if (response.body && contentType.startsWith("text/event-stream")) {
|
|
3180
|
-
params.socket.send(JSON.stringify({
|
|
3181
|
-
type: "response.start",
|
|
3182
|
-
requestId: params.frame.requestId,
|
|
3183
|
-
status: response.status,
|
|
3184
|
-
headers: responseHeaders
|
|
3185
|
-
}));
|
|
3186
|
-
const reader = response.body.getReader();
|
|
3187
|
-
try {
|
|
3188
|
-
while (true) {
|
|
3189
|
-
const { value, done } = await reader.read();
|
|
3190
|
-
if (done) {
|
|
3191
|
-
break;
|
|
3192
|
-
}
|
|
3193
|
-
if (value && value.length > 0) {
|
|
3194
|
-
params.socket.send(JSON.stringify({
|
|
3195
|
-
type: "response.chunk",
|
|
3196
|
-
requestId: params.frame.requestId,
|
|
3197
|
-
bodyBase64: encodeBase64(value)
|
|
3198
|
-
}));
|
|
3199
|
-
}
|
|
3200
|
-
}
|
|
3201
|
-
} finally {
|
|
3202
|
-
reader.releaseLock();
|
|
3203
|
-
}
|
|
3204
|
-
params.socket.send(JSON.stringify({
|
|
3205
|
-
type: "response.end",
|
|
3206
|
-
requestId: params.frame.requestId
|
|
3207
|
-
}));
|
|
3208
|
-
return;
|
|
3193
|
+
if (runtime2?.lastConnectedAt) {
|
|
3194
|
+
console.log(`Last connected: ${runtime2.lastConnectedAt}`);
|
|
3195
|
+
}
|
|
3196
|
+
if (runtime2?.lastError) {
|
|
3197
|
+
console.log(`Last error: ${runtime2.lastError}`);
|
|
3209
3198
|
}
|
|
3210
|
-
const responseBody = response.body ? new Uint8Array(await response.arrayBuffer()) : new Uint8Array();
|
|
3211
|
-
params.socket.send(JSON.stringify({
|
|
3212
|
-
type: "response",
|
|
3213
|
-
requestId: params.frame.requestId,
|
|
3214
|
-
status: response.status,
|
|
3215
|
-
headers: responseHeaders,
|
|
3216
|
-
bodyBase64: encodeBase64(responseBody)
|
|
3217
|
-
}));
|
|
3218
|
-
}
|
|
3219
|
-
async connectOnce(params) {
|
|
3220
|
-
await new Promise((resolve13, reject) => {
|
|
3221
|
-
const socket = new WebSocket(params.wsUrl);
|
|
3222
|
-
const pingTimer = setInterval(() => {
|
|
3223
|
-
if (socket.readyState === WebSocket.OPEN) {
|
|
3224
|
-
socket.send(JSON.stringify({ type: "ping", at: (/* @__PURE__ */ new Date()).toISOString() }));
|
|
3225
|
-
}
|
|
3226
|
-
}, 15e3);
|
|
3227
|
-
socket.addEventListener("open", () => {
|
|
3228
|
-
console.log(`\u2713 Remote connector connected: ${params.wsUrl}`);
|
|
3229
|
-
});
|
|
3230
|
-
socket.addEventListener("message", (event) => {
|
|
3231
|
-
void (async () => {
|
|
3232
|
-
let frame = null;
|
|
3233
|
-
try {
|
|
3234
|
-
frame = JSON.parse(String(event.data ?? ""));
|
|
3235
|
-
} catch {
|
|
3236
|
-
return;
|
|
3237
|
-
}
|
|
3238
|
-
if (!frame || frame.type !== "request") {
|
|
3239
|
-
return;
|
|
3240
|
-
}
|
|
3241
|
-
try {
|
|
3242
|
-
await this.handleRelayRequest({ frame, localOrigin: params.localOrigin, socket });
|
|
3243
|
-
} catch (error) {
|
|
3244
|
-
socket.send(JSON.stringify({
|
|
3245
|
-
type: "response.error",
|
|
3246
|
-
requestId: frame.requestId,
|
|
3247
|
-
message: error instanceof Error ? error.message : String(error)
|
|
3248
|
-
}));
|
|
3249
|
-
}
|
|
3250
|
-
})();
|
|
3251
|
-
});
|
|
3252
|
-
socket.addEventListener("close", () => {
|
|
3253
|
-
clearInterval(pingTimer);
|
|
3254
|
-
resolve13();
|
|
3255
|
-
});
|
|
3256
|
-
socket.addEventListener("error", () => {
|
|
3257
|
-
clearInterval(pingTimer);
|
|
3258
|
-
reject(new Error("Remote connector websocket failed."));
|
|
3259
|
-
});
|
|
3260
|
-
});
|
|
3261
3199
|
}
|
|
3262
|
-
async
|
|
3263
|
-
const
|
|
3264
|
-
const
|
|
3265
|
-
|
|
3266
|
-
const
|
|
3267
|
-
const
|
|
3268
|
-
const
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
}
|
|
3285
|
-
|
|
3286
|
-
|
|
3200
|
+
async doctor(opts = {}) {
|
|
3201
|
+
const config2 = loadConfig9(getConfigPath4());
|
|
3202
|
+
const snapshot = resolveNextclawRemoteStatusSnapshot(config2);
|
|
3203
|
+
const localOrigin = resolveConfiguredLocalOrigin(config2);
|
|
3204
|
+
const localUi = await probeLocalUi(localOrigin);
|
|
3205
|
+
const token = normalizeOptionalString3(config2.providers.nextclaw?.apiKey);
|
|
3206
|
+
const platformApiBase = normalizeOptionalString3(config2.remote.platformApiBase) ?? normalizeOptionalString3(config2.providers.nextclaw?.apiBase);
|
|
3207
|
+
const checks = [
|
|
3208
|
+
{
|
|
3209
|
+
name: "remote-enabled",
|
|
3210
|
+
ok: snapshot.configuredEnabled,
|
|
3211
|
+
detail: snapshot.configuredEnabled ? "enabled in config" : "disabled in config"
|
|
3212
|
+
},
|
|
3213
|
+
{
|
|
3214
|
+
name: "platform-token",
|
|
3215
|
+
ok: Boolean(token),
|
|
3216
|
+
detail: token ? "token configured" : 'run "nextclaw login" first'
|
|
3217
|
+
},
|
|
3218
|
+
{
|
|
3219
|
+
name: "platform-api-base",
|
|
3220
|
+
ok: Boolean(platformApiBase),
|
|
3221
|
+
detail: platformApiBase ?? "set remote.platformApiBase or login with --api-base"
|
|
3222
|
+
},
|
|
3223
|
+
{
|
|
3224
|
+
name: "local-ui",
|
|
3225
|
+
ok: localUi.ok,
|
|
3226
|
+
detail: `${localOrigin} (${localUi.detail})`
|
|
3227
|
+
},
|
|
3228
|
+
{
|
|
3229
|
+
name: "service-runtime",
|
|
3230
|
+
ok: snapshot.runtime?.state === "connected",
|
|
3231
|
+
detail: snapshot.runtime ? snapshot.runtime.state : "no managed remote runtime detected"
|
|
3287
3232
|
}
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3233
|
+
];
|
|
3234
|
+
if (opts.json) {
|
|
3235
|
+
console.log(JSON.stringify({ generatedAt: (/* @__PURE__ */ new Date()).toISOString(), checks, snapshot }, null, 2));
|
|
3236
|
+
return;
|
|
3237
|
+
}
|
|
3238
|
+
console.log("NextClaw Remote Doctor");
|
|
3239
|
+
for (const check of checks) {
|
|
3240
|
+
console.log(`${check.ok ? "\u2713" : "\u2717"} ${check.name}: ${check.detail}`);
|
|
3241
|
+
}
|
|
3291
3242
|
}
|
|
3292
3243
|
};
|
|
3293
3244
|
|
|
3294
3245
|
// src/cli/commands/diagnostics.ts
|
|
3295
3246
|
import { createServer as createNetServer } from "net";
|
|
3296
|
-
import { existsSync as
|
|
3247
|
+
import { existsSync as existsSync6, readFileSync as readFileSync6 } from "fs";
|
|
3297
3248
|
import { resolve as resolve8 } from "path";
|
|
3298
3249
|
import {
|
|
3299
|
-
APP_NAME,
|
|
3300
|
-
getConfigPath as
|
|
3250
|
+
APP_NAME as APP_NAME2,
|
|
3251
|
+
getConfigPath as getConfigPath5,
|
|
3301
3252
|
getDataDir as getDataDir5,
|
|
3302
3253
|
getWorkspacePath as getWorkspacePath4,
|
|
3303
3254
|
hasSecretRef,
|
|
3304
|
-
loadConfig as
|
|
3255
|
+
loadConfig as loadConfig10
|
|
3305
3256
|
} from "@nextclaw/core";
|
|
3306
3257
|
import { listBuiltinProviders } from "@nextclaw/runtime";
|
|
3258
|
+
|
|
3259
|
+
// src/cli/commands/diagnostics-render.ts
|
|
3260
|
+
import { APP_NAME } from "@nextclaw/core";
|
|
3261
|
+
function printStatusReport(params) {
|
|
3262
|
+
const { logo, report, verbose } = params;
|
|
3263
|
+
console.log(`${logo} ${APP_NAME} Status`);
|
|
3264
|
+
console.log(`Level: ${report.level}`);
|
|
3265
|
+
console.log(`Generated: ${report.generatedAt}`);
|
|
3266
|
+
console.log("");
|
|
3267
|
+
printProcessSection(report);
|
|
3268
|
+
printEndpointSection(report);
|
|
3269
|
+
printProviderSection(report);
|
|
3270
|
+
printTextList("Fix actions", report.fixActions);
|
|
3271
|
+
printTextList("Issues", report.issues);
|
|
3272
|
+
printTextList("Recommendations", report.recommendations);
|
|
3273
|
+
if (verbose && report.logTail.length > 0) {
|
|
3274
|
+
console.log("");
|
|
3275
|
+
console.log("Recent logs:");
|
|
3276
|
+
for (const line of report.logTail) {
|
|
3277
|
+
console.log(line);
|
|
3278
|
+
}
|
|
3279
|
+
}
|
|
3280
|
+
}
|
|
3281
|
+
function printProcessSection(report) {
|
|
3282
|
+
const processLabel = report.process.running ? `running (PID ${report.process.pid})` : report.process.staleState ? "stale-state" : "stopped";
|
|
3283
|
+
console.log(`Process: ${processLabel}`);
|
|
3284
|
+
console.log(`State file: ${report.serviceStatePath} ${report.serviceStateExists ? "\u2713" : "\u2717"}`);
|
|
3285
|
+
if (report.process.startedAt) {
|
|
3286
|
+
console.log(`Started: ${report.process.startedAt}`);
|
|
3287
|
+
}
|
|
3288
|
+
console.log(`Managed health: ${report.health.managed.state} (${report.health.managed.detail})`);
|
|
3289
|
+
if (!report.process.running) {
|
|
3290
|
+
console.log(`Configured health: ${report.health.configured.state} (${report.health.configured.detail})`);
|
|
3291
|
+
}
|
|
3292
|
+
}
|
|
3293
|
+
function printEndpointSection(report) {
|
|
3294
|
+
console.log(`UI: ${report.endpoints.uiUrl ?? report.endpoints.configuredUiUrl}`);
|
|
3295
|
+
console.log(`API: ${report.endpoints.apiUrl ?? report.endpoints.configuredApiUrl}`);
|
|
3296
|
+
console.log(`Remote: ${report.remote.configuredEnabled ? "enabled" : "disabled"}${report.remote.runtime ? ` (${report.remote.runtime.state})` : ""}`);
|
|
3297
|
+
if (report.remote.runtime?.deviceName) {
|
|
3298
|
+
console.log(`Remote device: ${report.remote.runtime.deviceName}`);
|
|
3299
|
+
}
|
|
3300
|
+
if (report.remote.runtime?.platformBase) {
|
|
3301
|
+
console.log(`Remote platform: ${report.remote.runtime.platformBase}`);
|
|
3302
|
+
}
|
|
3303
|
+
if (report.remote.runtime?.lastError) {
|
|
3304
|
+
console.log(`Remote error: ${report.remote.runtime.lastError}`);
|
|
3305
|
+
}
|
|
3306
|
+
console.log(`Config: ${report.configPath} ${report.configExists ? "\u2713" : "\u2717"}`);
|
|
3307
|
+
console.log(`Workspace: ${report.workspacePath} ${report.workspaceExists ? "\u2713" : "\u2717"}`);
|
|
3308
|
+
console.log(`Model: ${report.model}`);
|
|
3309
|
+
}
|
|
3310
|
+
function printProviderSection(report) {
|
|
3311
|
+
for (const provider of report.providers) {
|
|
3312
|
+
console.log(`${provider.name}: ${provider.configured ? "\u2713" : "not set"}${provider.detail ? ` (${provider.detail})` : ""}`);
|
|
3313
|
+
}
|
|
3314
|
+
}
|
|
3315
|
+
function printDoctorReport(params) {
|
|
3316
|
+
console.log(`${params.logo} ${APP_NAME} Doctor`);
|
|
3317
|
+
console.log(`Generated: ${params.generatedAt}`);
|
|
3318
|
+
console.log("");
|
|
3319
|
+
for (const check of params.checks) {
|
|
3320
|
+
const icon = check.status === "pass" ? "\u2713" : check.status === "warn" ? "!" : "\u2717";
|
|
3321
|
+
console.log(`${icon} ${check.name}: ${check.detail}`);
|
|
3322
|
+
}
|
|
3323
|
+
printTextList("Recommendations", params.recommendations);
|
|
3324
|
+
if (params.verbose && params.logTail.length > 0) {
|
|
3325
|
+
console.log("");
|
|
3326
|
+
console.log("Recent logs:");
|
|
3327
|
+
for (const line of params.logTail) {
|
|
3328
|
+
console.log(line);
|
|
3329
|
+
}
|
|
3330
|
+
}
|
|
3331
|
+
}
|
|
3332
|
+
function printTextList(title, items) {
|
|
3333
|
+
if (items.length === 0) {
|
|
3334
|
+
return;
|
|
3335
|
+
}
|
|
3336
|
+
console.log("");
|
|
3337
|
+
console.log(`${title}:`);
|
|
3338
|
+
for (const item of items) {
|
|
3339
|
+
console.log(`- ${item}`);
|
|
3340
|
+
}
|
|
3341
|
+
}
|
|
3342
|
+
|
|
3343
|
+
// src/cli/commands/diagnostics.ts
|
|
3307
3344
|
var DiagnosticsCommands = class {
|
|
3308
3345
|
constructor(deps) {
|
|
3309
3346
|
this.deps = deps;
|
|
@@ -3318,56 +3355,7 @@ var DiagnosticsCommands = class {
|
|
|
3318
3355
|
process.exitCode = 0;
|
|
3319
3356
|
return;
|
|
3320
3357
|
}
|
|
3321
|
-
|
|
3322
|
-
console.log(`Level: ${report.level}`);
|
|
3323
|
-
console.log(`Generated: ${report.generatedAt}`);
|
|
3324
|
-
console.log("");
|
|
3325
|
-
const processLabel = report.process.running ? `running (PID ${report.process.pid})` : report.process.staleState ? "stale-state" : "stopped";
|
|
3326
|
-
console.log(`Process: ${processLabel}`);
|
|
3327
|
-
console.log(`State file: ${report.serviceStatePath} ${report.serviceStateExists ? "\u2713" : "\u2717"}`);
|
|
3328
|
-
if (report.process.startedAt) {
|
|
3329
|
-
console.log(`Started: ${report.process.startedAt}`);
|
|
3330
|
-
}
|
|
3331
|
-
console.log(`Managed health: ${report.health.managed.state} (${report.health.managed.detail})`);
|
|
3332
|
-
if (!report.process.running) {
|
|
3333
|
-
console.log(`Configured health: ${report.health.configured.state} (${report.health.configured.detail})`);
|
|
3334
|
-
}
|
|
3335
|
-
console.log(`UI: ${report.endpoints.uiUrl ?? report.endpoints.configuredUiUrl}`);
|
|
3336
|
-
console.log(`API: ${report.endpoints.apiUrl ?? report.endpoints.configuredApiUrl}`);
|
|
3337
|
-
console.log(`Config: ${report.configPath} ${report.configExists ? "\u2713" : "\u2717"}`);
|
|
3338
|
-
console.log(`Workspace: ${report.workspacePath} ${report.workspaceExists ? "\u2713" : "\u2717"}`);
|
|
3339
|
-
console.log(`Model: ${report.model}`);
|
|
3340
|
-
for (const provider of report.providers) {
|
|
3341
|
-
console.log(`${provider.name}: ${provider.configured ? "\u2713" : "not set"}${provider.detail ? ` (${provider.detail})` : ""}`);
|
|
3342
|
-
}
|
|
3343
|
-
if (report.fixActions.length > 0) {
|
|
3344
|
-
console.log("");
|
|
3345
|
-
console.log("Fix actions:");
|
|
3346
|
-
for (const action of report.fixActions) {
|
|
3347
|
-
console.log(`- ${action}`);
|
|
3348
|
-
}
|
|
3349
|
-
}
|
|
3350
|
-
if (report.issues.length > 0) {
|
|
3351
|
-
console.log("");
|
|
3352
|
-
console.log("Issues:");
|
|
3353
|
-
for (const issue of report.issues) {
|
|
3354
|
-
console.log(`- ${issue}`);
|
|
3355
|
-
}
|
|
3356
|
-
}
|
|
3357
|
-
if (report.recommendations.length > 0) {
|
|
3358
|
-
console.log("");
|
|
3359
|
-
console.log("Recommendations:");
|
|
3360
|
-
for (const recommendation of report.recommendations) {
|
|
3361
|
-
console.log(`- ${recommendation}`);
|
|
3362
|
-
}
|
|
3363
|
-
}
|
|
3364
|
-
if (opts.verbose && report.logTail.length > 0) {
|
|
3365
|
-
console.log("");
|
|
3366
|
-
console.log("Recent logs:");
|
|
3367
|
-
for (const line of report.logTail) {
|
|
3368
|
-
console.log(line);
|
|
3369
|
-
}
|
|
3370
|
-
}
|
|
3358
|
+
printStatusReport({ logo: this.deps.logo, report, verbose: Boolean(opts.verbose) });
|
|
3371
3359
|
process.exitCode = 0;
|
|
3372
3360
|
}
|
|
3373
3361
|
async doctor(opts = {}) {
|
|
@@ -3438,32 +3426,19 @@ var DiagnosticsCommands = class {
|
|
|
3438
3426
|
process.exitCode = exitCode;
|
|
3439
3427
|
return;
|
|
3440
3428
|
}
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
console.log("");
|
|
3450
|
-
console.log("Recommendations:");
|
|
3451
|
-
for (const recommendation of report.recommendations) {
|
|
3452
|
-
console.log(`- ${recommendation}`);
|
|
3453
|
-
}
|
|
3454
|
-
}
|
|
3455
|
-
if (opts.verbose && report.logTail.length > 0) {
|
|
3456
|
-
console.log("");
|
|
3457
|
-
console.log("Recent logs:");
|
|
3458
|
-
for (const line of report.logTail) {
|
|
3459
|
-
console.log(line);
|
|
3460
|
-
}
|
|
3461
|
-
}
|
|
3429
|
+
printDoctorReport({
|
|
3430
|
+
logo: this.deps.logo,
|
|
3431
|
+
generatedAt: report.generatedAt,
|
|
3432
|
+
checks,
|
|
3433
|
+
recommendations: report.recommendations,
|
|
3434
|
+
verbose: Boolean(opts.verbose),
|
|
3435
|
+
logTail: report.logTail
|
|
3436
|
+
});
|
|
3462
3437
|
process.exitCode = exitCode;
|
|
3463
3438
|
}
|
|
3464
3439
|
async collectRuntimeStatus(params) {
|
|
3465
|
-
const configPath =
|
|
3466
|
-
const config2 =
|
|
3440
|
+
const configPath = getConfigPath5();
|
|
3441
|
+
const config2 = loadConfig10();
|
|
3467
3442
|
const workspacePath = getWorkspacePath4(config2.agents.defaults.workspace);
|
|
3468
3443
|
const serviceStatePath = resolve8(getDataDir5(), "run", "service.json");
|
|
3469
3444
|
const fixActions = [];
|
|
@@ -3483,72 +3458,36 @@ var DiagnosticsCommands = class {
|
|
|
3483
3458
|
const managedApiUrl = serviceState?.apiUrl ?? null;
|
|
3484
3459
|
const managedHealth = running && managedApiUrl ? await this.probeApiHealth(`${managedApiUrl}/health`) : { state: "unreachable", detail: "service not running" };
|
|
3485
3460
|
const configuredHealth = await this.probeApiHealth(`${configuredApiUrl}/health`, 900);
|
|
3461
|
+
const remote = resolveNextclawRemoteStatusSnapshot(config2);
|
|
3486
3462
|
const orphanSuspected = !running && configuredHealth.state === "ok";
|
|
3487
|
-
const providers =
|
|
3488
|
-
const provider = config2.providers[spec.name];
|
|
3489
|
-
const apiKeyRefSet = hasSecretRef(config2, `providers.${spec.name}.apiKey`);
|
|
3490
|
-
if (!provider) {
|
|
3491
|
-
return { name: spec.displayName ?? spec.name, configured: false, detail: "missing config" };
|
|
3492
|
-
}
|
|
3493
|
-
if (spec.isLocal) {
|
|
3494
|
-
return {
|
|
3495
|
-
name: spec.displayName ?? spec.name,
|
|
3496
|
-
configured: Boolean(provider.apiBase),
|
|
3497
|
-
detail: provider.apiBase ? provider.apiBase : "apiBase not set"
|
|
3498
|
-
};
|
|
3499
|
-
}
|
|
3500
|
-
return {
|
|
3501
|
-
name: spec.displayName ?? spec.name,
|
|
3502
|
-
configured: Boolean(provider.apiKey) || apiKeyRefSet,
|
|
3503
|
-
detail: provider.apiKey ? "apiKey set" : apiKeyRefSet ? "apiKey ref set" : "apiKey not set"
|
|
3504
|
-
};
|
|
3505
|
-
});
|
|
3463
|
+
const providers = this.listProviderStatuses(config2);
|
|
3506
3464
|
const issues = [];
|
|
3507
3465
|
const recommendations = [];
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
|
|
3515
|
-
|
|
3516
|
-
|
|
3517
|
-
issues
|
|
3518
|
-
recommendations
|
|
3519
|
-
}
|
|
3520
|
-
if (running && managedHealth.state !== "ok") {
|
|
3521
|
-
issues.push(`Managed service health check failed: ${managedHealth.detail}`);
|
|
3522
|
-
recommendations.push(`Check logs at ${serviceState?.logPath ?? resolveServiceLogPath()}.`);
|
|
3523
|
-
}
|
|
3524
|
-
if (running && serviceState?.startupState === "degraded" && managedHealth.state !== "ok") {
|
|
3525
|
-
const startupHint = serviceState.startupLastProbeError ? ` (${serviceState.startupLastProbeError})` : "";
|
|
3526
|
-
issues.push(`Service is in degraded startup state${startupHint}.`);
|
|
3527
|
-
recommendations.push(`Wait and re-check ${APP_NAME} status; if it does not recover, inspect logs and restart.`);
|
|
3528
|
-
}
|
|
3529
|
-
if (!running) {
|
|
3530
|
-
recommendations.push(`Run ${APP_NAME} start to launch the service.`);
|
|
3531
|
-
}
|
|
3532
|
-
if (orphanSuspected) {
|
|
3533
|
-
issues.push("A service appears healthy on configured API endpoint, but state is missing/stale.");
|
|
3534
|
-
recommendations.push("Another process may be occupying the UI port; stop it or use --ui-port with a free port.");
|
|
3535
|
-
}
|
|
3536
|
-
if (!providers.some((provider) => provider.configured)) {
|
|
3537
|
-
recommendations.push("Configure at least one provider API key in UI or config before expecting agent replies.");
|
|
3538
|
-
}
|
|
3466
|
+
this.collectRuntimeIssues({
|
|
3467
|
+
configPath,
|
|
3468
|
+
workspacePath,
|
|
3469
|
+
staleState,
|
|
3470
|
+
running,
|
|
3471
|
+
managedHealth,
|
|
3472
|
+
serviceState,
|
|
3473
|
+
orphanSuspected,
|
|
3474
|
+
providers,
|
|
3475
|
+
issues,
|
|
3476
|
+
recommendations
|
|
3477
|
+
});
|
|
3539
3478
|
const logTail = params.verbose ? this.readLogTail(serviceState?.logPath ?? resolveServiceLogPath(), 25) : [];
|
|
3540
3479
|
const level = running ? managedHealth.state === "ok" ? issues.length > 0 ? "degraded" : "healthy" : "degraded" : "stopped";
|
|
3541
3480
|
const exitCode = 0;
|
|
3542
3481
|
return {
|
|
3543
3482
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3544
3483
|
configPath,
|
|
3545
|
-
configExists:
|
|
3484
|
+
configExists: existsSync6(configPath),
|
|
3546
3485
|
workspacePath,
|
|
3547
|
-
workspaceExists:
|
|
3486
|
+
workspaceExists: existsSync6(workspacePath),
|
|
3548
3487
|
model: config2.agents.defaults.model,
|
|
3549
3488
|
providers,
|
|
3550
3489
|
serviceStatePath,
|
|
3551
|
-
serviceStateExists:
|
|
3490
|
+
serviceStateExists: existsSync6(serviceStatePath),
|
|
3552
3491
|
fixActions,
|
|
3553
3492
|
process: {
|
|
3554
3493
|
managedByState,
|
|
@@ -3571,6 +3510,7 @@ var DiagnosticsCommands = class {
|
|
|
3571
3510
|
issues,
|
|
3572
3511
|
recommendations,
|
|
3573
3512
|
logTail,
|
|
3513
|
+
remote,
|
|
3574
3514
|
level,
|
|
3575
3515
|
exitCode
|
|
3576
3516
|
};
|
|
@@ -3597,12 +3537,66 @@ var DiagnosticsCommands = class {
|
|
|
3597
3537
|
clearTimeout(timer);
|
|
3598
3538
|
}
|
|
3599
3539
|
}
|
|
3540
|
+
listProviderStatuses(config2) {
|
|
3541
|
+
return listBuiltinProviders().map((spec) => {
|
|
3542
|
+
const provider = config2.providers[spec.name];
|
|
3543
|
+
const apiKeyRefSet = hasSecretRef(config2, `providers.${spec.name}.apiKey`);
|
|
3544
|
+
if (!provider) {
|
|
3545
|
+
return { name: spec.displayName ?? spec.name, configured: false, detail: "missing config" };
|
|
3546
|
+
}
|
|
3547
|
+
if (spec.isLocal) {
|
|
3548
|
+
return {
|
|
3549
|
+
name: spec.displayName ?? spec.name,
|
|
3550
|
+
configured: Boolean(provider.apiBase),
|
|
3551
|
+
detail: provider.apiBase ? provider.apiBase : "apiBase not set"
|
|
3552
|
+
};
|
|
3553
|
+
}
|
|
3554
|
+
return {
|
|
3555
|
+
name: spec.displayName ?? spec.name,
|
|
3556
|
+
configured: Boolean(provider.apiKey) || apiKeyRefSet,
|
|
3557
|
+
detail: provider.apiKey ? "apiKey set" : apiKeyRefSet ? "apiKey ref set" : "apiKey not set"
|
|
3558
|
+
};
|
|
3559
|
+
});
|
|
3560
|
+
}
|
|
3561
|
+
collectRuntimeIssues(params) {
|
|
3562
|
+
if (!existsSync6(params.configPath)) {
|
|
3563
|
+
params.issues.push("Config file is missing.");
|
|
3564
|
+
params.recommendations.push(`Run ${APP_NAME2} init to create config files.`);
|
|
3565
|
+
}
|
|
3566
|
+
if (!existsSync6(params.workspacePath)) {
|
|
3567
|
+
params.issues.push("Workspace directory does not exist.");
|
|
3568
|
+
params.recommendations.push(`Run ${APP_NAME2} init to create workspace templates.`);
|
|
3569
|
+
}
|
|
3570
|
+
if (params.staleState) {
|
|
3571
|
+
params.issues.push("Service state is stale (state exists but process is not running).");
|
|
3572
|
+
params.recommendations.push(`Run ${APP_NAME2} status --fix to clean stale state.`);
|
|
3573
|
+
}
|
|
3574
|
+
if (params.running && params.managedHealth.state !== "ok") {
|
|
3575
|
+
params.issues.push(`Managed service health check failed: ${params.managedHealth.detail}`);
|
|
3576
|
+
params.recommendations.push(`Check logs at ${params.serviceState?.logPath ?? resolveServiceLogPath()}.`);
|
|
3577
|
+
}
|
|
3578
|
+
if (params.running && params.serviceState?.startupState === "degraded" && params.managedHealth.state !== "ok") {
|
|
3579
|
+
const startupHint = params.serviceState.startupLastProbeError ? ` (${params.serviceState.startupLastProbeError})` : "";
|
|
3580
|
+
params.issues.push(`Service is in degraded startup state${startupHint}.`);
|
|
3581
|
+
params.recommendations.push(`Wait and re-check ${APP_NAME2} status; if it does not recover, inspect logs and restart.`);
|
|
3582
|
+
}
|
|
3583
|
+
if (!params.running) {
|
|
3584
|
+
params.recommendations.push(`Run ${APP_NAME2} start to launch the service.`);
|
|
3585
|
+
}
|
|
3586
|
+
if (params.orphanSuspected) {
|
|
3587
|
+
params.issues.push("A service appears healthy on configured API endpoint, but state is missing/stale.");
|
|
3588
|
+
params.recommendations.push("Another process may be occupying the UI port; stop it or use --ui-port with a free port.");
|
|
3589
|
+
}
|
|
3590
|
+
if (!params.providers.some((provider) => provider.configured)) {
|
|
3591
|
+
params.recommendations.push("Configure at least one provider API key in UI or config before expecting agent replies.");
|
|
3592
|
+
}
|
|
3593
|
+
}
|
|
3600
3594
|
readLogTail(path2, maxLines = 25) {
|
|
3601
|
-
if (!
|
|
3595
|
+
if (!existsSync6(path2)) {
|
|
3602
3596
|
return [];
|
|
3603
3597
|
}
|
|
3604
3598
|
try {
|
|
3605
|
-
const lines =
|
|
3599
|
+
const lines = readFileSync6(path2, "utf-8").split(/\r?\n/).filter(Boolean);
|
|
3606
3600
|
if (lines.length <= maxLines) {
|
|
3607
3601
|
return lines;
|
|
3608
3602
|
}
|
|
@@ -3642,8 +3636,8 @@ import {
|
|
|
3642
3636
|
stopPluginChannelGateways as stopPluginChannelGateways2
|
|
3643
3637
|
} from "@nextclaw/openclaw-compat";
|
|
3644
3638
|
import { startUiServer } from "@nextclaw/server";
|
|
3645
|
-
import { appendFileSync, closeSync, cpSync as cpSync2, existsSync as
|
|
3646
|
-
import { dirname as
|
|
3639
|
+
import { appendFileSync, closeSync, cpSync as cpSync2, existsSync as existsSync10, mkdirSync as mkdirSync5, openSync } from "fs";
|
|
3640
|
+
import { dirname as dirname2, join as join6, resolve as resolve10 } from "path";
|
|
3647
3641
|
import { spawn as spawn2 } from "child_process";
|
|
3648
3642
|
import { request as httpRequest } from "http";
|
|
3649
3643
|
import { request as httpsRequest } from "https";
|
|
@@ -3652,7 +3646,7 @@ import chokidar from "chokidar";
|
|
|
3652
3646
|
|
|
3653
3647
|
// src/cli/gateway/controller.ts
|
|
3654
3648
|
import { createHash } from "crypto";
|
|
3655
|
-
import { existsSync as
|
|
3649
|
+
import { existsSync as existsSync7, readFileSync as readFileSync7 } from "fs";
|
|
3656
3650
|
import {
|
|
3657
3651
|
buildConfigSchema,
|
|
3658
3652
|
ConfigSchema,
|
|
@@ -3660,12 +3654,12 @@ import {
|
|
|
3660
3654
|
redactConfigObject
|
|
3661
3655
|
} from "@nextclaw/core";
|
|
3662
3656
|
var hashRaw = (raw) => createHash("sha256").update(raw).digest("hex");
|
|
3663
|
-
var readConfigSnapshot = (
|
|
3664
|
-
const path2 =
|
|
3657
|
+
var readConfigSnapshot = (getConfigPath8) => {
|
|
3658
|
+
const path2 = getConfigPath8();
|
|
3665
3659
|
let raw = "";
|
|
3666
3660
|
let parsed = {};
|
|
3667
|
-
if (
|
|
3668
|
-
raw =
|
|
3661
|
+
if (existsSync7(path2)) {
|
|
3662
|
+
raw = readFileSync7(path2, "utf-8");
|
|
3669
3663
|
try {
|
|
3670
3664
|
parsed = JSON.parse(raw);
|
|
3671
3665
|
} catch {
|
|
@@ -3772,11 +3766,11 @@ var GatewayControllerImpl = class {
|
|
|
3772
3766
|
await this.deps.requestRestart(options);
|
|
3773
3767
|
return;
|
|
3774
3768
|
}
|
|
3775
|
-
const
|
|
3769
|
+
const delay = typeof options?.delayMs === "number" && Number.isFinite(options.delayMs) ? Math.max(0, options.delayMs) : 100;
|
|
3776
3770
|
console.log(`Gateway restart requested via tool${options?.reason ? ` (${options.reason})` : ""}.`);
|
|
3777
3771
|
setTimeout(() => {
|
|
3778
3772
|
process.exit(0);
|
|
3779
|
-
},
|
|
3773
|
+
}, delay);
|
|
3780
3774
|
}
|
|
3781
3775
|
status() {
|
|
3782
3776
|
return {
|
|
@@ -4122,9 +4116,9 @@ var MissingProvider = class extends LLMProvider {
|
|
|
4122
4116
|
};
|
|
4123
4117
|
|
|
4124
4118
|
// src/cli/commands/service-marketplace-installer.ts
|
|
4125
|
-
import { getWorkspacePath as getWorkspacePath5, loadConfig as
|
|
4126
|
-
import { existsSync as
|
|
4127
|
-
import { join as
|
|
4119
|
+
import { getWorkspacePath as getWorkspacePath5, loadConfig as loadConfig12 } from "@nextclaw/core";
|
|
4120
|
+
import { existsSync as existsSync8, rmSync as rmSync4 } from "fs";
|
|
4121
|
+
import { join as join4 } from "path";
|
|
4128
4122
|
|
|
4129
4123
|
// src/cli/commands/service-marketplace-helpers.ts
|
|
4130
4124
|
var containsAbsoluteFsPath = (line) => {
|
|
@@ -4172,7 +4166,7 @@ var buildMarketplaceSkillInstallArgs = (params) => {
|
|
|
4172
4166
|
};
|
|
4173
4167
|
|
|
4174
4168
|
// src/cli/commands/service-mcp-marketplace-ops.ts
|
|
4175
|
-
import { loadConfig as
|
|
4169
|
+
import { loadConfig as loadConfig11, saveConfig as saveConfig8 } from "@nextclaw/core";
|
|
4176
4170
|
import { McpDoctorFacade as McpDoctorFacade2, McpMutationService as McpMutationService2 } from "@nextclaw/mcp";
|
|
4177
4171
|
var ServiceMcpMarketplaceOps = class {
|
|
4178
4172
|
constructor(options) {
|
|
@@ -4240,13 +4234,13 @@ var ServiceMcpMarketplaceOps = class {
|
|
|
4240
4234
|
}
|
|
4241
4235
|
createMutationService() {
|
|
4242
4236
|
return new McpMutationService2({
|
|
4243
|
-
getConfig: () =>
|
|
4244
|
-
saveConfig: (config2) =>
|
|
4237
|
+
getConfig: () => loadConfig11(),
|
|
4238
|
+
saveConfig: (config2) => saveConfig8(config2)
|
|
4245
4239
|
});
|
|
4246
4240
|
}
|
|
4247
4241
|
createDoctorFacade() {
|
|
4248
4242
|
return new McpDoctorFacade2({
|
|
4249
|
-
getConfig: () =>
|
|
4243
|
+
getConfig: () => loadConfig11()
|
|
4250
4244
|
});
|
|
4251
4245
|
}
|
|
4252
4246
|
};
|
|
@@ -4287,7 +4281,7 @@ var ServiceMarketplaceInstaller = class {
|
|
|
4287
4281
|
if (params.kind && params.kind !== "marketplace") {
|
|
4288
4282
|
throw new Error(`Unsupported marketplace skill kind: ${params.kind}`);
|
|
4289
4283
|
}
|
|
4290
|
-
const workspace = getWorkspacePath5(
|
|
4284
|
+
const workspace = getWorkspacePath5(loadConfig12().agents.defaults.workspace);
|
|
4291
4285
|
const args = buildMarketplaceSkillInstallArgs({
|
|
4292
4286
|
slug: params.slug,
|
|
4293
4287
|
workspace,
|
|
@@ -4326,9 +4320,9 @@ var ServiceMarketplaceInstaller = class {
|
|
|
4326
4320
|
return { message: result.message };
|
|
4327
4321
|
}
|
|
4328
4322
|
async uninstallSkill(slug) {
|
|
4329
|
-
const workspace = getWorkspacePath5(
|
|
4330
|
-
const targetDir =
|
|
4331
|
-
if (!
|
|
4323
|
+
const workspace = getWorkspacePath5(loadConfig12().agents.defaults.workspace);
|
|
4324
|
+
const targetDir = join4(workspace, "skills", slug);
|
|
4325
|
+
if (!existsSync8(targetDir)) {
|
|
4332
4326
|
throw new Error(`Skill not installed in workspace: ${slug}`);
|
|
4333
4327
|
}
|
|
4334
4328
|
rmSync4(targetDir, { recursive: true, force: true });
|
|
@@ -4453,6 +4447,35 @@ async function reloadServicePlugins(params) {
|
|
|
4453
4447
|
};
|
|
4454
4448
|
}
|
|
4455
4449
|
|
|
4450
|
+
// src/cli/commands/service-startup-support.ts
|
|
4451
|
+
var pluginGatewayLogger = {
|
|
4452
|
+
info: (message) => console.log(`[plugins] ${message}`),
|
|
4453
|
+
warn: (message) => console.warn(`[plugins] ${message}`),
|
|
4454
|
+
error: (message) => console.error(`[plugins] ${message}`),
|
|
4455
|
+
debug: (message) => console.debug(`[plugins] ${message}`)
|
|
4456
|
+
};
|
|
4457
|
+
function logPluginGatewayDiagnostics(diagnostics) {
|
|
4458
|
+
for (const diag of diagnostics) {
|
|
4459
|
+
const prefix = diag.pluginId ? `${diag.pluginId}: ` : "";
|
|
4460
|
+
const text = `${prefix}${diag.message}`;
|
|
4461
|
+
if (diag.level === "error") {
|
|
4462
|
+
console.error(`[plugins] ${text}`);
|
|
4463
|
+
} else {
|
|
4464
|
+
console.warn(`[plugins] ${text}`);
|
|
4465
|
+
}
|
|
4466
|
+
}
|
|
4467
|
+
}
|
|
4468
|
+
async function startGatewaySupportServices(params) {
|
|
4469
|
+
if (params.cronJobs > 0) {
|
|
4470
|
+
console.log(`\u2713 Cron: ${params.cronJobs} scheduled jobs`);
|
|
4471
|
+
}
|
|
4472
|
+
console.log("\u2713 Heartbeat: every 30m");
|
|
4473
|
+
params.remoteModule?.start();
|
|
4474
|
+
params.watchConfigFile();
|
|
4475
|
+
await params.startCron();
|
|
4476
|
+
await params.startHeartbeat();
|
|
4477
|
+
}
|
|
4478
|
+
|
|
4456
4479
|
// src/cli/commands/agent-runtime-pool.ts
|
|
4457
4480
|
import {
|
|
4458
4481
|
NativeAgentEngine,
|
|
@@ -5432,7 +5455,7 @@ function resolveRequestedToolNames(metadata) {
|
|
|
5432
5455
|
)
|
|
5433
5456
|
);
|
|
5434
5457
|
}
|
|
5435
|
-
function
|
|
5458
|
+
function normalizeOptionalString4(value) {
|
|
5436
5459
|
return normalizeString(value) ?? void 0;
|
|
5437
5460
|
}
|
|
5438
5461
|
function readMetadataModel(metadata) {
|
|
@@ -5552,7 +5575,7 @@ var NextclawNcpContextBuilder = class {
|
|
|
5552
5575
|
if (inboundModel) {
|
|
5553
5576
|
session.metadata.preferred_model = inboundModel;
|
|
5554
5577
|
}
|
|
5555
|
-
const effectiveModel =
|
|
5578
|
+
const effectiveModel = normalizeOptionalString4(session.metadata.preferred_model) ?? profile.model;
|
|
5556
5579
|
const clearThinking = requestMetadata.clear_thinking === true || requestMetadata.reset_thinking === true;
|
|
5557
5580
|
if (clearThinking) {
|
|
5558
5581
|
delete session.metadata.preferred_thinking;
|
|
@@ -5569,8 +5592,8 @@ var NextclawNcpContextBuilder = class {
|
|
|
5569
5592
|
model: effectiveModel,
|
|
5570
5593
|
sessionThinkingLevel: parseThinkingLevel(session.metadata.preferred_thinking) ?? null
|
|
5571
5594
|
});
|
|
5572
|
-
const channel =
|
|
5573
|
-
const chatId =
|
|
5595
|
+
const channel = normalizeOptionalString4(requestMetadata.channel) ?? normalizeOptionalString4(session.metadata.last_channel) ?? "ui";
|
|
5596
|
+
const chatId = normalizeOptionalString4(requestMetadata.chatId) ?? normalizeOptionalString4(requestMetadata.chat_id) ?? normalizeOptionalString4(session.metadata.last_to) ?? "web-ui";
|
|
5574
5597
|
session.metadata.last_channel = channel;
|
|
5575
5598
|
session.metadata.last_to = chatId;
|
|
5576
5599
|
const requestedSkillNames = resolveRequestedSkillNames(requestMetadata);
|
|
@@ -6288,15 +6311,68 @@ async function createUiNcpAgent(params) {
|
|
|
6288
6311
|
};
|
|
6289
6312
|
}
|
|
6290
6313
|
|
|
6314
|
+
// src/cli/commands/service-remote-runtime.ts
|
|
6315
|
+
import { RemoteServiceModule } from "@nextclaw/remote";
|
|
6316
|
+
function createManagedRemoteModule(params) {
|
|
6317
|
+
if (!params.config.ui.enabled) {
|
|
6318
|
+
return null;
|
|
6319
|
+
}
|
|
6320
|
+
return new RemoteServiceModule({
|
|
6321
|
+
config: params.config,
|
|
6322
|
+
localOrigin: params.localOrigin,
|
|
6323
|
+
statusStore: createNextclawRemoteStatusStore("service"),
|
|
6324
|
+
createConnector: (logger) => createNextclawRemoteConnector({ logger }),
|
|
6325
|
+
logger: {
|
|
6326
|
+
info: (message) => console.log(`[remote] ${message}`),
|
|
6327
|
+
warn: (message) => console.warn(`[remote] ${message}`),
|
|
6328
|
+
error: (message) => console.error(`[remote] ${message}`)
|
|
6329
|
+
}
|
|
6330
|
+
});
|
|
6331
|
+
}
|
|
6332
|
+
function writeInitialManagedServiceState(params) {
|
|
6333
|
+
writeServiceState({
|
|
6334
|
+
pid: params.snapshot.pid,
|
|
6335
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6336
|
+
uiUrl: params.snapshot.uiUrl,
|
|
6337
|
+
apiUrl: params.snapshot.apiUrl,
|
|
6338
|
+
uiHost: params.snapshot.uiHost,
|
|
6339
|
+
uiPort: params.snapshot.uiPort,
|
|
6340
|
+
logPath: params.snapshot.logPath,
|
|
6341
|
+
startupLastProbeError: null,
|
|
6342
|
+
startupTimeoutMs: params.readinessTimeoutMs,
|
|
6343
|
+
startupCheckedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6344
|
+
...params.config.remote.enabled ? { remote: buildNextclawConfiguredRemoteState(params.config) } : {}
|
|
6345
|
+
});
|
|
6346
|
+
}
|
|
6347
|
+
function writeReadyManagedServiceState(params) {
|
|
6348
|
+
const currentState = readServiceState();
|
|
6349
|
+
const state = {
|
|
6350
|
+
pid: params.snapshot.pid,
|
|
6351
|
+
startedAt: currentState?.startedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
6352
|
+
uiUrl: params.snapshot.uiUrl,
|
|
6353
|
+
apiUrl: params.snapshot.apiUrl,
|
|
6354
|
+
uiHost: params.snapshot.uiHost,
|
|
6355
|
+
uiPort: params.snapshot.uiPort,
|
|
6356
|
+
logPath: params.snapshot.logPath,
|
|
6357
|
+
startupState: params.readiness.ready ? "ready" : "degraded",
|
|
6358
|
+
startupLastProbeError: params.readiness.lastProbeError,
|
|
6359
|
+
startupTimeoutMs: params.readinessTimeoutMs,
|
|
6360
|
+
startupCheckedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6361
|
+
...currentState?.remote ? { remote: currentState.remote } : {}
|
|
6362
|
+
};
|
|
6363
|
+
writeServiceState(state);
|
|
6364
|
+
return state;
|
|
6365
|
+
}
|
|
6366
|
+
|
|
6291
6367
|
// src/cli/commands/ui-chat-run-coordinator.ts
|
|
6292
|
-
import { existsSync as
|
|
6293
|
-
import { join as
|
|
6368
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync4, readdirSync as readdirSync2, readFileSync as readFileSync8, writeFileSync as writeFileSync4 } from "fs";
|
|
6369
|
+
import { join as join5 } from "path";
|
|
6294
6370
|
import {
|
|
6295
6371
|
getDataDir as getDataDir6,
|
|
6296
6372
|
parseAgentScopedSessionKey as parseAgentScopedSessionKey2,
|
|
6297
6373
|
safeFilename
|
|
6298
6374
|
} from "@nextclaw/core";
|
|
6299
|
-
var RUNS_DIR =
|
|
6375
|
+
var RUNS_DIR = join5(getDataDir6(), "runs");
|
|
6300
6376
|
var NON_TERMINAL_STATES = /* @__PURE__ */ new Set(["queued", "running"]);
|
|
6301
6377
|
var DEFAULT_SESSION_TYPE = "native";
|
|
6302
6378
|
var SESSION_TYPE_METADATA_KEY = "session_type";
|
|
@@ -6355,7 +6431,7 @@ function hasToolSessionEvent(run) {
|
|
|
6355
6431
|
var UiChatRunCoordinator = class {
|
|
6356
6432
|
constructor(options) {
|
|
6357
6433
|
this.options = options;
|
|
6358
|
-
|
|
6434
|
+
mkdirSync4(RUNS_DIR, { recursive: true });
|
|
6359
6435
|
this.loadPersistedRuns();
|
|
6360
6436
|
}
|
|
6361
6437
|
runs = /* @__PURE__ */ new Map();
|
|
@@ -6822,7 +6898,7 @@ var UiChatRunCoordinator = class {
|
|
|
6822
6898
|
};
|
|
6823
6899
|
}
|
|
6824
6900
|
getRunPath(runId) {
|
|
6825
|
-
return
|
|
6901
|
+
return join5(RUNS_DIR, `${safeFilename(runId)}.json`);
|
|
6826
6902
|
}
|
|
6827
6903
|
persistRun(run) {
|
|
6828
6904
|
const persisted = {
|
|
@@ -6840,20 +6916,20 @@ var UiChatRunCoordinator = class {
|
|
|
6840
6916
|
...typeof run.reply === "string" ? { reply: run.reply } : {},
|
|
6841
6917
|
events: run.events
|
|
6842
6918
|
};
|
|
6843
|
-
|
|
6919
|
+
writeFileSync4(this.getRunPath(run.runId), `${JSON.stringify(persisted, null, 2)}
|
|
6844
6920
|
`);
|
|
6845
6921
|
}
|
|
6846
6922
|
loadPersistedRuns() {
|
|
6847
|
-
if (!
|
|
6923
|
+
if (!existsSync9(RUNS_DIR)) {
|
|
6848
6924
|
return;
|
|
6849
6925
|
}
|
|
6850
6926
|
for (const entry of readdirSync2(RUNS_DIR, { withFileTypes: true })) {
|
|
6851
6927
|
if (!entry.isFile() || !entry.name.endsWith(".json")) {
|
|
6852
6928
|
continue;
|
|
6853
6929
|
}
|
|
6854
|
-
const path2 =
|
|
6930
|
+
const path2 = join5(RUNS_DIR, entry.name);
|
|
6855
6931
|
try {
|
|
6856
|
-
const parsed = JSON.parse(
|
|
6932
|
+
const parsed = JSON.parse(readFileSync8(path2, "utf-8"));
|
|
6857
6933
|
const runId = readOptionalString(parsed.runId);
|
|
6858
6934
|
const sessionKey = readOptionalString(parsed.sessionKey);
|
|
6859
6935
|
if (!runId || !sessionKey) {
|
|
@@ -6908,22 +6984,22 @@ var UiChatRunCoordinator = class {
|
|
|
6908
6984
|
|
|
6909
6985
|
// src/cli/commands/service.ts
|
|
6910
6986
|
var {
|
|
6911
|
-
APP_NAME:
|
|
6987
|
+
APP_NAME: APP_NAME3,
|
|
6912
6988
|
ChannelManager: ChannelManager2,
|
|
6913
6989
|
CronService: CronService2,
|
|
6914
6990
|
getApiBase,
|
|
6915
|
-
getConfigPath:
|
|
6991
|
+
getConfigPath: getConfigPath6,
|
|
6916
6992
|
getDataDir: getDataDir7,
|
|
6917
6993
|
getProvider,
|
|
6918
6994
|
getProviderName,
|
|
6919
6995
|
getWorkspacePath: getWorkspacePath9,
|
|
6920
6996
|
HeartbeatService,
|
|
6921
6997
|
LiteLLMProvider,
|
|
6922
|
-
loadConfig:
|
|
6998
|
+
loadConfig: loadConfig13,
|
|
6923
6999
|
MessageBus,
|
|
6924
7000
|
ProviderManager,
|
|
6925
7001
|
resolveConfigSecrets: resolveConfigSecrets2,
|
|
6926
|
-
saveConfig:
|
|
7002
|
+
saveConfig: saveConfig9,
|
|
6927
7003
|
SessionManager,
|
|
6928
7004
|
parseAgentScopedSessionKey: parseAgentScopedSessionKey3
|
|
6929
7005
|
} = NextclawCore;
|
|
@@ -6943,8 +7019,8 @@ var ServiceCommands = class {
|
|
|
6943
7019
|
async startGateway(options = {}) {
|
|
6944
7020
|
this.applyLiveConfigReload = null;
|
|
6945
7021
|
this.liveUiNcpAgent = null;
|
|
6946
|
-
const runtimeConfigPath =
|
|
6947
|
-
const config2 = resolveConfigSecrets2(
|
|
7022
|
+
const runtimeConfigPath = getConfigPath6();
|
|
7023
|
+
const config2 = resolveConfigSecrets2(loadConfig13(), { configPath: runtimeConfigPath });
|
|
6948
7024
|
const workspace = getWorkspacePath9(config2.agents.defaults.workspace);
|
|
6949
7025
|
let pluginRegistry = loadPluginRegistry(config2, workspace);
|
|
6950
7026
|
let extensionRegistry = toExtensionRegistry(pluginRegistry);
|
|
@@ -6957,27 +7033,11 @@ var ServiceCommands = class {
|
|
|
6957
7033
|
});
|
|
6958
7034
|
const sessionManager = new SessionManager(workspace);
|
|
6959
7035
|
let pluginGatewayHandles = [];
|
|
6960
|
-
const
|
|
6961
|
-
info: (message) => console.log(`[plugins] ${message}`),
|
|
6962
|
-
warn: (message) => console.warn(`[plugins] ${message}`),
|
|
6963
|
-
error: (message) => console.error(`[plugins] ${message}`),
|
|
6964
|
-
debug: (message) => console.debug(`[plugins] ${message}`)
|
|
6965
|
-
};
|
|
6966
|
-
const logPluginGatewayDiagnostics = (diagnostics) => {
|
|
6967
|
-
for (const diag of diagnostics) {
|
|
6968
|
-
const prefix = diag.pluginId ? `${diag.pluginId}: ` : "";
|
|
6969
|
-
const text = `${prefix}${diag.message}`;
|
|
6970
|
-
if (diag.level === "error") {
|
|
6971
|
-
console.error(`[plugins] ${text}`);
|
|
6972
|
-
} else {
|
|
6973
|
-
console.warn(`[plugins] ${text}`);
|
|
6974
|
-
}
|
|
6975
|
-
}
|
|
6976
|
-
};
|
|
6977
|
-
const cronStorePath = join7(getDataDir7(), "cron", "jobs.json");
|
|
7036
|
+
const cronStorePath = join6(getDataDir7(), "cron", "jobs.json");
|
|
6978
7037
|
const cron2 = new CronService2(cronStorePath);
|
|
6979
7038
|
const uiConfig = resolveUiConfig(config2, options.uiOverrides);
|
|
6980
7039
|
const uiStaticDir = options.uiStaticDir === void 0 ? resolveUiStaticDir() : options.uiStaticDir;
|
|
7040
|
+
const localOrigin = resolveUiApiBase(uiConfig.host, uiConfig.port);
|
|
6981
7041
|
if (!provider) {
|
|
6982
7042
|
console.warn("Warning: No API key configured. The gateway is running, but agent replies are disabled until provider config is set.");
|
|
6983
7043
|
}
|
|
@@ -6989,7 +7049,7 @@ var ServiceCommands = class {
|
|
|
6989
7049
|
sessionManager,
|
|
6990
7050
|
providerManager,
|
|
6991
7051
|
makeProvider: (nextConfig) => this.makeProvider(nextConfig, { allowMissing: true }) ?? this.makeMissingProvider(nextConfig),
|
|
6992
|
-
loadConfig: () => resolveConfigSecrets2(
|
|
7052
|
+
loadConfig: () => resolveConfigSecrets2(loadConfig13(), { configPath: runtimeConfigPath }),
|
|
6993
7053
|
getExtensionChannels: () => extensionRegistry.channels,
|
|
6994
7054
|
onRestartRequired: (paths) => {
|
|
6995
7055
|
void this.deps.requestRestart({
|
|
@@ -7000,14 +7060,14 @@ var ServiceCommands = class {
|
|
|
7000
7060
|
}
|
|
7001
7061
|
});
|
|
7002
7062
|
this.applyLiveConfigReload = async () => {
|
|
7003
|
-
await reloader.applyReloadPlan(resolveConfigSecrets2(
|
|
7063
|
+
await reloader.applyReloadPlan(resolveConfigSecrets2(loadConfig13(), { configPath: runtimeConfigPath }));
|
|
7004
7064
|
};
|
|
7005
7065
|
const gatewayController = new GatewayControllerImpl({
|
|
7006
7066
|
reloader,
|
|
7007
7067
|
cron: cron2,
|
|
7008
7068
|
sessionManager,
|
|
7009
|
-
getConfigPath:
|
|
7010
|
-
saveConfig:
|
|
7069
|
+
getConfigPath: getConfigPath6,
|
|
7070
|
+
saveConfig: saveConfig9,
|
|
7011
7071
|
requestRestart: async (options2) => {
|
|
7012
7072
|
await this.deps.requestRestart({
|
|
7013
7073
|
reason: options2?.reason ?? "gateway tool restart",
|
|
@@ -7033,7 +7093,7 @@ var ServiceCommands = class {
|
|
|
7033
7093
|
resolveMessageToolHints: ({ channel, accountId }) => resolvePluginChannelMessageToolHints({
|
|
7034
7094
|
registry: pluginRegistry,
|
|
7035
7095
|
channel,
|
|
7036
|
-
cfg: resolveConfigSecrets2(
|
|
7096
|
+
cfg: resolveConfigSecrets2(loadConfig13(), { configPath: runtimeConfigPath }),
|
|
7037
7097
|
accountId
|
|
7038
7098
|
})
|
|
7039
7099
|
});
|
|
@@ -7066,14 +7126,14 @@ var ServiceCommands = class {
|
|
|
7066
7126
|
});
|
|
7067
7127
|
let pluginChannelBindings = getPluginChannelBindings3(pluginRegistry);
|
|
7068
7128
|
setPluginRuntimeBridge({
|
|
7069
|
-
loadConfig: () => toPluginConfigView(resolveConfigSecrets2(
|
|
7129
|
+
loadConfig: () => toPluginConfigView(resolveConfigSecrets2(loadConfig13(), { configPath: runtimeConfigPath }), pluginChannelBindings),
|
|
7070
7130
|
writeConfigFile: async (nextConfigView) => {
|
|
7071
7131
|
if (!nextConfigView || typeof nextConfigView !== "object" || Array.isArray(nextConfigView)) {
|
|
7072
7132
|
throw new Error("plugin runtime writeConfigFile expects an object config");
|
|
7073
7133
|
}
|
|
7074
|
-
const current =
|
|
7134
|
+
const current = loadConfig13();
|
|
7075
7135
|
const next = mergePluginConfigView(current, nextConfigView, pluginChannelBindings);
|
|
7076
|
-
|
|
7136
|
+
saveConfig9(next);
|
|
7077
7137
|
},
|
|
7078
7138
|
dispatchReplyWithBufferedBlockDispatcher: async ({ ctx, dispatcherOptions }) => {
|
|
7079
7139
|
const bodyForAgent = typeof ctx.BodyForAgent === "string" ? ctx.BodyForAgent : "";
|
|
@@ -7147,23 +7207,23 @@ var ServiceCommands = class {
|
|
|
7147
7207
|
providerManager,
|
|
7148
7208
|
bus,
|
|
7149
7209
|
gatewayController,
|
|
7150
|
-
() => resolveConfigSecrets2(
|
|
7210
|
+
() => resolveConfigSecrets2(loadConfig13(), { configPath: runtimeConfigPath }),
|
|
7151
7211
|
() => extensionRegistry,
|
|
7152
7212
|
({ channel, accountId }) => resolvePluginChannelMessageToolHints({
|
|
7153
7213
|
registry: pluginRegistry,
|
|
7154
7214
|
channel,
|
|
7155
|
-
cfg: resolveConfigSecrets2(
|
|
7215
|
+
cfg: resolveConfigSecrets2(loadConfig13(), { configPath: runtimeConfigPath }),
|
|
7156
7216
|
accountId
|
|
7157
7217
|
})
|
|
7158
7218
|
);
|
|
7159
|
-
const
|
|
7160
|
-
|
|
7161
|
-
|
|
7162
|
-
|
|
7163
|
-
|
|
7164
|
-
|
|
7165
|
-
|
|
7166
|
-
|
|
7219
|
+
const remoteModule = createManagedRemoteModule({ config: config2, localOrigin });
|
|
7220
|
+
await startGatewaySupportServices({
|
|
7221
|
+
cronJobs: cron2.status().jobs,
|
|
7222
|
+
remoteModule,
|
|
7223
|
+
watchConfigFile: () => this.watchConfigFile(reloader),
|
|
7224
|
+
startCron: () => cron2.start(),
|
|
7225
|
+
startHeartbeat: () => heartbeat.start()
|
|
7226
|
+
});
|
|
7167
7227
|
try {
|
|
7168
7228
|
const startedPluginGateways = await startPluginChannelGateways2({
|
|
7169
7229
|
registry: pluginRegistry,
|
|
@@ -7177,6 +7237,7 @@ var ServiceCommands = class {
|
|
|
7177
7237
|
} finally {
|
|
7178
7238
|
this.applyLiveConfigReload = null;
|
|
7179
7239
|
this.liveUiNcpAgent = null;
|
|
7240
|
+
await remoteModule?.stop();
|
|
7180
7241
|
await stopPluginChannelGateways2(pluginGatewayHandles);
|
|
7181
7242
|
setPluginRuntimeBridge(null);
|
|
7182
7243
|
}
|
|
@@ -7189,7 +7250,7 @@ var ServiceCommands = class {
|
|
|
7189
7250
|
return trimmed || void 0;
|
|
7190
7251
|
}
|
|
7191
7252
|
watchConfigFile(reloader) {
|
|
7192
|
-
const configPath = resolve10(
|
|
7253
|
+
const configPath = resolve10(getConfigPath6());
|
|
7193
7254
|
const watcher = chokidar.watch(configPath, {
|
|
7194
7255
|
ignoreInitial: true,
|
|
7195
7256
|
awaitWriteFinish: { stabilityThreshold: 200, pollInterval: 50 }
|
|
@@ -7310,7 +7371,7 @@ var ServiceCommands = class {
|
|
|
7310
7371
|
});
|
|
7311
7372
|
}
|
|
7312
7373
|
async runForeground(options) {
|
|
7313
|
-
const config2 =
|
|
7374
|
+
const config2 = loadConfig13();
|
|
7314
7375
|
const uiConfig = resolveUiConfig(config2, options.uiOverrides);
|
|
7315
7376
|
const uiUrl = resolveUiApiBase(uiConfig.host, uiConfig.port);
|
|
7316
7377
|
if (options.open) {
|
|
@@ -7323,14 +7384,14 @@ var ServiceCommands = class {
|
|
|
7323
7384
|
});
|
|
7324
7385
|
}
|
|
7325
7386
|
async startService(options) {
|
|
7326
|
-
const config2 =
|
|
7387
|
+
const config2 = loadConfig13();
|
|
7327
7388
|
const uiConfig = resolveUiConfig(config2, options.uiOverrides);
|
|
7328
7389
|
const uiUrl = resolveUiApiBase(uiConfig.host, uiConfig.port);
|
|
7329
7390
|
const apiUrl = `${uiUrl}/api`;
|
|
7330
7391
|
const staticDir = resolveUiStaticDir();
|
|
7331
7392
|
const existing = readServiceState();
|
|
7332
7393
|
if (existing && isProcessRunning(existing.pid)) {
|
|
7333
|
-
console.log(`\u2713 ${
|
|
7394
|
+
console.log(`\u2713 ${APP_NAME3} is already running (PID ${existing.pid})`);
|
|
7334
7395
|
console.log(`UI: ${existing.uiUrl}`);
|
|
7335
7396
|
console.log(`API: ${existing.apiUrl}`);
|
|
7336
7397
|
const parsedUi = (() => {
|
|
@@ -7378,13 +7439,13 @@ var ServiceCommands = class {
|
|
|
7378
7439
|
healthUrl
|
|
7379
7440
|
});
|
|
7380
7441
|
if (!portPreflight.ok) {
|
|
7381
|
-
console.error(`Error: Cannot start ${
|
|
7442
|
+
console.error(`Error: Cannot start ${APP_NAME3} because UI port ${uiConfig.port} is already occupied.`);
|
|
7382
7443
|
console.error(portPreflight.message);
|
|
7383
7444
|
return;
|
|
7384
7445
|
}
|
|
7385
7446
|
const logPath = resolveServiceLogPath();
|
|
7386
7447
|
const logDir = resolve10(logPath, "..");
|
|
7387
|
-
|
|
7448
|
+
mkdirSync5(logDir, { recursive: true });
|
|
7388
7449
|
const logFd = openSync(logPath, "a");
|
|
7389
7450
|
const readinessTimeoutMs = this.resolveStartupTimeoutMs(options.startupTimeoutMs);
|
|
7390
7451
|
const quickPhaseTimeoutMs = Math.min(8e3, readinessTimeoutMs);
|
|
@@ -7393,10 +7454,8 @@ var ServiceCommands = class {
|
|
|
7393
7454
|
logPath,
|
|
7394
7455
|
`start requested: ui=${uiConfig.host}:${uiConfig.port}, readinessTimeoutMs=${readinessTimeoutMs}`
|
|
7395
7456
|
);
|
|
7396
|
-
console.log(`Starting ${
|
|
7397
|
-
const serveArgs = buildServeArgs({
|
|
7398
|
-
uiPort: uiConfig.port
|
|
7399
|
-
});
|
|
7457
|
+
console.log(`Starting ${APP_NAME3} background service (readiness timeout ${Math.ceil(readinessTimeoutMs / 1e3)}s)...`);
|
|
7458
|
+
const serveArgs = buildServeArgs({ uiPort: uiConfig.port });
|
|
7400
7459
|
this.appendStartupStage(logPath, `spawning background process: ${process.execPath} ${[...process.execArgv, ...serveArgs].join(" ")}`);
|
|
7401
7460
|
const child = spawn2(process.execPath, [...process.execArgv, ...serveArgs], {
|
|
7402
7461
|
env: process.env,
|
|
@@ -7417,6 +7476,11 @@ var ServiceCommands = class {
|
|
|
7417
7476
|
});
|
|
7418
7477
|
return;
|
|
7419
7478
|
}
|
|
7479
|
+
writeInitialManagedServiceState({
|
|
7480
|
+
config: config2,
|
|
7481
|
+
readinessTimeoutMs,
|
|
7482
|
+
snapshot: { pid: child.pid, uiUrl, apiUrl, uiHost: uiConfig.host, uiPort: uiConfig.port, logPath }
|
|
7483
|
+
});
|
|
7420
7484
|
this.appendStartupStage(logPath, `health probe started: ${healthUrl} (phase=quick, timeoutMs=${quickPhaseTimeoutMs})`);
|
|
7421
7485
|
let readiness = await this.waitForBackgroundServiceReady({
|
|
7422
7486
|
pid: child.pid,
|
|
@@ -7458,28 +7522,19 @@ var ServiceCommands = class {
|
|
|
7458
7522
|
);
|
|
7459
7523
|
}
|
|
7460
7524
|
child.unref();
|
|
7461
|
-
const state = {
|
|
7462
|
-
|
|
7463
|
-
|
|
7464
|
-
uiUrl,
|
|
7465
|
-
|
|
7466
|
-
uiHost: uiConfig.host,
|
|
7467
|
-
uiPort: uiConfig.port,
|
|
7468
|
-
logPath,
|
|
7469
|
-
startupState: readiness.ready ? "ready" : "degraded",
|
|
7470
|
-
startupLastProbeError: readiness.lastProbeError,
|
|
7471
|
-
startupTimeoutMs: readinessTimeoutMs,
|
|
7472
|
-
startupCheckedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
7473
|
-
};
|
|
7474
|
-
writeServiceState(state);
|
|
7525
|
+
const state = writeReadyManagedServiceState({
|
|
7526
|
+
readinessTimeoutMs,
|
|
7527
|
+
readiness,
|
|
7528
|
+
snapshot: { pid: child.pid, uiUrl, apiUrl, uiHost: uiConfig.host, uiPort: uiConfig.port, logPath }
|
|
7529
|
+
});
|
|
7475
7530
|
if (!readiness.ready) {
|
|
7476
7531
|
const hint = readiness.lastProbeError ? ` Last probe error: ${readiness.lastProbeError}` : "";
|
|
7477
7532
|
console.warn(
|
|
7478
|
-
`Warning: ${
|
|
7533
|
+
`Warning: ${APP_NAME3} is running (PID ${state.pid}) but not healthy yet after ${Math.ceil(readinessTimeoutMs / 1e3)}s. Marked as degraded.${hint}`
|
|
7479
7534
|
);
|
|
7480
|
-
console.warn(`Tip: Run "${
|
|
7535
|
+
console.warn(`Tip: Run "${APP_NAME3} status --json" and check logs: ${logPath}`);
|
|
7481
7536
|
} else {
|
|
7482
|
-
console.log(`\u2713 ${
|
|
7537
|
+
console.log(`\u2713 ${APP_NAME3} started in background (PID ${state.pid})`);
|
|
7483
7538
|
}
|
|
7484
7539
|
console.log(`UI: ${uiUrl}`);
|
|
7485
7540
|
console.log(`API: ${apiUrl}`);
|
|
@@ -7501,7 +7556,7 @@ var ServiceCommands = class {
|
|
|
7501
7556
|
clearServiceState();
|
|
7502
7557
|
return;
|
|
7503
7558
|
}
|
|
7504
|
-
console.log(`Stopping ${
|
|
7559
|
+
console.log(`Stopping ${APP_NAME3} (PID ${state.pid})...`);
|
|
7505
7560
|
try {
|
|
7506
7561
|
process.kill(state.pid, "SIGTERM");
|
|
7507
7562
|
} catch (error) {
|
|
@@ -7519,7 +7574,7 @@ var ServiceCommands = class {
|
|
|
7519
7574
|
await waitForExit(state.pid, 2e3);
|
|
7520
7575
|
}
|
|
7521
7576
|
clearServiceState();
|
|
7522
|
-
console.log(`\u2713 ${
|
|
7577
|
+
console.log(`\u2713 ${APP_NAME3} stopped`);
|
|
7523
7578
|
}
|
|
7524
7579
|
async waitForBackgroundServiceReady(params) {
|
|
7525
7580
|
const startedAt = Date.now();
|
|
@@ -7737,7 +7792,7 @@ var ServiceCommands = class {
|
|
|
7737
7792
|
return null;
|
|
7738
7793
|
}
|
|
7739
7794
|
console.error("Error: No API key configured.");
|
|
7740
|
-
console.error(`Set one in ${
|
|
7795
|
+
console.error(`Set one in ${getConfigPath6()} under providers section`);
|
|
7741
7796
|
process.exit(1);
|
|
7742
7797
|
}
|
|
7743
7798
|
return new LiteLLMProvider({
|
|
@@ -7774,8 +7829,8 @@ var ServiceCommands = class {
|
|
|
7774
7829
|
}
|
|
7775
7830
|
printServiceControlHints() {
|
|
7776
7831
|
console.log("Service controls:");
|
|
7777
|
-
console.log(` - Check status: ${
|
|
7778
|
-
console.log(` - If you need to stop the service, run: ${
|
|
7832
|
+
console.log(` - Check status: ${APP_NAME3} status`);
|
|
7833
|
+
console.log(` - If you need to stop the service, run: ${APP_NAME3} stop`);
|
|
7779
7834
|
}
|
|
7780
7835
|
async startUiIfEnabled(uiConfig, uiStaticDir, cronService, runtimePool, sessionManager, providerManager, bus, gatewayController, getConfig, getExtensionRegistry, resolveMessageToolHints) {
|
|
7781
7836
|
if (!uiConfig.enabled) {
|
|
@@ -7864,7 +7919,7 @@ var ServiceCommands = class {
|
|
|
7864
7919
|
const uiServer = startUiServer({
|
|
7865
7920
|
host: uiConfig.host,
|
|
7866
7921
|
port: uiConfig.port,
|
|
7867
|
-
configPath:
|
|
7922
|
+
configPath: getConfigPath6(),
|
|
7868
7923
|
productVersion: getPackageVersion(),
|
|
7869
7924
|
staticDir: uiStaticDir ?? void 0,
|
|
7870
7925
|
cronService,
|
|
@@ -7952,10 +8007,10 @@ var ServiceCommands = class {
|
|
|
7952
8007
|
}
|
|
7953
8008
|
}
|
|
7954
8009
|
installBuiltinMarketplaceSkill(slug, force) {
|
|
7955
|
-
const workspace = getWorkspacePath9(
|
|
7956
|
-
const destination =
|
|
7957
|
-
const destinationSkillFile =
|
|
7958
|
-
if (
|
|
8010
|
+
const workspace = getWorkspacePath9(loadConfig13().agents.defaults.workspace);
|
|
8011
|
+
const destination = join6(workspace, "skills", slug);
|
|
8012
|
+
const destinationSkillFile = join6(destination, "SKILL.md");
|
|
8013
|
+
if (existsSync10(destinationSkillFile) && !force) {
|
|
7959
8014
|
return {
|
|
7960
8015
|
message: `${slug} is already installed`
|
|
7961
8016
|
};
|
|
@@ -7963,15 +8018,15 @@ var ServiceCommands = class {
|
|
|
7963
8018
|
const loader = createSkillsLoader(workspace);
|
|
7964
8019
|
const builtin = (loader?.listSkills(false) ?? []).find((skill) => skill.name === slug && skill.source === "builtin");
|
|
7965
8020
|
if (!builtin) {
|
|
7966
|
-
if (
|
|
8021
|
+
if (existsSync10(destinationSkillFile)) {
|
|
7967
8022
|
return {
|
|
7968
8023
|
message: `${slug} is already installed`
|
|
7969
8024
|
};
|
|
7970
8025
|
}
|
|
7971
8026
|
return null;
|
|
7972
8027
|
}
|
|
7973
|
-
|
|
7974
|
-
cpSync2(
|
|
8028
|
+
mkdirSync5(join6(workspace, "skills"), { recursive: true });
|
|
8029
|
+
cpSync2(dirname2(builtin.path), destination, { recursive: true, force: true });
|
|
7975
8030
|
return {
|
|
7976
8031
|
message: `Installed skill: ${slug}`
|
|
7977
8032
|
};
|
|
@@ -8030,11 +8085,11 @@ ${stderr}`.trim();
|
|
|
8030
8085
|
};
|
|
8031
8086
|
|
|
8032
8087
|
// src/cli/workspace.ts
|
|
8033
|
-
import { cpSync as cpSync3, existsSync as
|
|
8088
|
+
import { cpSync as cpSync3, existsSync as existsSync11, mkdirSync as mkdirSync6, readFileSync as readFileSync9, readdirSync as readdirSync3, rmSync as rmSync5, writeFileSync as writeFileSync5 } from "fs";
|
|
8034
8089
|
import { createRequire as createRequire2 } from "module";
|
|
8035
|
-
import { dirname as
|
|
8090
|
+
import { dirname as dirname3, join as join7, resolve as resolve11 } from "path";
|
|
8036
8091
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
8037
|
-
import { APP_NAME as
|
|
8092
|
+
import { APP_NAME as APP_NAME4, getDataDir as getDataDir8 } from "@nextclaw/core";
|
|
8038
8093
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
8039
8094
|
var WorkspaceManager = class {
|
|
8040
8095
|
constructor(logo) {
|
|
@@ -8062,30 +8117,30 @@ var WorkspaceManager = class {
|
|
|
8062
8117
|
{ source: "memory/MEMORY.md", target: "memory/MEMORY.md" }
|
|
8063
8118
|
];
|
|
8064
8119
|
for (const entry of templateFiles) {
|
|
8065
|
-
const filePath =
|
|
8066
|
-
if (!force &&
|
|
8120
|
+
const filePath = join7(workspace, entry.target);
|
|
8121
|
+
if (!force && existsSync11(filePath)) {
|
|
8067
8122
|
continue;
|
|
8068
8123
|
}
|
|
8069
|
-
const templatePath =
|
|
8070
|
-
if (!
|
|
8124
|
+
const templatePath = join7(templateDir, entry.source);
|
|
8125
|
+
if (!existsSync11(templatePath)) {
|
|
8071
8126
|
console.warn(`Warning: Template file missing: ${templatePath}`);
|
|
8072
8127
|
continue;
|
|
8073
8128
|
}
|
|
8074
|
-
const raw =
|
|
8075
|
-
const content = raw.replace(/\$\{APP_NAME\}/g,
|
|
8076
|
-
|
|
8077
|
-
|
|
8129
|
+
const raw = readFileSync9(templatePath, "utf-8");
|
|
8130
|
+
const content = raw.replace(/\$\{APP_NAME\}/g, APP_NAME4);
|
|
8131
|
+
mkdirSync6(dirname3(filePath), { recursive: true });
|
|
8132
|
+
writeFileSync5(filePath, content);
|
|
8078
8133
|
created.push(entry.target);
|
|
8079
8134
|
}
|
|
8080
|
-
const memoryDir =
|
|
8081
|
-
if (!
|
|
8082
|
-
|
|
8083
|
-
created.push(
|
|
8135
|
+
const memoryDir = join7(workspace, "memory");
|
|
8136
|
+
if (!existsSync11(memoryDir)) {
|
|
8137
|
+
mkdirSync6(memoryDir, { recursive: true });
|
|
8138
|
+
created.push(join7("memory", ""));
|
|
8084
8139
|
}
|
|
8085
|
-
const skillsDir =
|
|
8086
|
-
if (!
|
|
8087
|
-
|
|
8088
|
-
created.push(
|
|
8140
|
+
const skillsDir = join7(workspace, "skills");
|
|
8141
|
+
if (!existsSync11(skillsDir)) {
|
|
8142
|
+
mkdirSync6(skillsDir, { recursive: true });
|
|
8143
|
+
created.push(join7("skills", ""));
|
|
8089
8144
|
}
|
|
8090
8145
|
const seeded = this.seedBuiltinSkills(skillsDir, { force });
|
|
8091
8146
|
if (seeded > 0) {
|
|
@@ -8104,12 +8159,12 @@ var WorkspaceManager = class {
|
|
|
8104
8159
|
if (!entry.isDirectory()) {
|
|
8105
8160
|
continue;
|
|
8106
8161
|
}
|
|
8107
|
-
const src =
|
|
8108
|
-
if (!
|
|
8162
|
+
const src = join7(sourceDir, entry.name);
|
|
8163
|
+
if (!existsSync11(join7(src, "SKILL.md"))) {
|
|
8109
8164
|
continue;
|
|
8110
8165
|
}
|
|
8111
|
-
const dest =
|
|
8112
|
-
if (!force &&
|
|
8166
|
+
const dest = join7(targetDir, entry.name);
|
|
8167
|
+
if (!force && existsSync11(dest)) {
|
|
8113
8168
|
continue;
|
|
8114
8169
|
}
|
|
8115
8170
|
try {
|
|
@@ -8126,13 +8181,13 @@ var WorkspaceManager = class {
|
|
|
8126
8181
|
try {
|
|
8127
8182
|
const require3 = createRequire2(import.meta.url);
|
|
8128
8183
|
const entry = require3.resolve("@nextclaw/core");
|
|
8129
|
-
const pkgRoot = resolve11(
|
|
8130
|
-
const distSkills =
|
|
8131
|
-
if (
|
|
8184
|
+
const pkgRoot = resolve11(dirname3(entry), "..");
|
|
8185
|
+
const distSkills = join7(pkgRoot, "dist", "skills");
|
|
8186
|
+
if (existsSync11(distSkills)) {
|
|
8132
8187
|
return distSkills;
|
|
8133
8188
|
}
|
|
8134
|
-
const srcSkills =
|
|
8135
|
-
if (
|
|
8189
|
+
const srcSkills = join7(pkgRoot, "src", "agent", "skills");
|
|
8190
|
+
if (existsSync11(srcSkills)) {
|
|
8136
8191
|
return srcSkills;
|
|
8137
8192
|
}
|
|
8138
8193
|
return null;
|
|
@@ -8147,17 +8202,17 @@ var WorkspaceManager = class {
|
|
|
8147
8202
|
}
|
|
8148
8203
|
const cliDir = resolve11(fileURLToPath4(new URL(".", import.meta.url)));
|
|
8149
8204
|
const pkgRoot = resolve11(cliDir, "..", "..");
|
|
8150
|
-
const candidates = [
|
|
8205
|
+
const candidates = [join7(pkgRoot, "templates")];
|
|
8151
8206
|
for (const candidate of candidates) {
|
|
8152
|
-
if (
|
|
8207
|
+
if (existsSync11(candidate)) {
|
|
8153
8208
|
return candidate;
|
|
8154
8209
|
}
|
|
8155
8210
|
}
|
|
8156
8211
|
return null;
|
|
8157
8212
|
}
|
|
8158
8213
|
getBridgeDir() {
|
|
8159
|
-
const userBridge =
|
|
8160
|
-
if (
|
|
8214
|
+
const userBridge = join7(getDataDir8(), "bridge");
|
|
8215
|
+
if (existsSync11(join7(userBridge, "dist", "index.js"))) {
|
|
8161
8216
|
return userBridge;
|
|
8162
8217
|
}
|
|
8163
8218
|
if (!which("npm")) {
|
|
@@ -8166,21 +8221,21 @@ var WorkspaceManager = class {
|
|
|
8166
8221
|
}
|
|
8167
8222
|
const cliDir = resolve11(fileURLToPath4(new URL(".", import.meta.url)));
|
|
8168
8223
|
const pkgRoot = resolve11(cliDir, "..", "..");
|
|
8169
|
-
const pkgBridge =
|
|
8170
|
-
const srcBridge =
|
|
8224
|
+
const pkgBridge = join7(pkgRoot, "bridge");
|
|
8225
|
+
const srcBridge = join7(pkgRoot, "..", "..", "bridge");
|
|
8171
8226
|
let source = null;
|
|
8172
|
-
if (
|
|
8227
|
+
if (existsSync11(join7(pkgBridge, "package.json"))) {
|
|
8173
8228
|
source = pkgBridge;
|
|
8174
|
-
} else if (
|
|
8229
|
+
} else if (existsSync11(join7(srcBridge, "package.json"))) {
|
|
8175
8230
|
source = srcBridge;
|
|
8176
8231
|
}
|
|
8177
8232
|
if (!source) {
|
|
8178
|
-
console.error(`Bridge source not found. Try reinstalling ${
|
|
8233
|
+
console.error(`Bridge source not found. Try reinstalling ${APP_NAME4}.`);
|
|
8179
8234
|
process.exit(1);
|
|
8180
8235
|
}
|
|
8181
8236
|
console.log(`${this.logo} Setting up bridge...`);
|
|
8182
|
-
|
|
8183
|
-
if (
|
|
8237
|
+
mkdirSync6(resolve11(userBridge, ".."), { recursive: true });
|
|
8238
|
+
if (existsSync11(userBridge)) {
|
|
8184
8239
|
rmSync5(userBridge, { recursive: true, force: true });
|
|
8185
8240
|
}
|
|
8186
8241
|
cpSync3(source, userBridge, {
|
|
@@ -8233,6 +8288,7 @@ var CliRuntime = class {
|
|
|
8233
8288
|
cronCommands;
|
|
8234
8289
|
platformAuthCommands;
|
|
8235
8290
|
remoteCommands;
|
|
8291
|
+
remote;
|
|
8236
8292
|
diagnosticsCommands;
|
|
8237
8293
|
constructor(options = {}) {
|
|
8238
8294
|
this.logo = options.logo ?? LOGO;
|
|
@@ -8256,6 +8312,16 @@ var CliRuntime = class {
|
|
|
8256
8312
|
this.cronCommands = new CronCommands();
|
|
8257
8313
|
this.platformAuthCommands = new PlatformAuthCommands();
|
|
8258
8314
|
this.remoteCommands = new RemoteCommands();
|
|
8315
|
+
this.remote = new RemoteRuntimeActions({
|
|
8316
|
+
appName: APP_NAME5,
|
|
8317
|
+
initAuto: (source) => this.init({ source, auto: true }),
|
|
8318
|
+
remoteCommands: this.remoteCommands,
|
|
8319
|
+
restartBackgroundService: (reason) => this.restartBackgroundService(reason),
|
|
8320
|
+
hasRunningManagedService: () => {
|
|
8321
|
+
const state = readServiceState();
|
|
8322
|
+
return Boolean(state && isProcessRunning(state.pid));
|
|
8323
|
+
}
|
|
8324
|
+
});
|
|
8259
8325
|
this.diagnosticsCommands = new DiagnosticsCommands({ logo: this.logo });
|
|
8260
8326
|
this.restartCoordinator = new RestartCoordinator({
|
|
8261
8327
|
readServiceState,
|
|
@@ -8286,7 +8352,7 @@ var CliRuntime = class {
|
|
|
8286
8352
|
const uiHost = FORCED_PUBLIC_UI_HOST;
|
|
8287
8353
|
const uiPort = typeof state.uiPort === "number" && Number.isFinite(state.uiPort) ? state.uiPort : 18791;
|
|
8288
8354
|
console.log(
|
|
8289
|
-
`Applying changes (${reason}): restarting ${
|
|
8355
|
+
`Applying changes (${reason}): restarting ${APP_NAME5} background service...`
|
|
8290
8356
|
);
|
|
8291
8357
|
await this.serviceCommands.stopService();
|
|
8292
8358
|
await this.serviceCommands.startService({
|
|
@@ -8441,7 +8507,7 @@ var CliRuntime = class {
|
|
|
8441
8507
|
}
|
|
8442
8508
|
async onboard() {
|
|
8443
8509
|
console.warn(
|
|
8444
|
-
`Warning: ${
|
|
8510
|
+
`Warning: ${APP_NAME5} onboard is deprecated. Use "${APP_NAME5} init" instead.`
|
|
8445
8511
|
);
|
|
8446
8512
|
await this.init({ source: "onboard" });
|
|
8447
8513
|
}
|
|
@@ -8449,18 +8515,18 @@ var CliRuntime = class {
|
|
|
8449
8515
|
const source = options.source ?? "init";
|
|
8450
8516
|
const prefix = options.auto ? "Auto init" : "Init";
|
|
8451
8517
|
const force = Boolean(options.force);
|
|
8452
|
-
const configPath =
|
|
8518
|
+
const configPath = getConfigPath7();
|
|
8453
8519
|
let createdConfig = false;
|
|
8454
|
-
if (!
|
|
8520
|
+
if (!existsSync12(configPath)) {
|
|
8455
8521
|
const config3 = ConfigSchema2.parse({});
|
|
8456
|
-
|
|
8522
|
+
saveConfig10(config3);
|
|
8457
8523
|
createdConfig = true;
|
|
8458
8524
|
}
|
|
8459
|
-
const config2 =
|
|
8525
|
+
const config2 = loadConfig14();
|
|
8460
8526
|
const workspaceSetting = config2.agents.defaults.workspace;
|
|
8461
|
-
const workspacePath = !workspaceSetting || workspaceSetting === DEFAULT_WORKSPACE_PATH ?
|
|
8462
|
-
const workspaceExisted =
|
|
8463
|
-
|
|
8527
|
+
const workspacePath = !workspaceSetting || workspaceSetting === DEFAULT_WORKSPACE_PATH ? join8(getDataDir9(), DEFAULT_WORKSPACE_DIR) : expandHome2(workspaceSetting);
|
|
8528
|
+
const workspaceExisted = existsSync12(workspacePath);
|
|
8529
|
+
mkdirSync7(workspacePath, { recursive: true });
|
|
8464
8530
|
const templateResult = this.workspaceManager.createWorkspaceTemplates(
|
|
8465
8531
|
workspacePath,
|
|
8466
8532
|
{ force }
|
|
@@ -8479,13 +8545,13 @@ var CliRuntime = class {
|
|
|
8479
8545
|
}
|
|
8480
8546
|
if (!options.auto) {
|
|
8481
8547
|
console.log(`
|
|
8482
|
-
${this.logo} ${
|
|
8548
|
+
${this.logo} ${APP_NAME5} is ready! (${source})`);
|
|
8483
8549
|
console.log("\nNext steps:");
|
|
8484
8550
|
console.log(` 1. Add your API key to ${configPath}`);
|
|
8485
|
-
console.log(` 2. Chat: ${
|
|
8551
|
+
console.log(` 2. Chat: ${APP_NAME5} agent -m "Hello!"`);
|
|
8486
8552
|
} else {
|
|
8487
8553
|
console.log(
|
|
8488
|
-
`Tip: Run "${
|
|
8554
|
+
`Tip: Run "${APP_NAME5} init${force ? " --force" : ""}" to re-run initialization if needed.`
|
|
8489
8555
|
);
|
|
8490
8556
|
}
|
|
8491
8557
|
}
|
|
@@ -8493,9 +8559,6 @@ ${this.logo} ${APP_NAME4} is ready! (${source})`);
|
|
|
8493
8559
|
await this.init({ source: "login", auto: true });
|
|
8494
8560
|
await this.platformAuthCommands.login(opts);
|
|
8495
8561
|
}
|
|
8496
|
-
async remoteConnect(opts = {}) {
|
|
8497
|
-
await this.remoteCommands.connect(opts);
|
|
8498
|
-
}
|
|
8499
8562
|
async gateway(opts) {
|
|
8500
8563
|
const uiOverrides = {
|
|
8501
8564
|
host: FORCED_PUBLIC_UI_HOST
|
|
@@ -8546,7 +8609,7 @@ ${this.logo} ${APP_NAME4} is ready! (${source})`);
|
|
|
8546
8609
|
await this.writeRestartSentinelFromExecContext("cli.restart");
|
|
8547
8610
|
const state = readServiceState();
|
|
8548
8611
|
if (state && isProcessRunning(state.pid)) {
|
|
8549
|
-
console.log(`Restarting ${
|
|
8612
|
+
console.log(`Restarting ${APP_NAME5}...`);
|
|
8550
8613
|
await this.serviceCommands.stopService();
|
|
8551
8614
|
} else if (state) {
|
|
8552
8615
|
clearServiceState();
|
|
@@ -8585,8 +8648,8 @@ ${this.logo} ${APP_NAME4} is ready! (${source})`);
|
|
|
8585
8648
|
await this.serviceCommands.stopService();
|
|
8586
8649
|
}
|
|
8587
8650
|
async agent(opts) {
|
|
8588
|
-
const configPath =
|
|
8589
|
-
const config2 = resolveConfigSecrets3(
|
|
8651
|
+
const configPath = getConfigPath7();
|
|
8652
|
+
const config2 = resolveConfigSecrets3(loadConfig14(), { configPath });
|
|
8590
8653
|
const workspace = getWorkspacePath10(config2.agents.defaults.workspace);
|
|
8591
8654
|
const pluginRegistry = loadPluginRegistry(config2, workspace);
|
|
8592
8655
|
const extensionRegistry = toExtensionRegistry(pluginRegistry);
|
|
@@ -8594,7 +8657,7 @@ ${this.logo} ${APP_NAME4} is ready! (${source})`);
|
|
|
8594
8657
|
const pluginChannelBindings = getPluginChannelBindings4(pluginRegistry);
|
|
8595
8658
|
setPluginRuntimeBridge2({
|
|
8596
8659
|
loadConfig: () => toPluginConfigView(
|
|
8597
|
-
resolveConfigSecrets3(
|
|
8660
|
+
resolveConfigSecrets3(loadConfig14(), { configPath }),
|
|
8598
8661
|
pluginChannelBindings
|
|
8599
8662
|
),
|
|
8600
8663
|
writeConfigFile: async (nextConfigView) => {
|
|
@@ -8603,13 +8666,13 @@ ${this.logo} ${APP_NAME4} is ready! (${source})`);
|
|
|
8603
8666
|
"plugin runtime writeConfigFile expects an object config"
|
|
8604
8667
|
);
|
|
8605
8668
|
}
|
|
8606
|
-
const current =
|
|
8669
|
+
const current = loadConfig14();
|
|
8607
8670
|
const next = mergePluginConfigView(
|
|
8608
8671
|
current,
|
|
8609
8672
|
nextConfigView,
|
|
8610
8673
|
pluginChannelBindings
|
|
8611
8674
|
);
|
|
8612
|
-
|
|
8675
|
+
saveConfig10(next);
|
|
8613
8676
|
}
|
|
8614
8677
|
});
|
|
8615
8678
|
try {
|
|
@@ -8635,7 +8698,7 @@ ${this.logo} ${APP_NAME4} is ready! (${source})`);
|
|
|
8635
8698
|
resolveMessageToolHints: ({ channel, accountId }) => resolvePluginChannelMessageToolHints2({
|
|
8636
8699
|
registry: pluginRegistry,
|
|
8637
8700
|
channel,
|
|
8638
|
-
cfg: resolveConfigSecrets3(
|
|
8701
|
+
cfg: resolveConfigSecrets3(loadConfig14(), { configPath }),
|
|
8639
8702
|
accountId
|
|
8640
8703
|
})
|
|
8641
8704
|
});
|
|
@@ -8654,10 +8717,10 @@ ${this.logo} ${APP_NAME4} is ready! (${source})`);
|
|
|
8654
8717
|
`${this.logo} Interactive mode (type exit or Ctrl+C to quit)
|
|
8655
8718
|
`
|
|
8656
8719
|
);
|
|
8657
|
-
const historyFile =
|
|
8720
|
+
const historyFile = join8(getDataDir9(), "history", "cli_history");
|
|
8658
8721
|
const historyDir = resolve12(historyFile, "..");
|
|
8659
|
-
|
|
8660
|
-
const history =
|
|
8722
|
+
mkdirSync7(historyDir, { recursive: true });
|
|
8723
|
+
const history = existsSync12(historyFile) ? readFileSync10(historyFile, "utf-8").split("\n").filter(Boolean) : [];
|
|
8661
8724
|
const rl = createInterface3({
|
|
8662
8725
|
input: process.stdin,
|
|
8663
8726
|
output: process.stdout
|
|
@@ -8666,7 +8729,7 @@ ${this.logo} ${APP_NAME4} is ready! (${source})`);
|
|
|
8666
8729
|
const merged = history.concat(
|
|
8667
8730
|
rl.history ?? []
|
|
8668
8731
|
);
|
|
8669
|
-
|
|
8732
|
+
writeFileSync6(historyFile, merged.join("\n"));
|
|
8670
8733
|
process.exit(0);
|
|
8671
8734
|
});
|
|
8672
8735
|
let running = true;
|
|
@@ -8736,7 +8799,7 @@ ${this.logo} ${APP_NAME4} is ready! (${source})`);
|
|
|
8736
8799
|
}
|
|
8737
8800
|
const state = readServiceState();
|
|
8738
8801
|
if (state && isProcessRunning(state.pid)) {
|
|
8739
|
-
console.log(`Tip: restart ${
|
|
8802
|
+
console.log(`Tip: restart ${APP_NAME5} to apply the update.`);
|
|
8740
8803
|
}
|
|
8741
8804
|
}
|
|
8742
8805
|
pluginsList(opts = {}) {
|
|
@@ -8830,7 +8893,7 @@ ${this.logo} ${APP_NAME4} is ready! (${source})`);
|
|
|
8830
8893
|
await this.diagnosticsCommands.doctor(opts);
|
|
8831
8894
|
}
|
|
8832
8895
|
async skillsInstall(options) {
|
|
8833
|
-
const config2 =
|
|
8896
|
+
const config2 = loadConfig14();
|
|
8834
8897
|
const workdir = resolveSkillsInstallWorkdir({
|
|
8835
8898
|
explicitWorkdir: options.workdir,
|
|
8836
8899
|
configuredWorkspace: config2.agents.defaults.workspace
|
|
@@ -8894,20 +8957,19 @@ ${this.logo} ${APP_NAME4} is ready! (${source})`);
|
|
|
8894
8957
|
// src/cli/index.ts
|
|
8895
8958
|
var program = new Command();
|
|
8896
8959
|
var runtime = new CliRuntime({ logo: LOGO });
|
|
8897
|
-
program.name(
|
|
8898
|
-
program.command("onboard").description(`Initialize ${
|
|
8899
|
-
program.command("init").description(`Initialize ${
|
|
8960
|
+
program.name(APP_NAME6).description(`${LOGO} ${APP_NAME6} - ${APP_TAGLINE}`).version(getPackageVersion(), "-v, --version", "show version");
|
|
8961
|
+
program.command("onboard").description(`Initialize ${APP_NAME6} configuration and workspace`).action(async () => runtime.onboard());
|
|
8962
|
+
program.command("init").description(`Initialize ${APP_NAME6} configuration and workspace`).option("-f, --force", "Overwrite existing template files").action(async (opts) => runtime.init({ force: Boolean(opts.force) }));
|
|
8900
8963
|
program.command("login").description("Login to NextClaw platform and save token into providers.nextclaw.apiKey").option("--api-base <url>", "Platform API base (supports /v1 suffix)").option("--email <email>", "Login email").option("--password <password>", "Login password").option("--register", "Register first, then login", false).action(async (opts) => runtime.login(opts));
|
|
8901
|
-
|
|
8902
|
-
|
|
8903
|
-
program.command("
|
|
8904
|
-
program.command("
|
|
8905
|
-
program.command("
|
|
8906
|
-
program.command("
|
|
8907
|
-
program.command("
|
|
8908
|
-
program.command("stop").description(`Stop the ${APP_NAME5} background service`).action(async () => runtime.stop());
|
|
8964
|
+
registerRemoteCommands(program, runtime.remote);
|
|
8965
|
+
program.command("gateway").description(`Start the ${APP_NAME6} gateway`).option("-p, --port <port>", "Gateway port", "18790").option("-v, --verbose", "Verbose output", false).option("--ui", "Enable UI server", false).option("--ui-port <port>", "UI port").option("--ui-open", "Open browser when UI starts", false).action(async (opts) => runtime.gateway(opts));
|
|
8966
|
+
program.command("ui").description(`Start the ${APP_NAME6} UI with gateway`).option("--port <port>", "UI port").option("--no-open", "Disable opening browser").action(async (opts) => runtime.ui(opts));
|
|
8967
|
+
program.command("start").description(`Start the ${APP_NAME6} gateway + UI in the background`).option("--ui-port <port>", "UI port").option("--start-timeout <ms>", "Maximum wait time for startup readiness in milliseconds").option("--open", "Open browser after start", false).action(async (opts) => runtime.start(opts));
|
|
8968
|
+
program.command("restart").description(`Restart the ${APP_NAME6} background service`).option("--ui-port <port>", "UI port").option("--start-timeout <ms>", "Maximum wait time for startup readiness in milliseconds").option("--open", "Open browser after restart", false).action(async (opts) => runtime.restart(opts));
|
|
8969
|
+
program.command("serve").description(`Run the ${APP_NAME6} gateway + UI in the foreground`).option("--ui-port <port>", "UI port").option("--open", "Open browser after start", false).action(async (opts) => runtime.serve(opts));
|
|
8970
|
+
program.command("stop").description(`Stop the ${APP_NAME6} background service`).action(async () => runtime.stop());
|
|
8909
8971
|
program.command("agent").description("Interact with the agent directly").option("-m, --message <message>", "Message to send to the agent").option("-s, --session <session>", "Session ID", "cli:default").option("--model <model>", "Session model override for this run").option("--no-markdown", "Disable Markdown rendering").action(async (opts) => runtime.agent(opts));
|
|
8910
|
-
program.command("update").description(`Update ${
|
|
8972
|
+
program.command("update").description(`Update ${APP_NAME6}`).option("--timeout <ms>", "Update command timeout in milliseconds").action(async (opts) => runtime.update(opts));
|
|
8911
8973
|
var skills = program.command("skills").description("Manage skills");
|
|
8912
8974
|
skills.command("install <slug>").description("Install a skill from NextClaw marketplace").option("--api-base <url>", "Marketplace API base URL").option("--workdir <dir>", "Workspace directory to install into").option("--dir <dir>", "Skills directory name (default: skills)").option("-f, --force", "Overwrite existing skill files", false).action(async (slug, opts) => runtime.skillsInstall({ slug, ...opts, apiBaseUrl: opts.apiBase }));
|
|
8913
8975
|
var withRepeatableTag = (value, previous = []) => [...previous, value];
|
|
@@ -8952,6 +9014,6 @@ cron.command("add").requiredOption("-n, --name <name>", "Job name").requiredOpti
|
|
|
8952
9014
|
cron.command("remove <jobId>").action((jobId) => runtime.cronRemove(jobId));
|
|
8953
9015
|
cron.command("enable <jobId>").option("--disable", "Disable instead of enable").action((jobId, opts) => runtime.cronEnable(jobId, opts));
|
|
8954
9016
|
cron.command("run <jobId>").option("-f, --force", "Run even if disabled").action(async (jobId, opts) => runtime.cronRun(jobId, opts));
|
|
8955
|
-
program.command("status").description(`Show ${
|
|
8956
|
-
program.command("doctor").description(`Run ${
|
|
9017
|
+
program.command("status").description(`Show ${APP_NAME6} status`).option("--json", "Output JSON", false).option("--verbose", "Show extra diagnostics", false).option("--fix", "Fix stale service state when safe", false).action(async (opts) => runtime.status(opts));
|
|
9018
|
+
program.command("doctor").description(`Run ${APP_NAME6} diagnostics`).option("--json", "Output JSON", false).option("--verbose", "Show extra diagnostics", false).option("--fix", "Fix stale service state when safe", false).action(async (opts) => runtime.doctor(opts));
|
|
8957
9019
|
program.parseAsync(process.argv);
|