nextclaw 0.13.4 → 0.13.5
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 +438 -1078
- package/package.json +7 -7
- package/templates/USAGE.md +5 -15
package/dist/cli/index.js
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
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_NAME5, APP_TAGLINE } from "@nextclaw/core";
|
|
6
6
|
|
|
7
7
|
// src/cli/runtime.ts
|
|
8
8
|
import {
|
|
9
|
-
loadConfig as
|
|
10
|
-
saveConfig as
|
|
11
|
-
getConfigPath as
|
|
9
|
+
loadConfig as loadConfig13,
|
|
10
|
+
saveConfig as saveConfig9,
|
|
11
|
+
getConfigPath as getConfigPath6,
|
|
12
12
|
getDataDir as getDataDir9,
|
|
13
13
|
ConfigSchema as ConfigSchema2,
|
|
14
14
|
getWorkspacePath as getWorkspacePath10,
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
AgentLoop,
|
|
18
18
|
ProviderManager as ProviderManager2,
|
|
19
19
|
resolveConfigSecrets as resolveConfigSecrets3,
|
|
20
|
-
APP_NAME as
|
|
20
|
+
APP_NAME as APP_NAME4,
|
|
21
21
|
DEFAULT_WORKSPACE_DIR,
|
|
22
22
|
DEFAULT_WORKSPACE_PATH
|
|
23
23
|
} from "@nextclaw/core";
|
|
@@ -822,15 +822,6 @@ function writeServiceState(state) {
|
|
|
822
822
|
mkdirSync3(resolve4(path2, ".."), { recursive: true });
|
|
823
823
|
writeFileSync3(path2, JSON.stringify(state, null, 2));
|
|
824
824
|
}
|
|
825
|
-
function updateServiceState(updater) {
|
|
826
|
-
const current = readServiceState();
|
|
827
|
-
if (!current) {
|
|
828
|
-
return null;
|
|
829
|
-
}
|
|
830
|
-
const next = updater(current);
|
|
831
|
-
writeServiceState(next);
|
|
832
|
-
return next;
|
|
833
|
-
}
|
|
834
825
|
function clearServiceState() {
|
|
835
826
|
const path2 = resolveServiceStatePath();
|
|
836
827
|
if (existsSync4(path2)) {
|
|
@@ -3034,11 +3025,11 @@ var PlatformAuthCommands = class {
|
|
|
3034
3025
|
};
|
|
3035
3026
|
|
|
3036
3027
|
// src/cli/commands/remote.ts
|
|
3037
|
-
import { getConfigPath as
|
|
3038
|
-
import { hostname as hostname3 } from "os";
|
|
3039
|
-
|
|
3040
|
-
// src/cli/remote/remote-relay-bridge.ts
|
|
3028
|
+
import { getConfigPath as getConfigPath3, getDataDir as getDataDir4, loadConfig as loadConfig8 } from "@nextclaw/core";
|
|
3041
3029
|
import { ensureUiBridgeSecret } from "@nextclaw/server";
|
|
3030
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
|
|
3031
|
+
import { dirname as dirname2, join as join4 } from "path";
|
|
3032
|
+
import { hostname, platform as readPlatform } from "os";
|
|
3042
3033
|
function encodeBase64(bytes) {
|
|
3043
3034
|
return Buffer.from(bytes).toString("base64");
|
|
3044
3035
|
}
|
|
@@ -3048,122 +3039,9 @@ function decodeBase64(base64) {
|
|
|
3048
3039
|
}
|
|
3049
3040
|
return new Uint8Array(Buffer.from(base64, "base64"));
|
|
3050
3041
|
}
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
}
|
|
3055
|
-
async ensureLocalUiHealthy() {
|
|
3056
|
-
const response = await fetch(`${this.localOrigin}/api/health`);
|
|
3057
|
-
if (!response.ok) {
|
|
3058
|
-
throw new Error(`Local UI is not healthy at ${this.localOrigin}. Start NextClaw first.`);
|
|
3059
|
-
}
|
|
3060
|
-
}
|
|
3061
|
-
async forward(frame, socket) {
|
|
3062
|
-
const bridgeCookie = await this.requestBridgeCookie();
|
|
3063
|
-
const url = new URL(frame.path, this.localOrigin);
|
|
3064
|
-
const headers = this.createForwardHeaders(frame.headers, bridgeCookie);
|
|
3065
|
-
const response = await fetch(url, {
|
|
3066
|
-
method: frame.method,
|
|
3067
|
-
headers,
|
|
3068
|
-
body: frame.method === "GET" || frame.method === "HEAD" ? void 0 : decodeBase64(frame.bodyBase64)
|
|
3069
|
-
});
|
|
3070
|
-
const responseHeaders = Array.from(response.headers.entries()).filter(([key]) => {
|
|
3071
|
-
const lower = key.toLowerCase();
|
|
3072
|
-
return !["content-length", "connection", "transfer-encoding", "set-cookie"].includes(lower);
|
|
3073
|
-
});
|
|
3074
|
-
const contentType = response.headers.get("content-type")?.toLowerCase() ?? "";
|
|
3075
|
-
if (response.body && contentType.startsWith("text/event-stream")) {
|
|
3076
|
-
await this.sendStreamingResponse({ frame, response, responseHeaders, socket });
|
|
3077
|
-
return;
|
|
3078
|
-
}
|
|
3079
|
-
const responseBody = response.body ? new Uint8Array(await response.arrayBuffer()) : new Uint8Array();
|
|
3080
|
-
socket.send(JSON.stringify({
|
|
3081
|
-
type: "response",
|
|
3082
|
-
requestId: frame.requestId,
|
|
3083
|
-
status: response.status,
|
|
3084
|
-
headers: responseHeaders,
|
|
3085
|
-
bodyBase64: encodeBase64(responseBody)
|
|
3086
|
-
}));
|
|
3087
|
-
}
|
|
3088
|
-
createForwardHeaders(headersList, bridgeCookie) {
|
|
3089
|
-
const headers = new Headers();
|
|
3090
|
-
for (const [key, value] of headersList) {
|
|
3091
|
-
const lower = key.toLowerCase();
|
|
3092
|
-
if ([
|
|
3093
|
-
"host",
|
|
3094
|
-
"connection",
|
|
3095
|
-
"content-length",
|
|
3096
|
-
"cookie",
|
|
3097
|
-
"x-forwarded-for",
|
|
3098
|
-
"x-forwarded-proto",
|
|
3099
|
-
"cf-connecting-ip"
|
|
3100
|
-
].includes(lower)) {
|
|
3101
|
-
continue;
|
|
3102
|
-
}
|
|
3103
|
-
headers.set(key, value);
|
|
3104
|
-
}
|
|
3105
|
-
if (bridgeCookie) {
|
|
3106
|
-
headers.set("cookie", bridgeCookie);
|
|
3107
|
-
}
|
|
3108
|
-
return headers;
|
|
3109
|
-
}
|
|
3110
|
-
async requestBridgeCookie() {
|
|
3111
|
-
const response = await fetch(`${this.localOrigin}/api/auth/bridge`, {
|
|
3112
|
-
method: "POST",
|
|
3113
|
-
headers: {
|
|
3114
|
-
"x-nextclaw-ui-bridge-secret": ensureUiBridgeSecret()
|
|
3115
|
-
}
|
|
3116
|
-
});
|
|
3117
|
-
const payload = await response.json();
|
|
3118
|
-
if (!response.ok || !payload.ok) {
|
|
3119
|
-
throw new Error(payload.error?.message ?? `Failed to request local auth bridge (${response.status}).`);
|
|
3120
|
-
}
|
|
3121
|
-
return typeof payload.data?.cookie === "string" && payload.data.cookie.trim().length > 0 ? payload.data.cookie.trim() : null;
|
|
3122
|
-
}
|
|
3123
|
-
async sendStreamingResponse(params) {
|
|
3124
|
-
params.socket.send(JSON.stringify({
|
|
3125
|
-
type: "response.start",
|
|
3126
|
-
requestId: params.frame.requestId,
|
|
3127
|
-
status: params.response.status,
|
|
3128
|
-
headers: params.responseHeaders
|
|
3129
|
-
}));
|
|
3130
|
-
const reader = params.response.body?.getReader();
|
|
3131
|
-
if (!reader) {
|
|
3132
|
-
params.socket.send(JSON.stringify({
|
|
3133
|
-
type: "response.end",
|
|
3134
|
-
requestId: params.frame.requestId
|
|
3135
|
-
}));
|
|
3136
|
-
return;
|
|
3137
|
-
}
|
|
3138
|
-
try {
|
|
3139
|
-
while (true) {
|
|
3140
|
-
const { value, done } = await reader.read();
|
|
3141
|
-
if (done) {
|
|
3142
|
-
break;
|
|
3143
|
-
}
|
|
3144
|
-
if (value && value.length > 0) {
|
|
3145
|
-
params.socket.send(JSON.stringify({
|
|
3146
|
-
type: "response.chunk",
|
|
3147
|
-
requestId: params.frame.requestId,
|
|
3148
|
-
bodyBase64: encodeBase64(value)
|
|
3149
|
-
}));
|
|
3150
|
-
}
|
|
3151
|
-
}
|
|
3152
|
-
} finally {
|
|
3153
|
-
reader.releaseLock();
|
|
3154
|
-
}
|
|
3155
|
-
params.socket.send(JSON.stringify({
|
|
3156
|
-
type: "response.end",
|
|
3157
|
-
requestId: params.frame.requestId
|
|
3158
|
-
}));
|
|
3159
|
-
}
|
|
3160
|
-
};
|
|
3161
|
-
|
|
3162
|
-
// src/cli/remote/remote-platform-client.ts
|
|
3163
|
-
import { getConfigPath as getConfigPath3, getDataDir as getDataDir4, loadConfig as loadConfig8 } from "@nextclaw/core";
|
|
3164
|
-
import { existsSync as existsSync6, mkdirSync as mkdirSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
|
|
3165
|
-
import { dirname as dirname2, join as join4 } from "path";
|
|
3166
|
-
import { hostname, platform as readPlatform } from "os";
|
|
3042
|
+
function delay(ms) {
|
|
3043
|
+
return new Promise((resolveDelay) => setTimeout(resolveDelay, ms));
|
|
3044
|
+
}
|
|
3167
3045
|
function ensureDir(path2) {
|
|
3168
3046
|
mkdirSync4(path2, { recursive: true });
|
|
3169
3047
|
}
|
|
@@ -3182,82 +3060,9 @@ function writeJsonFile(path2, value) {
|
|
|
3182
3060
|
writeFileSync4(path2, `${JSON.stringify(value, null, 2)}
|
|
3183
3061
|
`, "utf-8");
|
|
3184
3062
|
}
|
|
3185
|
-
|
|
3186
|
-
if (value.length <= 12) {
|
|
3187
|
-
return "<redacted>";
|
|
3188
|
-
}
|
|
3189
|
-
return `${value.slice(0, 6)}...${value.slice(-4)}`;
|
|
3190
|
-
}
|
|
3191
|
-
function normalizeOptionalString3(value) {
|
|
3192
|
-
if (typeof value !== "string") {
|
|
3193
|
-
return void 0;
|
|
3194
|
-
}
|
|
3195
|
-
const trimmed = value.trim();
|
|
3196
|
-
return trimmed.length > 0 ? trimmed : void 0;
|
|
3197
|
-
}
|
|
3198
|
-
function delay(ms, signal) {
|
|
3199
|
-
return new Promise((resolveDelay, rejectDelay) => {
|
|
3200
|
-
const timer = setTimeout(() => {
|
|
3201
|
-
signal?.removeEventListener("abort", onAbort);
|
|
3202
|
-
resolveDelay();
|
|
3203
|
-
}, ms);
|
|
3204
|
-
const onAbort = () => {
|
|
3205
|
-
clearTimeout(timer);
|
|
3206
|
-
rejectDelay(new Error("Remote connector aborted."));
|
|
3207
|
-
};
|
|
3208
|
-
if (signal) {
|
|
3209
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
3210
|
-
}
|
|
3211
|
-
});
|
|
3212
|
-
}
|
|
3213
|
-
function redactWsUrl(url) {
|
|
3214
|
-
try {
|
|
3215
|
-
const parsed = new URL(url);
|
|
3216
|
-
const token = parsed.searchParams.get("token");
|
|
3217
|
-
if (token) {
|
|
3218
|
-
parsed.searchParams.set("token", maskToken(token));
|
|
3219
|
-
}
|
|
3220
|
-
return parsed.toString();
|
|
3221
|
-
} catch {
|
|
3222
|
-
return url;
|
|
3223
|
-
}
|
|
3224
|
-
}
|
|
3225
|
-
var RemotePlatformClient = class {
|
|
3063
|
+
var RemoteCommands = class {
|
|
3226
3064
|
remoteDir = join4(getDataDir4(), "remote");
|
|
3227
3065
|
devicePath = join4(this.remoteDir, "device.json");
|
|
3228
|
-
resolveRunContext(opts) {
|
|
3229
|
-
const { platformBase, token, config: config2 } = this.resolvePlatformAccess(opts);
|
|
3230
|
-
return {
|
|
3231
|
-
config: config2,
|
|
3232
|
-
platformBase,
|
|
3233
|
-
token,
|
|
3234
|
-
localOrigin: this.resolveLocalOrigin(config2, opts),
|
|
3235
|
-
displayName: this.resolveDisplayName(config2, opts),
|
|
3236
|
-
deviceInstallId: this.ensureDeviceInstallId(),
|
|
3237
|
-
autoReconnect: opts.once ? false : opts.autoReconnect ?? config2.remote.autoReconnect
|
|
3238
|
-
};
|
|
3239
|
-
}
|
|
3240
|
-
async registerDevice(params) {
|
|
3241
|
-
const response = await fetch(`${params.platformBase}/platform/remote/devices/register`, {
|
|
3242
|
-
method: "POST",
|
|
3243
|
-
headers: {
|
|
3244
|
-
"content-type": "application/json",
|
|
3245
|
-
authorization: `Bearer ${params.token}`
|
|
3246
|
-
},
|
|
3247
|
-
body: JSON.stringify({
|
|
3248
|
-
deviceInstallId: params.deviceInstallId,
|
|
3249
|
-
displayName: params.displayName,
|
|
3250
|
-
platform: readPlatform(),
|
|
3251
|
-
appVersion: getPackageVersion(),
|
|
3252
|
-
localOrigin: params.localOrigin
|
|
3253
|
-
})
|
|
3254
|
-
});
|
|
3255
|
-
const payload = await response.json();
|
|
3256
|
-
if (!response.ok || !payload.ok || !payload.data?.device) {
|
|
3257
|
-
throw new Error(payload.error?.message ?? `Failed to register remote device (${response.status}).`);
|
|
3258
|
-
}
|
|
3259
|
-
return payload.data.device;
|
|
3260
|
-
}
|
|
3261
3066
|
ensureDeviceInstallId() {
|
|
3262
3067
|
const existing = readJsonFile2(this.devicePath);
|
|
3263
3068
|
if (existing?.deviceInstallId?.trim()) {
|
|
@@ -3276,10 +3081,10 @@ var RemotePlatformClient = class {
|
|
|
3276
3081
|
if (!token) {
|
|
3277
3082
|
throw new Error('NextClaw platform token is missing. Run "nextclaw login" first.');
|
|
3278
3083
|
}
|
|
3279
|
-
const configuredApiBase =
|
|
3280
|
-
const rawApiBase =
|
|
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;
|
|
3281
3086
|
if (!rawApiBase) {
|
|
3282
|
-
throw new Error("Platform API base is missing. Pass --api-base
|
|
3087
|
+
throw new Error("Platform API base is missing. Pass --api-base or run nextclaw login.");
|
|
3283
3088
|
}
|
|
3284
3089
|
const { platformBase } = resolvePlatformApiBase({
|
|
3285
3090
|
explicitApiBase: rawApiBase,
|
|
@@ -3288,9 +3093,8 @@ var RemotePlatformClient = class {
|
|
|
3288
3093
|
return { platformBase, token, config: config2 };
|
|
3289
3094
|
}
|
|
3290
3095
|
resolveLocalOrigin(config2, opts) {
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
return explicitOrigin.replace(/\/$/, "");
|
|
3096
|
+
if (typeof opts.localOrigin === "string" && opts.localOrigin.trim().length > 0) {
|
|
3097
|
+
return opts.localOrigin.trim().replace(/\/$/, "");
|
|
3294
3098
|
}
|
|
3295
3099
|
const state = readServiceState();
|
|
3296
3100
|
if (state && isProcessRunning(state.pid) && Number.isFinite(state.uiPort)) {
|
|
@@ -3299,413 +3103,191 @@ var RemotePlatformClient = class {
|
|
|
3299
3103
|
const configuredPort = typeof config2.ui?.port === "number" && Number.isFinite(config2.ui.port) ? config2.ui.port : 18791;
|
|
3300
3104
|
return `http://127.0.0.1:${configuredPort}`;
|
|
3301
3105
|
}
|
|
3302
|
-
|
|
3303
|
-
|
|
3106
|
+
async ensureLocalUiHealthy(localOrigin) {
|
|
3107
|
+
const response = await fetch(`${localOrigin}/api/health`);
|
|
3108
|
+
if (!response.ok) {
|
|
3109
|
+
throw new Error(`Local UI is not healthy at ${localOrigin}. Start NextClaw first.`);
|
|
3110
|
+
}
|
|
3304
3111
|
}
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3112
|
+
async registerDevice(params) {
|
|
3113
|
+
const response = await fetch(`${params.platformBase}/platform/remote/devices/register`, {
|
|
3114
|
+
method: "POST",
|
|
3115
|
+
headers: {
|
|
3116
|
+
"content-type": "application/json",
|
|
3117
|
+
authorization: `Bearer ${params.token}`
|
|
3118
|
+
},
|
|
3119
|
+
body: JSON.stringify({
|
|
3120
|
+
deviceInstallId: params.deviceInstallId,
|
|
3121
|
+
displayName: params.displayName,
|
|
3122
|
+
platform: readPlatform(),
|
|
3123
|
+
appVersion: getPackageVersion(),
|
|
3124
|
+
localOrigin: params.localOrigin
|
|
3125
|
+
})
|
|
3126
|
+
});
|
|
3127
|
+
const payload = await response.json();
|
|
3128
|
+
if (!response.ok || !payload.ok || !payload.data?.device) {
|
|
3129
|
+
throw new Error(payload.error?.message ?? `Failed to register remote device (${response.status}).`);
|
|
3130
|
+
}
|
|
3131
|
+
return payload.data.device;
|
|
3311
3132
|
}
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
let aborted = false;
|
|
3318
|
-
const pingTimer = setInterval(() => {
|
|
3319
|
-
if (socket.readyState === WebSocket.OPEN) {
|
|
3320
|
-
socket.send(JSON.stringify({ type: "ping", at: (/* @__PURE__ */ new Date()).toISOString() }));
|
|
3321
|
-
}
|
|
3322
|
-
}, 15e3);
|
|
3323
|
-
const cleanup = () => {
|
|
3324
|
-
clearInterval(pingTimer);
|
|
3325
|
-
params.signal?.removeEventListener("abort", onAbort);
|
|
3326
|
-
};
|
|
3327
|
-
const finishResolve = (value) => {
|
|
3328
|
-
if (settled) {
|
|
3329
|
-
return;
|
|
3330
|
-
}
|
|
3331
|
-
settled = true;
|
|
3332
|
-
cleanup();
|
|
3333
|
-
resolve13(value);
|
|
3334
|
-
};
|
|
3335
|
-
const finishReject = (error) => {
|
|
3336
|
-
if (settled) {
|
|
3337
|
-
return;
|
|
3338
|
-
}
|
|
3339
|
-
settled = true;
|
|
3340
|
-
cleanup();
|
|
3341
|
-
reject(error);
|
|
3342
|
-
};
|
|
3343
|
-
const onAbort = () => {
|
|
3344
|
-
aborted = true;
|
|
3345
|
-
try {
|
|
3346
|
-
socket.close(1e3, "Remote connector aborted");
|
|
3347
|
-
} catch {
|
|
3348
|
-
finishResolve("aborted");
|
|
3349
|
-
}
|
|
3350
|
-
};
|
|
3351
|
-
if (params.signal) {
|
|
3352
|
-
if (params.signal.aborted) {
|
|
3353
|
-
onAbort();
|
|
3354
|
-
} else {
|
|
3355
|
-
params.signal.addEventListener("abort", onAbort, { once: true });
|
|
3356
|
-
}
|
|
3133
|
+
async requestBridgeCookie(localOrigin) {
|
|
3134
|
+
const response = await fetch(`${localOrigin}/api/auth/bridge`, {
|
|
3135
|
+
method: "POST",
|
|
3136
|
+
headers: {
|
|
3137
|
+
"x-nextclaw-ui-bridge-secret": ensureUiBridgeSecret()
|
|
3357
3138
|
}
|
|
3358
|
-
socket.addEventListener("open", () => {
|
|
3359
|
-
params.statusStore?.write({
|
|
3360
|
-
enabled: true,
|
|
3361
|
-
state: "connected",
|
|
3362
|
-
deviceId: params.deviceId,
|
|
3363
|
-
deviceName: params.displayName,
|
|
3364
|
-
platformBase: params.platformBase,
|
|
3365
|
-
localOrigin: params.localOrigin,
|
|
3366
|
-
lastConnectedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3367
|
-
lastError: null
|
|
3368
|
-
});
|
|
3369
|
-
this.logger.info(`\u2713 Remote connector connected: ${redactWsUrl(params.wsUrl)}`);
|
|
3370
|
-
});
|
|
3371
|
-
socket.addEventListener("message", (event) => {
|
|
3372
|
-
this.handleSocketMessage({ data: event.data, relayBridge: params.relayBridge, socket });
|
|
3373
|
-
});
|
|
3374
|
-
socket.addEventListener("close", () => {
|
|
3375
|
-
finishResolve(aborted ? "aborted" : "closed");
|
|
3376
|
-
});
|
|
3377
|
-
socket.addEventListener("error", () => {
|
|
3378
|
-
if (aborted) {
|
|
3379
|
-
finishResolve("aborted");
|
|
3380
|
-
return;
|
|
3381
|
-
}
|
|
3382
|
-
finishReject(new Error("Remote connector websocket failed."));
|
|
3383
|
-
});
|
|
3384
3139
|
});
|
|
3140
|
+
const payload = await response.json();
|
|
3141
|
+
if (!response.ok || !payload.ok) {
|
|
3142
|
+
throw new Error(payload.error?.message ?? `Failed to request local auth bridge (${response.status}).`);
|
|
3143
|
+
}
|
|
3144
|
+
return typeof payload.data?.cookie === "string" && payload.data.cookie.trim().length > 0 ? payload.data.cookie.trim() : null;
|
|
3385
3145
|
}
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3146
|
+
async handleRelayRequest(params) {
|
|
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;
|
|
3400
3162
|
}
|
|
3401
|
-
|
|
3402
|
-
}
|
|
3403
|
-
parseRelayFrame(data) {
|
|
3404
|
-
try {
|
|
3405
|
-
const frame = JSON.parse(String(data ?? ""));
|
|
3406
|
-
return frame.type === "request" ? frame : null;
|
|
3407
|
-
} catch {
|
|
3408
|
-
return null;
|
|
3163
|
+
headers.set(key, value);
|
|
3409
3164
|
}
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
if (params.device) {
|
|
3413
|
-
return params.device;
|
|
3165
|
+
if (bridgeCookie) {
|
|
3166
|
+
headers.set("cookie", bridgeCookie);
|
|
3414
3167
|
}
|
|
3415
|
-
const
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
localOrigin: params.context.localOrigin
|
|
3168
|
+
const bodyBytes = decodeBase64(params.frame.bodyBase64);
|
|
3169
|
+
const response = await fetch(url, {
|
|
3170
|
+
method: params.frame.method,
|
|
3171
|
+
headers,
|
|
3172
|
+
body: params.frame.method === "GET" || params.frame.method === "HEAD" ? void 0 : bodyBytes
|
|
3421
3173
|
});
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
return device;
|
|
3426
|
-
}
|
|
3427
|
-
writeRemoteState(statusStore, next) {
|
|
3428
|
-
statusStore?.write(next);
|
|
3429
|
-
}
|
|
3430
|
-
async runCycle(params) {
|
|
3431
|
-
try {
|
|
3432
|
-
this.writeRemoteState(params.opts.statusStore, {
|
|
3433
|
-
enabled: true,
|
|
3434
|
-
state: "connecting",
|
|
3435
|
-
deviceId: params.device?.id,
|
|
3436
|
-
deviceName: params.context.displayName,
|
|
3437
|
-
platformBase: params.context.platformBase,
|
|
3438
|
-
localOrigin: params.context.localOrigin,
|
|
3439
|
-
lastError: null
|
|
3440
|
-
});
|
|
3441
|
-
const device = await this.ensureDevice({ device: params.device, context: params.context });
|
|
3442
|
-
const wsUrl = `${params.context.platformBase.replace(/^http/i, "ws")}/platform/remote/connect?deviceId=${encodeURIComponent(device.id)}&token=${encodeURIComponent(params.context.token)}`;
|
|
3443
|
-
const outcome = await this.connectOnce({
|
|
3444
|
-
wsUrl,
|
|
3445
|
-
relayBridge: params.relayBridge,
|
|
3446
|
-
signal: params.opts.signal,
|
|
3447
|
-
statusStore: params.opts.statusStore,
|
|
3448
|
-
displayName: params.context.displayName,
|
|
3449
|
-
deviceId: device.id,
|
|
3450
|
-
platformBase: params.context.platformBase,
|
|
3451
|
-
localOrigin: params.context.localOrigin
|
|
3452
|
-
});
|
|
3453
|
-
if (outcome !== "aborted") {
|
|
3454
|
-
this.writeRemoteState(params.opts.statusStore, {
|
|
3455
|
-
enabled: true,
|
|
3456
|
-
state: "disconnected",
|
|
3457
|
-
deviceId: device.id,
|
|
3458
|
-
deviceName: params.context.displayName,
|
|
3459
|
-
platformBase: params.context.platformBase,
|
|
3460
|
-
localOrigin: params.context.localOrigin,
|
|
3461
|
-
lastError: null
|
|
3462
|
-
});
|
|
3463
|
-
}
|
|
3464
|
-
return { device, aborted: outcome === "aborted" };
|
|
3465
|
-
} catch (error) {
|
|
3466
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
3467
|
-
this.writeRemoteState(params.opts.statusStore, {
|
|
3468
|
-
enabled: true,
|
|
3469
|
-
state: "error",
|
|
3470
|
-
deviceId: params.device?.id,
|
|
3471
|
-
deviceName: params.context.displayName,
|
|
3472
|
-
platformBase: params.context.platformBase,
|
|
3473
|
-
localOrigin: params.context.localOrigin,
|
|
3474
|
-
lastError: message
|
|
3475
|
-
});
|
|
3476
|
-
this.logger.error(`Remote connector error: ${message}`);
|
|
3477
|
-
return { device: params.device, aborted: false };
|
|
3478
|
-
}
|
|
3479
|
-
}
|
|
3480
|
-
async run(opts = {}) {
|
|
3481
|
-
const context = this.platformClient.resolveRunContext(opts);
|
|
3482
|
-
const relayBridge = new RemoteRelayBridge(context.localOrigin);
|
|
3483
|
-
await relayBridge.ensureLocalUiHealthy();
|
|
3484
|
-
let device = null;
|
|
3485
|
-
while (!opts.signal?.aborted) {
|
|
3486
|
-
const cycle = await this.runCycle({ device, context, relayBridge, opts });
|
|
3487
|
-
device = cycle.device;
|
|
3488
|
-
if (cycle.aborted || !context.autoReconnect || opts.signal?.aborted) {
|
|
3489
|
-
break;
|
|
3490
|
-
}
|
|
3491
|
-
this.logger.warn("Remote connector disconnected. Reconnecting in 3s...");
|
|
3492
|
-
try {
|
|
3493
|
-
await delay(3e3, opts.signal);
|
|
3494
|
-
} catch {
|
|
3495
|
-
break;
|
|
3496
|
-
}
|
|
3497
|
-
}
|
|
3498
|
-
this.writeRemoteState(opts.statusStore, {
|
|
3499
|
-
enabled: opts.mode === "service" ? true : Boolean(context.config.remote.enabled),
|
|
3500
|
-
state: opts.signal?.aborted ? "disconnected" : "disabled",
|
|
3501
|
-
deviceId: device?.id,
|
|
3502
|
-
deviceName: context.displayName,
|
|
3503
|
-
platformBase: context.platformBase,
|
|
3504
|
-
localOrigin: context.localOrigin,
|
|
3505
|
-
lastError: null
|
|
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);
|
|
3506
3177
|
});
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
|
|
3515
|
-
|
|
3516
|
-
|
|
3517
|
-
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
}
|
|
3531
|
-
|
|
3532
|
-
const serviceState = readServiceState();
|
|
3533
|
-
if (serviceState?.remote) {
|
|
3534
|
-
return {
|
|
3535
|
-
configuredEnabled: Boolean(config2.remote.enabled),
|
|
3536
|
-
runtime: serviceState.remote
|
|
3537
|
-
};
|
|
3538
|
-
}
|
|
3539
|
-
if (config2.remote.enabled) {
|
|
3540
|
-
return {
|
|
3541
|
-
configuredEnabled: true,
|
|
3542
|
-
runtime: {
|
|
3543
|
-
...buildConfiguredRemoteState(config2),
|
|
3544
|
-
deviceName: normalizeOptionalString4(config2.remote.deviceName) ?? hostname2()
|
|
3545
|
-
}
|
|
3546
|
-
};
|
|
3547
|
-
}
|
|
3548
|
-
return {
|
|
3549
|
-
configuredEnabled: false,
|
|
3550
|
-
runtime: null
|
|
3551
|
-
};
|
|
3552
|
-
}
|
|
3553
|
-
var RemoteStatusStore = class {
|
|
3554
|
-
constructor(mode) {
|
|
3555
|
-
this.mode = mode;
|
|
3556
|
-
}
|
|
3557
|
-
write(next) {
|
|
3558
|
-
updateServiceState((state) => ({
|
|
3559
|
-
...state,
|
|
3560
|
-
remote: {
|
|
3561
|
-
...state.remote,
|
|
3562
|
-
...next,
|
|
3563
|
-
mode: this.mode,
|
|
3564
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3565
|
-
}
|
|
3566
|
-
}));
|
|
3567
|
-
}
|
|
3568
|
-
};
|
|
3569
|
-
|
|
3570
|
-
// src/cli/commands/remote.ts
|
|
3571
|
-
function normalizeOptionalString5(value) {
|
|
3572
|
-
if (typeof value !== "string") {
|
|
3573
|
-
return void 0;
|
|
3574
|
-
}
|
|
3575
|
-
const trimmed = value.trim();
|
|
3576
|
-
return trimmed.length > 0 ? trimmed : void 0;
|
|
3577
|
-
}
|
|
3578
|
-
function resolveConfiguredLocalOrigin(config2) {
|
|
3579
|
-
const state = readServiceState();
|
|
3580
|
-
if (state && isProcessRunning(state.pid) && Number.isFinite(state.uiPort)) {
|
|
3581
|
-
return `http://127.0.0.1:${state.uiPort}`;
|
|
3582
|
-
}
|
|
3583
|
-
const port = typeof config2.ui.port === "number" && Number.isFinite(config2.ui.port) ? config2.ui.port : 18791;
|
|
3584
|
-
return `http://127.0.0.1:${port}`;
|
|
3585
|
-
}
|
|
3586
|
-
async function probeLocalUi(localOrigin) {
|
|
3587
|
-
try {
|
|
3588
|
-
const response = await fetch(`${localOrigin}/api/health`);
|
|
3589
|
-
if (!response.ok) {
|
|
3590
|
-
return { ok: false, detail: `health returned ${response.status}` };
|
|
3591
|
-
}
|
|
3592
|
-
return { ok: true, detail: "health endpoint returned ok" };
|
|
3593
|
-
} catch (error) {
|
|
3594
|
-
return {
|
|
3595
|
-
ok: false,
|
|
3596
|
-
detail: error instanceof Error ? error.message : String(error)
|
|
3597
|
-
};
|
|
3598
|
-
}
|
|
3599
|
-
}
|
|
3600
|
-
var RemoteCommands = class {
|
|
3601
|
-
enableConfig(opts = {}) {
|
|
3602
|
-
const config2 = loadConfig10(getConfigPath4());
|
|
3603
|
-
const next = {
|
|
3604
|
-
...config2,
|
|
3605
|
-
remote: {
|
|
3606
|
-
...config2.remote,
|
|
3607
|
-
enabled: true,
|
|
3608
|
-
...normalizeOptionalString5(opts.apiBase) ? { platformApiBase: normalizeOptionalString5(opts.apiBase) ?? "" } : {},
|
|
3609
|
-
...normalizeOptionalString5(opts.name) ? { deviceName: normalizeOptionalString5(opts.name) ?? "" } : {}
|
|
3610
|
-
}
|
|
3611
|
-
};
|
|
3612
|
-
saveConfig7(next);
|
|
3613
|
-
return {
|
|
3614
|
-
changed: config2.remote.enabled !== next.remote.enabled || config2.remote.platformApiBase !== next.remote.platformApiBase || config2.remote.deviceName !== next.remote.deviceName,
|
|
3615
|
-
config: next
|
|
3616
|
-
};
|
|
3617
|
-
}
|
|
3618
|
-
disableConfig() {
|
|
3619
|
-
const config2 = loadConfig10(getConfigPath4());
|
|
3620
|
-
const next = {
|
|
3621
|
-
...config2,
|
|
3622
|
-
remote: {
|
|
3623
|
-
...config2.remote,
|
|
3624
|
-
enabled: false
|
|
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();
|
|
3625
3203
|
}
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
config: next
|
|
3631
|
-
};
|
|
3632
|
-
}
|
|
3633
|
-
async connect(opts = {}) {
|
|
3634
|
-
const connector = new RemoteConnector();
|
|
3635
|
-
await connector.run({
|
|
3636
|
-
...opts,
|
|
3637
|
-
mode: "foreground"
|
|
3638
|
-
});
|
|
3639
|
-
}
|
|
3640
|
-
async status(opts = {}) {
|
|
3641
|
-
const config2 = loadConfig10(getConfigPath4());
|
|
3642
|
-
const snapshot = resolveRemoteStatusSnapshot(config2);
|
|
3643
|
-
if (opts.json) {
|
|
3644
|
-
console.log(JSON.stringify(snapshot, null, 2));
|
|
3204
|
+
params.socket.send(JSON.stringify({
|
|
3205
|
+
type: "response.end",
|
|
3206
|
+
requestId: params.frame.requestId
|
|
3207
|
+
}));
|
|
3645
3208
|
return;
|
|
3646
3209
|
}
|
|
3647
|
-
const
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
);
|
|
3656
|
-
console.log(`Local origin: ${runtime2?.localOrigin ?? resolveConfiguredLocalOrigin(config2)}`);
|
|
3657
|
-
if (runtime2?.deviceId) {
|
|
3658
|
-
console.log(`Device ID: ${runtime2.deviceId}`);
|
|
3659
|
-
}
|
|
3660
|
-
if (runtime2?.lastConnectedAt) {
|
|
3661
|
-
console.log(`Last connected: ${runtime2.lastConnectedAt}`);
|
|
3662
|
-
}
|
|
3663
|
-
if (runtime2?.lastError) {
|
|
3664
|
-
console.log(`Last error: ${runtime2.lastError}`);
|
|
3665
|
-
}
|
|
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
|
+
}));
|
|
3666
3218
|
}
|
|
3667
|
-
async
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
|
|
3671
|
-
|
|
3672
|
-
|
|
3673
|
-
|
|
3674
|
-
|
|
3675
|
-
{
|
|
3676
|
-
|
|
3677
|
-
|
|
3678
|
-
|
|
3679
|
-
|
|
3680
|
-
|
|
3681
|
-
|
|
3682
|
-
|
|
3683
|
-
|
|
3684
|
-
|
|
3685
|
-
|
|
3686
|
-
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
|
|
3692
|
-
|
|
3693
|
-
|
|
3694
|
-
|
|
3695
|
-
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
|
|
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
|
+
}
|
|
3262
|
+
async connect(opts = {}) {
|
|
3263
|
+
const { platformBase, token, config: config2 } = this.resolvePlatformAccess(opts);
|
|
3264
|
+
const localOrigin = this.resolveLocalOrigin(config2, opts);
|
|
3265
|
+
await this.ensureLocalUiHealthy(localOrigin);
|
|
3266
|
+
const deviceInstallId = this.ensureDeviceInstallId();
|
|
3267
|
+
const displayName = typeof opts.name === "string" && opts.name.trim().length > 0 ? opts.name.trim() : hostname();
|
|
3268
|
+
const device = await this.registerDevice({
|
|
3269
|
+
platformBase,
|
|
3270
|
+
token,
|
|
3271
|
+
deviceInstallId,
|
|
3272
|
+
displayName,
|
|
3273
|
+
localOrigin
|
|
3274
|
+
});
|
|
3275
|
+
console.log(`\u2713 Remote device registered: ${device.displayName} (${device.id})`);
|
|
3276
|
+
console.log(`\u2713 Local origin: ${localOrigin}`);
|
|
3277
|
+
console.log(`\u2713 Platform: ${platformBase}`);
|
|
3278
|
+
const wsUrl = `${platformBase.replace(/^http/i, "ws")}/platform/remote/connect?deviceId=${encodeURIComponent(device.id)}&token=${encodeURIComponent(token)}`;
|
|
3279
|
+
do {
|
|
3280
|
+
try {
|
|
3281
|
+
await this.connectOnce({ wsUrl, localOrigin });
|
|
3282
|
+
} catch (error) {
|
|
3283
|
+
console.error(`Remote connector error: ${error instanceof Error ? error.message : String(error)}`);
|
|
3699
3284
|
}
|
|
3700
|
-
|
|
3701
|
-
|
|
3702
|
-
|
|
3703
|
-
|
|
3704
|
-
|
|
3705
|
-
|
|
3706
|
-
for (const check of checks) {
|
|
3707
|
-
console.log(`${check.ok ? "\u2713" : "\u2717"} ${check.name}: ${check.detail}`);
|
|
3708
|
-
}
|
|
3285
|
+
if (opts.once) {
|
|
3286
|
+
break;
|
|
3287
|
+
}
|
|
3288
|
+
console.log("Remote connector disconnected. Reconnecting in 3s...");
|
|
3289
|
+
await delay(3e3);
|
|
3290
|
+
} while (!opts.once);
|
|
3709
3291
|
}
|
|
3710
3292
|
};
|
|
3711
3293
|
|
|
@@ -3714,100 +3296,14 @@ import { createServer as createNetServer } from "net";
|
|
|
3714
3296
|
import { existsSync as existsSync7, readFileSync as readFileSync7 } from "fs";
|
|
3715
3297
|
import { resolve as resolve8 } from "path";
|
|
3716
3298
|
import {
|
|
3717
|
-
APP_NAME
|
|
3718
|
-
getConfigPath as
|
|
3299
|
+
APP_NAME,
|
|
3300
|
+
getConfigPath as getConfigPath4,
|
|
3719
3301
|
getDataDir as getDataDir5,
|
|
3720
3302
|
getWorkspacePath as getWorkspacePath4,
|
|
3721
3303
|
hasSecretRef,
|
|
3722
|
-
loadConfig as
|
|
3304
|
+
loadConfig as loadConfig9
|
|
3723
3305
|
} from "@nextclaw/core";
|
|
3724
3306
|
import { listBuiltinProviders } from "@nextclaw/runtime";
|
|
3725
|
-
|
|
3726
|
-
// src/cli/commands/diagnostics-render.ts
|
|
3727
|
-
import { APP_NAME } from "@nextclaw/core";
|
|
3728
|
-
function printStatusReport(params) {
|
|
3729
|
-
const { logo, report, verbose } = params;
|
|
3730
|
-
console.log(`${logo} ${APP_NAME} Status`);
|
|
3731
|
-
console.log(`Level: ${report.level}`);
|
|
3732
|
-
console.log(`Generated: ${report.generatedAt}`);
|
|
3733
|
-
console.log("");
|
|
3734
|
-
printProcessSection(report);
|
|
3735
|
-
printEndpointSection(report);
|
|
3736
|
-
printProviderSection(report);
|
|
3737
|
-
printTextList("Fix actions", report.fixActions);
|
|
3738
|
-
printTextList("Issues", report.issues);
|
|
3739
|
-
printTextList("Recommendations", report.recommendations);
|
|
3740
|
-
if (verbose && report.logTail.length > 0) {
|
|
3741
|
-
console.log("");
|
|
3742
|
-
console.log("Recent logs:");
|
|
3743
|
-
for (const line of report.logTail) {
|
|
3744
|
-
console.log(line);
|
|
3745
|
-
}
|
|
3746
|
-
}
|
|
3747
|
-
}
|
|
3748
|
-
function printProcessSection(report) {
|
|
3749
|
-
const processLabel = report.process.running ? `running (PID ${report.process.pid})` : report.process.staleState ? "stale-state" : "stopped";
|
|
3750
|
-
console.log(`Process: ${processLabel}`);
|
|
3751
|
-
console.log(`State file: ${report.serviceStatePath} ${report.serviceStateExists ? "\u2713" : "\u2717"}`);
|
|
3752
|
-
if (report.process.startedAt) {
|
|
3753
|
-
console.log(`Started: ${report.process.startedAt}`);
|
|
3754
|
-
}
|
|
3755
|
-
console.log(`Managed health: ${report.health.managed.state} (${report.health.managed.detail})`);
|
|
3756
|
-
if (!report.process.running) {
|
|
3757
|
-
console.log(`Configured health: ${report.health.configured.state} (${report.health.configured.detail})`);
|
|
3758
|
-
}
|
|
3759
|
-
}
|
|
3760
|
-
function printEndpointSection(report) {
|
|
3761
|
-
console.log(`UI: ${report.endpoints.uiUrl ?? report.endpoints.configuredUiUrl}`);
|
|
3762
|
-
console.log(`API: ${report.endpoints.apiUrl ?? report.endpoints.configuredApiUrl}`);
|
|
3763
|
-
console.log(`Remote: ${report.remote.configuredEnabled ? "enabled" : "disabled"}${report.remote.runtime ? ` (${report.remote.runtime.state})` : ""}`);
|
|
3764
|
-
if (report.remote.runtime?.deviceName) {
|
|
3765
|
-
console.log(`Remote device: ${report.remote.runtime.deviceName}`);
|
|
3766
|
-
}
|
|
3767
|
-
if (report.remote.runtime?.platformBase) {
|
|
3768
|
-
console.log(`Remote platform: ${report.remote.runtime.platformBase}`);
|
|
3769
|
-
}
|
|
3770
|
-
if (report.remote.runtime?.lastError) {
|
|
3771
|
-
console.log(`Remote error: ${report.remote.runtime.lastError}`);
|
|
3772
|
-
}
|
|
3773
|
-
console.log(`Config: ${report.configPath} ${report.configExists ? "\u2713" : "\u2717"}`);
|
|
3774
|
-
console.log(`Workspace: ${report.workspacePath} ${report.workspaceExists ? "\u2713" : "\u2717"}`);
|
|
3775
|
-
console.log(`Model: ${report.model}`);
|
|
3776
|
-
}
|
|
3777
|
-
function printProviderSection(report) {
|
|
3778
|
-
for (const provider of report.providers) {
|
|
3779
|
-
console.log(`${provider.name}: ${provider.configured ? "\u2713" : "not set"}${provider.detail ? ` (${provider.detail})` : ""}`);
|
|
3780
|
-
}
|
|
3781
|
-
}
|
|
3782
|
-
function printDoctorReport(params) {
|
|
3783
|
-
console.log(`${params.logo} ${APP_NAME} Doctor`);
|
|
3784
|
-
console.log(`Generated: ${params.generatedAt}`);
|
|
3785
|
-
console.log("");
|
|
3786
|
-
for (const check of params.checks) {
|
|
3787
|
-
const icon = check.status === "pass" ? "\u2713" : check.status === "warn" ? "!" : "\u2717";
|
|
3788
|
-
console.log(`${icon} ${check.name}: ${check.detail}`);
|
|
3789
|
-
}
|
|
3790
|
-
printTextList("Recommendations", params.recommendations);
|
|
3791
|
-
if (params.verbose && params.logTail.length > 0) {
|
|
3792
|
-
console.log("");
|
|
3793
|
-
console.log("Recent logs:");
|
|
3794
|
-
for (const line of params.logTail) {
|
|
3795
|
-
console.log(line);
|
|
3796
|
-
}
|
|
3797
|
-
}
|
|
3798
|
-
}
|
|
3799
|
-
function printTextList(title, items) {
|
|
3800
|
-
if (items.length === 0) {
|
|
3801
|
-
return;
|
|
3802
|
-
}
|
|
3803
|
-
console.log("");
|
|
3804
|
-
console.log(`${title}:`);
|
|
3805
|
-
for (const item of items) {
|
|
3806
|
-
console.log(`- ${item}`);
|
|
3807
|
-
}
|
|
3808
|
-
}
|
|
3809
|
-
|
|
3810
|
-
// src/cli/commands/diagnostics.ts
|
|
3811
3307
|
var DiagnosticsCommands = class {
|
|
3812
3308
|
constructor(deps) {
|
|
3813
3309
|
this.deps = deps;
|
|
@@ -3822,7 +3318,56 @@ var DiagnosticsCommands = class {
|
|
|
3822
3318
|
process.exitCode = 0;
|
|
3823
3319
|
return;
|
|
3824
3320
|
}
|
|
3825
|
-
|
|
3321
|
+
console.log(`${this.deps.logo} ${APP_NAME} Status`);
|
|
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
|
+
}
|
|
3826
3371
|
process.exitCode = 0;
|
|
3827
3372
|
}
|
|
3828
3373
|
async doctor(opts = {}) {
|
|
@@ -3893,19 +3438,32 @@ var DiagnosticsCommands = class {
|
|
|
3893
3438
|
process.exitCode = exitCode;
|
|
3894
3439
|
return;
|
|
3895
3440
|
}
|
|
3896
|
-
|
|
3897
|
-
|
|
3898
|
-
|
|
3899
|
-
|
|
3900
|
-
|
|
3901
|
-
|
|
3902
|
-
|
|
3903
|
-
|
|
3441
|
+
console.log(`${this.deps.logo} ${APP_NAME} Doctor`);
|
|
3442
|
+
console.log(`Generated: ${report.generatedAt}`);
|
|
3443
|
+
console.log("");
|
|
3444
|
+
for (const check of checks) {
|
|
3445
|
+
const icon = check.status === "pass" ? "\u2713" : check.status === "warn" ? "!" : "\u2717";
|
|
3446
|
+
console.log(`${icon} ${check.name}: ${check.detail}`);
|
|
3447
|
+
}
|
|
3448
|
+
if (report.recommendations.length > 0) {
|
|
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
|
+
}
|
|
3904
3462
|
process.exitCode = exitCode;
|
|
3905
3463
|
}
|
|
3906
3464
|
async collectRuntimeStatus(params) {
|
|
3907
|
-
const configPath =
|
|
3908
|
-
const config2 =
|
|
3465
|
+
const configPath = getConfigPath4();
|
|
3466
|
+
const config2 = loadConfig9();
|
|
3909
3467
|
const workspacePath = getWorkspacePath4(config2.agents.defaults.workspace);
|
|
3910
3468
|
const serviceStatePath = resolve8(getDataDir5(), "run", "service.json");
|
|
3911
3469
|
const fixActions = [];
|
|
@@ -3925,23 +3483,59 @@ var DiagnosticsCommands = class {
|
|
|
3925
3483
|
const managedApiUrl = serviceState?.apiUrl ?? null;
|
|
3926
3484
|
const managedHealth = running && managedApiUrl ? await this.probeApiHealth(`${managedApiUrl}/health`) : { state: "unreachable", detail: "service not running" };
|
|
3927
3485
|
const configuredHealth = await this.probeApiHealth(`${configuredApiUrl}/health`, 900);
|
|
3928
|
-
const remote = resolveRemoteStatusSnapshot(config2);
|
|
3929
3486
|
const orphanSuspected = !running && configuredHealth.state === "ok";
|
|
3930
|
-
const providers =
|
|
3487
|
+
const providers = listBuiltinProviders().map((spec) => {
|
|
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
|
+
});
|
|
3931
3506
|
const issues = [];
|
|
3932
3507
|
const recommendations = [];
|
|
3933
|
-
|
|
3934
|
-
|
|
3935
|
-
|
|
3936
|
-
|
|
3937
|
-
|
|
3938
|
-
|
|
3939
|
-
|
|
3940
|
-
|
|
3941
|
-
|
|
3942
|
-
issues
|
|
3943
|
-
recommendations
|
|
3944
|
-
}
|
|
3508
|
+
if (!existsSync7(configPath)) {
|
|
3509
|
+
issues.push("Config file is missing.");
|
|
3510
|
+
recommendations.push(`Run ${APP_NAME} init to create config files.`);
|
|
3511
|
+
}
|
|
3512
|
+
if (!existsSync7(workspacePath)) {
|
|
3513
|
+
issues.push("Workspace directory does not exist.");
|
|
3514
|
+
recommendations.push(`Run ${APP_NAME} init to create workspace templates.`);
|
|
3515
|
+
}
|
|
3516
|
+
if (staleState) {
|
|
3517
|
+
issues.push("Service state is stale (state exists but process is not running).");
|
|
3518
|
+
recommendations.push(`Run ${APP_NAME} status --fix to clean stale state.`);
|
|
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
|
+
}
|
|
3945
3539
|
const logTail = params.verbose ? this.readLogTail(serviceState?.logPath ?? resolveServiceLogPath(), 25) : [];
|
|
3946
3540
|
const level = running ? managedHealth.state === "ok" ? issues.length > 0 ? "degraded" : "healthy" : "degraded" : "stopped";
|
|
3947
3541
|
const exitCode = 0;
|
|
@@ -3977,7 +3571,6 @@ var DiagnosticsCommands = class {
|
|
|
3977
3571
|
issues,
|
|
3978
3572
|
recommendations,
|
|
3979
3573
|
logTail,
|
|
3980
|
-
remote,
|
|
3981
3574
|
level,
|
|
3982
3575
|
exitCode
|
|
3983
3576
|
};
|
|
@@ -4004,60 +3597,6 @@ var DiagnosticsCommands = class {
|
|
|
4004
3597
|
clearTimeout(timer);
|
|
4005
3598
|
}
|
|
4006
3599
|
}
|
|
4007
|
-
listProviderStatuses(config2) {
|
|
4008
|
-
return listBuiltinProviders().map((spec) => {
|
|
4009
|
-
const provider = config2.providers[spec.name];
|
|
4010
|
-
const apiKeyRefSet = hasSecretRef(config2, `providers.${spec.name}.apiKey`);
|
|
4011
|
-
if (!provider) {
|
|
4012
|
-
return { name: spec.displayName ?? spec.name, configured: false, detail: "missing config" };
|
|
4013
|
-
}
|
|
4014
|
-
if (spec.isLocal) {
|
|
4015
|
-
return {
|
|
4016
|
-
name: spec.displayName ?? spec.name,
|
|
4017
|
-
configured: Boolean(provider.apiBase),
|
|
4018
|
-
detail: provider.apiBase ? provider.apiBase : "apiBase not set"
|
|
4019
|
-
};
|
|
4020
|
-
}
|
|
4021
|
-
return {
|
|
4022
|
-
name: spec.displayName ?? spec.name,
|
|
4023
|
-
configured: Boolean(provider.apiKey) || apiKeyRefSet,
|
|
4024
|
-
detail: provider.apiKey ? "apiKey set" : apiKeyRefSet ? "apiKey ref set" : "apiKey not set"
|
|
4025
|
-
};
|
|
4026
|
-
});
|
|
4027
|
-
}
|
|
4028
|
-
collectRuntimeIssues(params) {
|
|
4029
|
-
if (!existsSync7(params.configPath)) {
|
|
4030
|
-
params.issues.push("Config file is missing.");
|
|
4031
|
-
params.recommendations.push(`Run ${APP_NAME2} init to create config files.`);
|
|
4032
|
-
}
|
|
4033
|
-
if (!existsSync7(params.workspacePath)) {
|
|
4034
|
-
params.issues.push("Workspace directory does not exist.");
|
|
4035
|
-
params.recommendations.push(`Run ${APP_NAME2} init to create workspace templates.`);
|
|
4036
|
-
}
|
|
4037
|
-
if (params.staleState) {
|
|
4038
|
-
params.issues.push("Service state is stale (state exists but process is not running).");
|
|
4039
|
-
params.recommendations.push(`Run ${APP_NAME2} status --fix to clean stale state.`);
|
|
4040
|
-
}
|
|
4041
|
-
if (params.running && params.managedHealth.state !== "ok") {
|
|
4042
|
-
params.issues.push(`Managed service health check failed: ${params.managedHealth.detail}`);
|
|
4043
|
-
params.recommendations.push(`Check logs at ${params.serviceState?.logPath ?? resolveServiceLogPath()}.`);
|
|
4044
|
-
}
|
|
4045
|
-
if (params.running && params.serviceState?.startupState === "degraded" && params.managedHealth.state !== "ok") {
|
|
4046
|
-
const startupHint = params.serviceState.startupLastProbeError ? ` (${params.serviceState.startupLastProbeError})` : "";
|
|
4047
|
-
params.issues.push(`Service is in degraded startup state${startupHint}.`);
|
|
4048
|
-
params.recommendations.push(`Wait and re-check ${APP_NAME2} status; if it does not recover, inspect logs and restart.`);
|
|
4049
|
-
}
|
|
4050
|
-
if (!params.running) {
|
|
4051
|
-
params.recommendations.push(`Run ${APP_NAME2} start to launch the service.`);
|
|
4052
|
-
}
|
|
4053
|
-
if (params.orphanSuspected) {
|
|
4054
|
-
params.issues.push("A service appears healthy on configured API endpoint, but state is missing/stale.");
|
|
4055
|
-
params.recommendations.push("Another process may be occupying the UI port; stop it or use --ui-port with a free port.");
|
|
4056
|
-
}
|
|
4057
|
-
if (!params.providers.some((provider) => provider.configured)) {
|
|
4058
|
-
params.recommendations.push("Configure at least one provider API key in UI or config before expecting agent replies.");
|
|
4059
|
-
}
|
|
4060
|
-
}
|
|
4061
3600
|
readLogTail(path2, maxLines = 25) {
|
|
4062
3601
|
if (!existsSync7(path2)) {
|
|
4063
3602
|
return [];
|
|
@@ -4121,8 +3660,8 @@ import {
|
|
|
4121
3660
|
redactConfigObject
|
|
4122
3661
|
} from "@nextclaw/core";
|
|
4123
3662
|
var hashRaw = (raw) => createHash("sha256").update(raw).digest("hex");
|
|
4124
|
-
var readConfigSnapshot = (
|
|
4125
|
-
const path2 =
|
|
3663
|
+
var readConfigSnapshot = (getConfigPath7) => {
|
|
3664
|
+
const path2 = getConfigPath7();
|
|
4126
3665
|
let raw = "";
|
|
4127
3666
|
let parsed = {};
|
|
4128
3667
|
if (existsSync8(path2)) {
|
|
@@ -4583,7 +4122,7 @@ var MissingProvider = class extends LLMProvider {
|
|
|
4583
4122
|
};
|
|
4584
4123
|
|
|
4585
4124
|
// src/cli/commands/service-marketplace-installer.ts
|
|
4586
|
-
import { getWorkspacePath as getWorkspacePath5, loadConfig as
|
|
4125
|
+
import { getWorkspacePath as getWorkspacePath5, loadConfig as loadConfig11 } from "@nextclaw/core";
|
|
4587
4126
|
import { existsSync as existsSync9, rmSync as rmSync4 } from "fs";
|
|
4588
4127
|
import { join as join5 } from "path";
|
|
4589
4128
|
|
|
@@ -4633,7 +4172,7 @@ var buildMarketplaceSkillInstallArgs = (params) => {
|
|
|
4633
4172
|
};
|
|
4634
4173
|
|
|
4635
4174
|
// src/cli/commands/service-mcp-marketplace-ops.ts
|
|
4636
|
-
import { loadConfig as
|
|
4175
|
+
import { loadConfig as loadConfig10, saveConfig as saveConfig7 } from "@nextclaw/core";
|
|
4637
4176
|
import { McpDoctorFacade as McpDoctorFacade2, McpMutationService as McpMutationService2 } from "@nextclaw/mcp";
|
|
4638
4177
|
var ServiceMcpMarketplaceOps = class {
|
|
4639
4178
|
constructor(options) {
|
|
@@ -4701,13 +4240,13 @@ var ServiceMcpMarketplaceOps = class {
|
|
|
4701
4240
|
}
|
|
4702
4241
|
createMutationService() {
|
|
4703
4242
|
return new McpMutationService2({
|
|
4704
|
-
getConfig: () =>
|
|
4705
|
-
saveConfig: (config2) =>
|
|
4243
|
+
getConfig: () => loadConfig10(),
|
|
4244
|
+
saveConfig: (config2) => saveConfig7(config2)
|
|
4706
4245
|
});
|
|
4707
4246
|
}
|
|
4708
4247
|
createDoctorFacade() {
|
|
4709
4248
|
return new McpDoctorFacade2({
|
|
4710
|
-
getConfig: () =>
|
|
4249
|
+
getConfig: () => loadConfig10()
|
|
4711
4250
|
});
|
|
4712
4251
|
}
|
|
4713
4252
|
};
|
|
@@ -4748,7 +4287,7 @@ var ServiceMarketplaceInstaller = class {
|
|
|
4748
4287
|
if (params.kind && params.kind !== "marketplace") {
|
|
4749
4288
|
throw new Error(`Unsupported marketplace skill kind: ${params.kind}`);
|
|
4750
4289
|
}
|
|
4751
|
-
const workspace = getWorkspacePath5(
|
|
4290
|
+
const workspace = getWorkspacePath5(loadConfig11().agents.defaults.workspace);
|
|
4752
4291
|
const args = buildMarketplaceSkillInstallArgs({
|
|
4753
4292
|
slug: params.slug,
|
|
4754
4293
|
workspace,
|
|
@@ -4787,7 +4326,7 @@ var ServiceMarketplaceInstaller = class {
|
|
|
4787
4326
|
return { message: result.message };
|
|
4788
4327
|
}
|
|
4789
4328
|
async uninstallSkill(slug) {
|
|
4790
|
-
const workspace = getWorkspacePath5(
|
|
4329
|
+
const workspace = getWorkspacePath5(loadConfig11().agents.defaults.workspace);
|
|
4791
4330
|
const targetDir = join5(workspace, "skills", slug);
|
|
4792
4331
|
if (!existsSync9(targetDir)) {
|
|
4793
4332
|
throw new Error(`Skill not installed in workspace: ${slug}`);
|
|
@@ -4914,35 +4453,6 @@ async function reloadServicePlugins(params) {
|
|
|
4914
4453
|
};
|
|
4915
4454
|
}
|
|
4916
4455
|
|
|
4917
|
-
// src/cli/commands/service-startup-support.ts
|
|
4918
|
-
var pluginGatewayLogger = {
|
|
4919
|
-
info: (message) => console.log(`[plugins] ${message}`),
|
|
4920
|
-
warn: (message) => console.warn(`[plugins] ${message}`),
|
|
4921
|
-
error: (message) => console.error(`[plugins] ${message}`),
|
|
4922
|
-
debug: (message) => console.debug(`[plugins] ${message}`)
|
|
4923
|
-
};
|
|
4924
|
-
function logPluginGatewayDiagnostics(diagnostics) {
|
|
4925
|
-
for (const diag of diagnostics) {
|
|
4926
|
-
const prefix = diag.pluginId ? `${diag.pluginId}: ` : "";
|
|
4927
|
-
const text = `${prefix}${diag.message}`;
|
|
4928
|
-
if (diag.level === "error") {
|
|
4929
|
-
console.error(`[plugins] ${text}`);
|
|
4930
|
-
} else {
|
|
4931
|
-
console.warn(`[plugins] ${text}`);
|
|
4932
|
-
}
|
|
4933
|
-
}
|
|
4934
|
-
}
|
|
4935
|
-
async function startGatewaySupportServices(params) {
|
|
4936
|
-
if (params.cronJobs > 0) {
|
|
4937
|
-
console.log(`\u2713 Cron: ${params.cronJobs} scheduled jobs`);
|
|
4938
|
-
}
|
|
4939
|
-
console.log("\u2713 Heartbeat: every 30m");
|
|
4940
|
-
params.remoteModule?.start();
|
|
4941
|
-
params.watchConfigFile();
|
|
4942
|
-
await params.startCron();
|
|
4943
|
-
await params.startHeartbeat();
|
|
4944
|
-
}
|
|
4945
|
-
|
|
4946
4456
|
// src/cli/commands/agent-runtime-pool.ts
|
|
4947
4457
|
import {
|
|
4948
4458
|
NativeAgentEngine,
|
|
@@ -5922,7 +5432,7 @@ function resolveRequestedToolNames(metadata) {
|
|
|
5922
5432
|
)
|
|
5923
5433
|
);
|
|
5924
5434
|
}
|
|
5925
|
-
function
|
|
5435
|
+
function normalizeOptionalString3(value) {
|
|
5926
5436
|
return normalizeString(value) ?? void 0;
|
|
5927
5437
|
}
|
|
5928
5438
|
function readMetadataModel(metadata) {
|
|
@@ -6042,7 +5552,7 @@ var NextclawNcpContextBuilder = class {
|
|
|
6042
5552
|
if (inboundModel) {
|
|
6043
5553
|
session.metadata.preferred_model = inboundModel;
|
|
6044
5554
|
}
|
|
6045
|
-
const effectiveModel =
|
|
5555
|
+
const effectiveModel = normalizeOptionalString3(session.metadata.preferred_model) ?? profile.model;
|
|
6046
5556
|
const clearThinking = requestMetadata.clear_thinking === true || requestMetadata.reset_thinking === true;
|
|
6047
5557
|
if (clearThinking) {
|
|
6048
5558
|
delete session.metadata.preferred_thinking;
|
|
@@ -6059,8 +5569,8 @@ var NextclawNcpContextBuilder = class {
|
|
|
6059
5569
|
model: effectiveModel,
|
|
6060
5570
|
sessionThinkingLevel: parseThinkingLevel(session.metadata.preferred_thinking) ?? null
|
|
6061
5571
|
});
|
|
6062
|
-
const channel =
|
|
6063
|
-
const chatId =
|
|
5572
|
+
const channel = normalizeOptionalString3(requestMetadata.channel) ?? normalizeOptionalString3(session.metadata.last_channel) ?? "ui";
|
|
5573
|
+
const chatId = normalizeOptionalString3(requestMetadata.chatId) ?? normalizeOptionalString3(requestMetadata.chat_id) ?? normalizeOptionalString3(session.metadata.last_to) ?? "web-ui";
|
|
6064
5574
|
session.metadata.last_channel = channel;
|
|
6065
5575
|
session.metadata.last_to = chatId;
|
|
6066
5576
|
const requestedSkillNames = resolveRequestedSkillNames(requestMetadata);
|
|
@@ -6778,119 +6288,6 @@ async function createUiNcpAgent(params) {
|
|
|
6778
6288
|
};
|
|
6779
6289
|
}
|
|
6780
6290
|
|
|
6781
|
-
// src/cli/remote/remote-service-module.ts
|
|
6782
|
-
var RemoteServiceModule = class {
|
|
6783
|
-
constructor(deps) {
|
|
6784
|
-
this.deps = deps;
|
|
6785
|
-
}
|
|
6786
|
-
abortController = null;
|
|
6787
|
-
runTask = null;
|
|
6788
|
-
statusStore = new RemoteStatusStore("service");
|
|
6789
|
-
start() {
|
|
6790
|
-
if (!this.deps.config.remote.enabled) {
|
|
6791
|
-
this.statusStore.write({
|
|
6792
|
-
enabled: false,
|
|
6793
|
-
state: "disabled",
|
|
6794
|
-
deviceName: void 0,
|
|
6795
|
-
deviceId: void 0,
|
|
6796
|
-
platformBase: void 0,
|
|
6797
|
-
localOrigin: this.deps.localOrigin,
|
|
6798
|
-
lastError: null,
|
|
6799
|
-
lastConnectedAt: null
|
|
6800
|
-
});
|
|
6801
|
-
return null;
|
|
6802
|
-
}
|
|
6803
|
-
const logger = this.deps.logger ?? {
|
|
6804
|
-
info: (message) => console.log(`[remote] ${message}`),
|
|
6805
|
-
warn: (message) => console.warn(`[remote] ${message}`),
|
|
6806
|
-
error: (message) => console.error(`[remote] ${message}`)
|
|
6807
|
-
};
|
|
6808
|
-
this.abortController = new AbortController();
|
|
6809
|
-
const connector = new RemoteConnector(logger);
|
|
6810
|
-
this.runTask = connector.run({
|
|
6811
|
-
mode: "service",
|
|
6812
|
-
signal: this.abortController.signal,
|
|
6813
|
-
autoReconnect: this.deps.config.remote.autoReconnect,
|
|
6814
|
-
localOrigin: this.deps.localOrigin,
|
|
6815
|
-
statusStore: this.statusStore
|
|
6816
|
-
});
|
|
6817
|
-
void this.runTask.catch((error) => {
|
|
6818
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
6819
|
-
this.statusStore.write({
|
|
6820
|
-
enabled: true,
|
|
6821
|
-
state: "error",
|
|
6822
|
-
deviceName: this.deps.config.remote.deviceName || void 0,
|
|
6823
|
-
deviceId: void 0,
|
|
6824
|
-
platformBase: this.deps.config.remote.platformApiBase || void 0,
|
|
6825
|
-
localOrigin: this.deps.localOrigin,
|
|
6826
|
-
lastError: message
|
|
6827
|
-
});
|
|
6828
|
-
logger.error(message);
|
|
6829
|
-
});
|
|
6830
|
-
return this.runTask;
|
|
6831
|
-
}
|
|
6832
|
-
async stop() {
|
|
6833
|
-
this.abortController?.abort();
|
|
6834
|
-
try {
|
|
6835
|
-
await this.runTask;
|
|
6836
|
-
} catch {
|
|
6837
|
-
} finally {
|
|
6838
|
-
this.abortController = null;
|
|
6839
|
-
this.runTask = null;
|
|
6840
|
-
}
|
|
6841
|
-
}
|
|
6842
|
-
};
|
|
6843
|
-
|
|
6844
|
-
// src/cli/commands/service-remote-runtime.ts
|
|
6845
|
-
function createManagedRemoteModule(params) {
|
|
6846
|
-
if (!params.config.ui.enabled) {
|
|
6847
|
-
return null;
|
|
6848
|
-
}
|
|
6849
|
-
return new RemoteServiceModule({
|
|
6850
|
-
config: params.config,
|
|
6851
|
-
localOrigin: params.localOrigin,
|
|
6852
|
-
logger: {
|
|
6853
|
-
info: (message) => console.log(`[remote] ${message}`),
|
|
6854
|
-
warn: (message) => console.warn(`[remote] ${message}`),
|
|
6855
|
-
error: (message) => console.error(`[remote] ${message}`)
|
|
6856
|
-
}
|
|
6857
|
-
});
|
|
6858
|
-
}
|
|
6859
|
-
function writeInitialManagedServiceState(params) {
|
|
6860
|
-
writeServiceState({
|
|
6861
|
-
pid: params.snapshot.pid,
|
|
6862
|
-
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6863
|
-
uiUrl: params.snapshot.uiUrl,
|
|
6864
|
-
apiUrl: params.snapshot.apiUrl,
|
|
6865
|
-
uiHost: params.snapshot.uiHost,
|
|
6866
|
-
uiPort: params.snapshot.uiPort,
|
|
6867
|
-
logPath: params.snapshot.logPath,
|
|
6868
|
-
startupLastProbeError: null,
|
|
6869
|
-
startupTimeoutMs: params.readinessTimeoutMs,
|
|
6870
|
-
startupCheckedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6871
|
-
...params.config.remote.enabled ? { remote: buildConfiguredRemoteState(params.config) } : {}
|
|
6872
|
-
});
|
|
6873
|
-
}
|
|
6874
|
-
function writeReadyManagedServiceState(params) {
|
|
6875
|
-
const currentState = readServiceState();
|
|
6876
|
-
const state = {
|
|
6877
|
-
pid: params.snapshot.pid,
|
|
6878
|
-
startedAt: currentState?.startedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
6879
|
-
uiUrl: params.snapshot.uiUrl,
|
|
6880
|
-
apiUrl: params.snapshot.apiUrl,
|
|
6881
|
-
uiHost: params.snapshot.uiHost,
|
|
6882
|
-
uiPort: params.snapshot.uiPort,
|
|
6883
|
-
logPath: params.snapshot.logPath,
|
|
6884
|
-
startupState: params.readiness.ready ? "ready" : "degraded",
|
|
6885
|
-
startupLastProbeError: params.readiness.lastProbeError,
|
|
6886
|
-
startupTimeoutMs: params.readinessTimeoutMs,
|
|
6887
|
-
startupCheckedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6888
|
-
...currentState?.remote ? { remote: currentState.remote } : {}
|
|
6889
|
-
};
|
|
6890
|
-
writeServiceState(state);
|
|
6891
|
-
return state;
|
|
6892
|
-
}
|
|
6893
|
-
|
|
6894
6291
|
// src/cli/commands/ui-chat-run-coordinator.ts
|
|
6895
6292
|
import { existsSync as existsSync10, mkdirSync as mkdirSync5, readdirSync as readdirSync2, readFileSync as readFileSync9, writeFileSync as writeFileSync5 } from "fs";
|
|
6896
6293
|
import { join as join6 } from "path";
|
|
@@ -7511,22 +6908,22 @@ var UiChatRunCoordinator = class {
|
|
|
7511
6908
|
|
|
7512
6909
|
// src/cli/commands/service.ts
|
|
7513
6910
|
var {
|
|
7514
|
-
APP_NAME:
|
|
6911
|
+
APP_NAME: APP_NAME2,
|
|
7515
6912
|
ChannelManager: ChannelManager2,
|
|
7516
6913
|
CronService: CronService2,
|
|
7517
6914
|
getApiBase,
|
|
7518
|
-
getConfigPath:
|
|
6915
|
+
getConfigPath: getConfigPath5,
|
|
7519
6916
|
getDataDir: getDataDir7,
|
|
7520
6917
|
getProvider,
|
|
7521
6918
|
getProviderName,
|
|
7522
6919
|
getWorkspacePath: getWorkspacePath9,
|
|
7523
6920
|
HeartbeatService,
|
|
7524
6921
|
LiteLLMProvider,
|
|
7525
|
-
loadConfig:
|
|
6922
|
+
loadConfig: loadConfig12,
|
|
7526
6923
|
MessageBus,
|
|
7527
6924
|
ProviderManager,
|
|
7528
6925
|
resolveConfigSecrets: resolveConfigSecrets2,
|
|
7529
|
-
saveConfig:
|
|
6926
|
+
saveConfig: saveConfig8,
|
|
7530
6927
|
SessionManager,
|
|
7531
6928
|
parseAgentScopedSessionKey: parseAgentScopedSessionKey3
|
|
7532
6929
|
} = NextclawCore;
|
|
@@ -7546,8 +6943,8 @@ var ServiceCommands = class {
|
|
|
7546
6943
|
async startGateway(options = {}) {
|
|
7547
6944
|
this.applyLiveConfigReload = null;
|
|
7548
6945
|
this.liveUiNcpAgent = null;
|
|
7549
|
-
const runtimeConfigPath =
|
|
7550
|
-
const config2 = resolveConfigSecrets2(
|
|
6946
|
+
const runtimeConfigPath = getConfigPath5();
|
|
6947
|
+
const config2 = resolveConfigSecrets2(loadConfig12(), { configPath: runtimeConfigPath });
|
|
7551
6948
|
const workspace = getWorkspacePath9(config2.agents.defaults.workspace);
|
|
7552
6949
|
let pluginRegistry = loadPluginRegistry(config2, workspace);
|
|
7553
6950
|
let extensionRegistry = toExtensionRegistry(pluginRegistry);
|
|
@@ -7560,11 +6957,27 @@ var ServiceCommands = class {
|
|
|
7560
6957
|
});
|
|
7561
6958
|
const sessionManager = new SessionManager(workspace);
|
|
7562
6959
|
let pluginGatewayHandles = [];
|
|
6960
|
+
const pluginGatewayLogger = {
|
|
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
|
+
};
|
|
7563
6977
|
const cronStorePath = join7(getDataDir7(), "cron", "jobs.json");
|
|
7564
6978
|
const cron2 = new CronService2(cronStorePath);
|
|
7565
6979
|
const uiConfig = resolveUiConfig(config2, options.uiOverrides);
|
|
7566
6980
|
const uiStaticDir = options.uiStaticDir === void 0 ? resolveUiStaticDir() : options.uiStaticDir;
|
|
7567
|
-
const localOrigin = resolveUiApiBase(uiConfig.host, uiConfig.port);
|
|
7568
6981
|
if (!provider) {
|
|
7569
6982
|
console.warn("Warning: No API key configured. The gateway is running, but agent replies are disabled until provider config is set.");
|
|
7570
6983
|
}
|
|
@@ -7576,7 +6989,7 @@ var ServiceCommands = class {
|
|
|
7576
6989
|
sessionManager,
|
|
7577
6990
|
providerManager,
|
|
7578
6991
|
makeProvider: (nextConfig) => this.makeProvider(nextConfig, { allowMissing: true }) ?? this.makeMissingProvider(nextConfig),
|
|
7579
|
-
loadConfig: () => resolveConfigSecrets2(
|
|
6992
|
+
loadConfig: () => resolveConfigSecrets2(loadConfig12(), { configPath: runtimeConfigPath }),
|
|
7580
6993
|
getExtensionChannels: () => extensionRegistry.channels,
|
|
7581
6994
|
onRestartRequired: (paths) => {
|
|
7582
6995
|
void this.deps.requestRestart({
|
|
@@ -7587,14 +7000,14 @@ var ServiceCommands = class {
|
|
|
7587
7000
|
}
|
|
7588
7001
|
});
|
|
7589
7002
|
this.applyLiveConfigReload = async () => {
|
|
7590
|
-
await reloader.applyReloadPlan(resolveConfigSecrets2(
|
|
7003
|
+
await reloader.applyReloadPlan(resolveConfigSecrets2(loadConfig12(), { configPath: runtimeConfigPath }));
|
|
7591
7004
|
};
|
|
7592
7005
|
const gatewayController = new GatewayControllerImpl({
|
|
7593
7006
|
reloader,
|
|
7594
7007
|
cron: cron2,
|
|
7595
7008
|
sessionManager,
|
|
7596
|
-
getConfigPath:
|
|
7597
|
-
saveConfig:
|
|
7009
|
+
getConfigPath: getConfigPath5,
|
|
7010
|
+
saveConfig: saveConfig8,
|
|
7598
7011
|
requestRestart: async (options2) => {
|
|
7599
7012
|
await this.deps.requestRestart({
|
|
7600
7013
|
reason: options2?.reason ?? "gateway tool restart",
|
|
@@ -7620,7 +7033,7 @@ var ServiceCommands = class {
|
|
|
7620
7033
|
resolveMessageToolHints: ({ channel, accountId }) => resolvePluginChannelMessageToolHints({
|
|
7621
7034
|
registry: pluginRegistry,
|
|
7622
7035
|
channel,
|
|
7623
|
-
cfg: resolveConfigSecrets2(
|
|
7036
|
+
cfg: resolveConfigSecrets2(loadConfig12(), { configPath: runtimeConfigPath }),
|
|
7624
7037
|
accountId
|
|
7625
7038
|
})
|
|
7626
7039
|
});
|
|
@@ -7653,14 +7066,14 @@ var ServiceCommands = class {
|
|
|
7653
7066
|
});
|
|
7654
7067
|
let pluginChannelBindings = getPluginChannelBindings3(pluginRegistry);
|
|
7655
7068
|
setPluginRuntimeBridge({
|
|
7656
|
-
loadConfig: () => toPluginConfigView(resolveConfigSecrets2(
|
|
7069
|
+
loadConfig: () => toPluginConfigView(resolveConfigSecrets2(loadConfig12(), { configPath: runtimeConfigPath }), pluginChannelBindings),
|
|
7657
7070
|
writeConfigFile: async (nextConfigView) => {
|
|
7658
7071
|
if (!nextConfigView || typeof nextConfigView !== "object" || Array.isArray(nextConfigView)) {
|
|
7659
7072
|
throw new Error("plugin runtime writeConfigFile expects an object config");
|
|
7660
7073
|
}
|
|
7661
|
-
const current =
|
|
7074
|
+
const current = loadConfig12();
|
|
7662
7075
|
const next = mergePluginConfigView(current, nextConfigView, pluginChannelBindings);
|
|
7663
|
-
|
|
7076
|
+
saveConfig8(next);
|
|
7664
7077
|
},
|
|
7665
7078
|
dispatchReplyWithBufferedBlockDispatcher: async ({ ctx, dispatcherOptions }) => {
|
|
7666
7079
|
const bodyForAgent = typeof ctx.BodyForAgent === "string" ? ctx.BodyForAgent : "";
|
|
@@ -7734,23 +7147,23 @@ var ServiceCommands = class {
|
|
|
7734
7147
|
providerManager,
|
|
7735
7148
|
bus,
|
|
7736
7149
|
gatewayController,
|
|
7737
|
-
() => resolveConfigSecrets2(
|
|
7150
|
+
() => resolveConfigSecrets2(loadConfig12(), { configPath: runtimeConfigPath }),
|
|
7738
7151
|
() => extensionRegistry,
|
|
7739
7152
|
({ channel, accountId }) => resolvePluginChannelMessageToolHints({
|
|
7740
7153
|
registry: pluginRegistry,
|
|
7741
7154
|
channel,
|
|
7742
|
-
cfg: resolveConfigSecrets2(
|
|
7155
|
+
cfg: resolveConfigSecrets2(loadConfig12(), { configPath: runtimeConfigPath }),
|
|
7743
7156
|
accountId
|
|
7744
7157
|
})
|
|
7745
7158
|
);
|
|
7746
|
-
const
|
|
7747
|
-
|
|
7748
|
-
|
|
7749
|
-
|
|
7750
|
-
|
|
7751
|
-
|
|
7752
|
-
|
|
7753
|
-
|
|
7159
|
+
const cronStatus = cron2.status();
|
|
7160
|
+
if (cronStatus.jobs > 0) {
|
|
7161
|
+
console.log(`\u2713 Cron: ${cronStatus.jobs} scheduled jobs`);
|
|
7162
|
+
}
|
|
7163
|
+
console.log("\u2713 Heartbeat: every 30m");
|
|
7164
|
+
this.watchConfigFile(reloader);
|
|
7165
|
+
await cron2.start();
|
|
7166
|
+
await heartbeat.start();
|
|
7754
7167
|
try {
|
|
7755
7168
|
const startedPluginGateways = await startPluginChannelGateways2({
|
|
7756
7169
|
registry: pluginRegistry,
|
|
@@ -7764,7 +7177,6 @@ var ServiceCommands = class {
|
|
|
7764
7177
|
} finally {
|
|
7765
7178
|
this.applyLiveConfigReload = null;
|
|
7766
7179
|
this.liveUiNcpAgent = null;
|
|
7767
|
-
await remoteModule?.stop();
|
|
7768
7180
|
await stopPluginChannelGateways2(pluginGatewayHandles);
|
|
7769
7181
|
setPluginRuntimeBridge(null);
|
|
7770
7182
|
}
|
|
@@ -7777,7 +7189,7 @@ var ServiceCommands = class {
|
|
|
7777
7189
|
return trimmed || void 0;
|
|
7778
7190
|
}
|
|
7779
7191
|
watchConfigFile(reloader) {
|
|
7780
|
-
const configPath = resolve10(
|
|
7192
|
+
const configPath = resolve10(getConfigPath5());
|
|
7781
7193
|
const watcher = chokidar.watch(configPath, {
|
|
7782
7194
|
ignoreInitial: true,
|
|
7783
7195
|
awaitWriteFinish: { stabilityThreshold: 200, pollInterval: 50 }
|
|
@@ -7898,7 +7310,7 @@ var ServiceCommands = class {
|
|
|
7898
7310
|
});
|
|
7899
7311
|
}
|
|
7900
7312
|
async runForeground(options) {
|
|
7901
|
-
const config2 =
|
|
7313
|
+
const config2 = loadConfig12();
|
|
7902
7314
|
const uiConfig = resolveUiConfig(config2, options.uiOverrides);
|
|
7903
7315
|
const uiUrl = resolveUiApiBase(uiConfig.host, uiConfig.port);
|
|
7904
7316
|
if (options.open) {
|
|
@@ -7911,14 +7323,14 @@ var ServiceCommands = class {
|
|
|
7911
7323
|
});
|
|
7912
7324
|
}
|
|
7913
7325
|
async startService(options) {
|
|
7914
|
-
const config2 =
|
|
7326
|
+
const config2 = loadConfig12();
|
|
7915
7327
|
const uiConfig = resolveUiConfig(config2, options.uiOverrides);
|
|
7916
7328
|
const uiUrl = resolveUiApiBase(uiConfig.host, uiConfig.port);
|
|
7917
7329
|
const apiUrl = `${uiUrl}/api`;
|
|
7918
7330
|
const staticDir = resolveUiStaticDir();
|
|
7919
7331
|
const existing = readServiceState();
|
|
7920
7332
|
if (existing && isProcessRunning(existing.pid)) {
|
|
7921
|
-
console.log(`\u2713 ${
|
|
7333
|
+
console.log(`\u2713 ${APP_NAME2} is already running (PID ${existing.pid})`);
|
|
7922
7334
|
console.log(`UI: ${existing.uiUrl}`);
|
|
7923
7335
|
console.log(`API: ${existing.apiUrl}`);
|
|
7924
7336
|
const parsedUi = (() => {
|
|
@@ -7966,7 +7378,7 @@ var ServiceCommands = class {
|
|
|
7966
7378
|
healthUrl
|
|
7967
7379
|
});
|
|
7968
7380
|
if (!portPreflight.ok) {
|
|
7969
|
-
console.error(`Error: Cannot start ${
|
|
7381
|
+
console.error(`Error: Cannot start ${APP_NAME2} because UI port ${uiConfig.port} is already occupied.`);
|
|
7970
7382
|
console.error(portPreflight.message);
|
|
7971
7383
|
return;
|
|
7972
7384
|
}
|
|
@@ -7981,8 +7393,10 @@ var ServiceCommands = class {
|
|
|
7981
7393
|
logPath,
|
|
7982
7394
|
`start requested: ui=${uiConfig.host}:${uiConfig.port}, readinessTimeoutMs=${readinessTimeoutMs}`
|
|
7983
7395
|
);
|
|
7984
|
-
console.log(`Starting ${
|
|
7985
|
-
const serveArgs = buildServeArgs({
|
|
7396
|
+
console.log(`Starting ${APP_NAME2} background service (readiness timeout ${Math.ceil(readinessTimeoutMs / 1e3)}s)...`);
|
|
7397
|
+
const serveArgs = buildServeArgs({
|
|
7398
|
+
uiPort: uiConfig.port
|
|
7399
|
+
});
|
|
7986
7400
|
this.appendStartupStage(logPath, `spawning background process: ${process.execPath} ${[...process.execArgv, ...serveArgs].join(" ")}`);
|
|
7987
7401
|
const child = spawn2(process.execPath, [...process.execArgv, ...serveArgs], {
|
|
7988
7402
|
env: process.env,
|
|
@@ -8003,11 +7417,6 @@ var ServiceCommands = class {
|
|
|
8003
7417
|
});
|
|
8004
7418
|
return;
|
|
8005
7419
|
}
|
|
8006
|
-
writeInitialManagedServiceState({
|
|
8007
|
-
config: config2,
|
|
8008
|
-
readinessTimeoutMs,
|
|
8009
|
-
snapshot: { pid: child.pid, uiUrl, apiUrl, uiHost: uiConfig.host, uiPort: uiConfig.port, logPath }
|
|
8010
|
-
});
|
|
8011
7420
|
this.appendStartupStage(logPath, `health probe started: ${healthUrl} (phase=quick, timeoutMs=${quickPhaseTimeoutMs})`);
|
|
8012
7421
|
let readiness = await this.waitForBackgroundServiceReady({
|
|
8013
7422
|
pid: child.pid,
|
|
@@ -8049,19 +7458,28 @@ var ServiceCommands = class {
|
|
|
8049
7458
|
);
|
|
8050
7459
|
}
|
|
8051
7460
|
child.unref();
|
|
8052
|
-
const state =
|
|
8053
|
-
|
|
8054
|
-
|
|
8055
|
-
|
|
8056
|
-
|
|
7461
|
+
const state = {
|
|
7462
|
+
pid: child.pid,
|
|
7463
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7464
|
+
uiUrl,
|
|
7465
|
+
apiUrl,
|
|
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);
|
|
8057
7475
|
if (!readiness.ready) {
|
|
8058
7476
|
const hint = readiness.lastProbeError ? ` Last probe error: ${readiness.lastProbeError}` : "";
|
|
8059
7477
|
console.warn(
|
|
8060
|
-
`Warning: ${
|
|
7478
|
+
`Warning: ${APP_NAME2} is running (PID ${state.pid}) but not healthy yet after ${Math.ceil(readinessTimeoutMs / 1e3)}s. Marked as degraded.${hint}`
|
|
8061
7479
|
);
|
|
8062
|
-
console.warn(`Tip: Run "${
|
|
7480
|
+
console.warn(`Tip: Run "${APP_NAME2} status --json" and check logs: ${logPath}`);
|
|
8063
7481
|
} else {
|
|
8064
|
-
console.log(`\u2713 ${
|
|
7482
|
+
console.log(`\u2713 ${APP_NAME2} started in background (PID ${state.pid})`);
|
|
8065
7483
|
}
|
|
8066
7484
|
console.log(`UI: ${uiUrl}`);
|
|
8067
7485
|
console.log(`API: ${apiUrl}`);
|
|
@@ -8083,7 +7501,7 @@ var ServiceCommands = class {
|
|
|
8083
7501
|
clearServiceState();
|
|
8084
7502
|
return;
|
|
8085
7503
|
}
|
|
8086
|
-
console.log(`Stopping ${
|
|
7504
|
+
console.log(`Stopping ${APP_NAME2} (PID ${state.pid})...`);
|
|
8087
7505
|
try {
|
|
8088
7506
|
process.kill(state.pid, "SIGTERM");
|
|
8089
7507
|
} catch (error) {
|
|
@@ -8101,7 +7519,7 @@ var ServiceCommands = class {
|
|
|
8101
7519
|
await waitForExit(state.pid, 2e3);
|
|
8102
7520
|
}
|
|
8103
7521
|
clearServiceState();
|
|
8104
|
-
console.log(`\u2713 ${
|
|
7522
|
+
console.log(`\u2713 ${APP_NAME2} stopped`);
|
|
8105
7523
|
}
|
|
8106
7524
|
async waitForBackgroundServiceReady(params) {
|
|
8107
7525
|
const startedAt = Date.now();
|
|
@@ -8319,7 +7737,7 @@ var ServiceCommands = class {
|
|
|
8319
7737
|
return null;
|
|
8320
7738
|
}
|
|
8321
7739
|
console.error("Error: No API key configured.");
|
|
8322
|
-
console.error(`Set one in ${
|
|
7740
|
+
console.error(`Set one in ${getConfigPath5()} under providers section`);
|
|
8323
7741
|
process.exit(1);
|
|
8324
7742
|
}
|
|
8325
7743
|
return new LiteLLMProvider({
|
|
@@ -8356,8 +7774,8 @@ var ServiceCommands = class {
|
|
|
8356
7774
|
}
|
|
8357
7775
|
printServiceControlHints() {
|
|
8358
7776
|
console.log("Service controls:");
|
|
8359
|
-
console.log(` - Check status: ${
|
|
8360
|
-
console.log(` - If you need to stop the service, run: ${
|
|
7777
|
+
console.log(` - Check status: ${APP_NAME2} status`);
|
|
7778
|
+
console.log(` - If you need to stop the service, run: ${APP_NAME2} stop`);
|
|
8361
7779
|
}
|
|
8362
7780
|
async startUiIfEnabled(uiConfig, uiStaticDir, cronService, runtimePool, sessionManager, providerManager, bus, gatewayController, getConfig, getExtensionRegistry, resolveMessageToolHints) {
|
|
8363
7781
|
if (!uiConfig.enabled) {
|
|
@@ -8446,7 +7864,7 @@ var ServiceCommands = class {
|
|
|
8446
7864
|
const uiServer = startUiServer({
|
|
8447
7865
|
host: uiConfig.host,
|
|
8448
7866
|
port: uiConfig.port,
|
|
8449
|
-
configPath:
|
|
7867
|
+
configPath: getConfigPath5(),
|
|
8450
7868
|
productVersion: getPackageVersion(),
|
|
8451
7869
|
staticDir: uiStaticDir ?? void 0,
|
|
8452
7870
|
cronService,
|
|
@@ -8534,7 +7952,7 @@ var ServiceCommands = class {
|
|
|
8534
7952
|
}
|
|
8535
7953
|
}
|
|
8536
7954
|
installBuiltinMarketplaceSkill(slug, force) {
|
|
8537
|
-
const workspace = getWorkspacePath9(
|
|
7955
|
+
const workspace = getWorkspacePath9(loadConfig12().agents.defaults.workspace);
|
|
8538
7956
|
const destination = join7(workspace, "skills", slug);
|
|
8539
7957
|
const destinationSkillFile = join7(destination, "SKILL.md");
|
|
8540
7958
|
if (existsSync11(destinationSkillFile) && !force) {
|
|
@@ -8611,58 +8029,12 @@ ${stderr}`.trim();
|
|
|
8611
8029
|
}
|
|
8612
8030
|
};
|
|
8613
8031
|
|
|
8614
|
-
// src/cli/remote/remote-runtime-actions.ts
|
|
8615
|
-
import { APP_NAME as APP_NAME4 } from "@nextclaw/core";
|
|
8616
|
-
var RemoteRuntimeActions = class {
|
|
8617
|
-
constructor(deps) {
|
|
8618
|
-
this.deps = deps;
|
|
8619
|
-
}
|
|
8620
|
-
async connect(opts = {}) {
|
|
8621
|
-
await this.deps.remoteCommands.connect(opts);
|
|
8622
|
-
}
|
|
8623
|
-
async enable(opts = {}) {
|
|
8624
|
-
await this.deps.initAuto("remote enable");
|
|
8625
|
-
const result = this.deps.remoteCommands.enableConfig(opts);
|
|
8626
|
-
console.log("\u2713 Remote access enabled");
|
|
8627
|
-
if (result.config.remote.deviceName.trim()) {
|
|
8628
|
-
console.log(`Device: ${result.config.remote.deviceName.trim()}`);
|
|
8629
|
-
}
|
|
8630
|
-
if (result.config.remote.platformApiBase.trim()) {
|
|
8631
|
-
console.log(`Platform: ${result.config.remote.platformApiBase.trim()}`);
|
|
8632
|
-
}
|
|
8633
|
-
if (this.hasRunningManagedService()) {
|
|
8634
|
-
await this.deps.restartBackgroundService("remote configuration updated");
|
|
8635
|
-
console.log("\u2713 Applied remote settings to running background service");
|
|
8636
|
-
return;
|
|
8637
|
-
}
|
|
8638
|
-
console.log(`Tip: Run "${APP_NAME4} start" to bring the managed remote connector online.`);
|
|
8639
|
-
}
|
|
8640
|
-
async disable() {
|
|
8641
|
-
const result = this.deps.remoteCommands.disableConfig();
|
|
8642
|
-
console.log(result.changed ? "\u2713 Remote access disabled" : "Remote access was already disabled");
|
|
8643
|
-
if (this.hasRunningManagedService()) {
|
|
8644
|
-
await this.deps.restartBackgroundService("remote access disabled");
|
|
8645
|
-
console.log("\u2713 Running background service restarted without remote access");
|
|
8646
|
-
}
|
|
8647
|
-
}
|
|
8648
|
-
async status(opts = {}) {
|
|
8649
|
-
await this.deps.remoteCommands.status(opts);
|
|
8650
|
-
}
|
|
8651
|
-
async doctor(opts = {}) {
|
|
8652
|
-
await this.deps.remoteCommands.doctor(opts);
|
|
8653
|
-
}
|
|
8654
|
-
hasRunningManagedService() {
|
|
8655
|
-
const state = readServiceState();
|
|
8656
|
-
return Boolean(state && isProcessRunning(state.pid));
|
|
8657
|
-
}
|
|
8658
|
-
};
|
|
8659
|
-
|
|
8660
8032
|
// src/cli/workspace.ts
|
|
8661
8033
|
import { cpSync as cpSync3, existsSync as existsSync12, mkdirSync as mkdirSync7, readFileSync as readFileSync10, readdirSync as readdirSync3, rmSync as rmSync5, writeFileSync as writeFileSync6 } from "fs";
|
|
8662
8034
|
import { createRequire as createRequire2 } from "module";
|
|
8663
8035
|
import { dirname as dirname4, join as join8, resolve as resolve11 } from "path";
|
|
8664
8036
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
8665
|
-
import { APP_NAME as
|
|
8037
|
+
import { APP_NAME as APP_NAME3, getDataDir as getDataDir8 } from "@nextclaw/core";
|
|
8666
8038
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
8667
8039
|
var WorkspaceManager = class {
|
|
8668
8040
|
constructor(logo) {
|
|
@@ -8700,7 +8072,7 @@ var WorkspaceManager = class {
|
|
|
8700
8072
|
continue;
|
|
8701
8073
|
}
|
|
8702
8074
|
const raw = readFileSync10(templatePath, "utf-8");
|
|
8703
|
-
const content = raw.replace(/\$\{APP_NAME\}/g,
|
|
8075
|
+
const content = raw.replace(/\$\{APP_NAME\}/g, APP_NAME3);
|
|
8704
8076
|
mkdirSync7(dirname4(filePath), { recursive: true });
|
|
8705
8077
|
writeFileSync6(filePath, content);
|
|
8706
8078
|
created.push(entry.target);
|
|
@@ -8803,7 +8175,7 @@ var WorkspaceManager = class {
|
|
|
8803
8175
|
source = srcBridge;
|
|
8804
8176
|
}
|
|
8805
8177
|
if (!source) {
|
|
8806
|
-
console.error(`Bridge source not found. Try reinstalling ${
|
|
8178
|
+
console.error(`Bridge source not found. Try reinstalling ${APP_NAME3}.`);
|
|
8807
8179
|
process.exit(1);
|
|
8808
8180
|
}
|
|
8809
8181
|
console.log(`${this.logo} Setting up bridge...`);
|
|
@@ -8861,7 +8233,6 @@ var CliRuntime = class {
|
|
|
8861
8233
|
cronCommands;
|
|
8862
8234
|
platformAuthCommands;
|
|
8863
8235
|
remoteCommands;
|
|
8864
|
-
remote;
|
|
8865
8236
|
diagnosticsCommands;
|
|
8866
8237
|
constructor(options = {}) {
|
|
8867
8238
|
this.logo = options.logo ?? LOGO;
|
|
@@ -8885,11 +8256,6 @@ var CliRuntime = class {
|
|
|
8885
8256
|
this.cronCommands = new CronCommands();
|
|
8886
8257
|
this.platformAuthCommands = new PlatformAuthCommands();
|
|
8887
8258
|
this.remoteCommands = new RemoteCommands();
|
|
8888
|
-
this.remote = new RemoteRuntimeActions({
|
|
8889
|
-
initAuto: (source) => this.init({ source, auto: true }),
|
|
8890
|
-
remoteCommands: this.remoteCommands,
|
|
8891
|
-
restartBackgroundService: (reason) => this.restartBackgroundService(reason)
|
|
8892
|
-
});
|
|
8893
8259
|
this.diagnosticsCommands = new DiagnosticsCommands({ logo: this.logo });
|
|
8894
8260
|
this.restartCoordinator = new RestartCoordinator({
|
|
8895
8261
|
readServiceState,
|
|
@@ -8920,7 +8286,7 @@ var CliRuntime = class {
|
|
|
8920
8286
|
const uiHost = FORCED_PUBLIC_UI_HOST;
|
|
8921
8287
|
const uiPort = typeof state.uiPort === "number" && Number.isFinite(state.uiPort) ? state.uiPort : 18791;
|
|
8922
8288
|
console.log(
|
|
8923
|
-
`Applying changes (${reason}): restarting ${
|
|
8289
|
+
`Applying changes (${reason}): restarting ${APP_NAME4} background service...`
|
|
8924
8290
|
);
|
|
8925
8291
|
await this.serviceCommands.stopService();
|
|
8926
8292
|
await this.serviceCommands.startService({
|
|
@@ -9075,7 +8441,7 @@ var CliRuntime = class {
|
|
|
9075
8441
|
}
|
|
9076
8442
|
async onboard() {
|
|
9077
8443
|
console.warn(
|
|
9078
|
-
`Warning: ${
|
|
8444
|
+
`Warning: ${APP_NAME4} onboard is deprecated. Use "${APP_NAME4} init" instead.`
|
|
9079
8445
|
);
|
|
9080
8446
|
await this.init({ source: "onboard" });
|
|
9081
8447
|
}
|
|
@@ -9083,14 +8449,14 @@ var CliRuntime = class {
|
|
|
9083
8449
|
const source = options.source ?? "init";
|
|
9084
8450
|
const prefix = options.auto ? "Auto init" : "Init";
|
|
9085
8451
|
const force = Boolean(options.force);
|
|
9086
|
-
const configPath =
|
|
8452
|
+
const configPath = getConfigPath6();
|
|
9087
8453
|
let createdConfig = false;
|
|
9088
8454
|
if (!existsSync13(configPath)) {
|
|
9089
8455
|
const config3 = ConfigSchema2.parse({});
|
|
9090
|
-
|
|
8456
|
+
saveConfig9(config3);
|
|
9091
8457
|
createdConfig = true;
|
|
9092
8458
|
}
|
|
9093
|
-
const config2 =
|
|
8459
|
+
const config2 = loadConfig13();
|
|
9094
8460
|
const workspaceSetting = config2.agents.defaults.workspace;
|
|
9095
8461
|
const workspacePath = !workspaceSetting || workspaceSetting === DEFAULT_WORKSPACE_PATH ? join9(getDataDir9(), DEFAULT_WORKSPACE_DIR) : expandHome2(workspaceSetting);
|
|
9096
8462
|
const workspaceExisted = existsSync13(workspacePath);
|
|
@@ -9113,13 +8479,13 @@ var CliRuntime = class {
|
|
|
9113
8479
|
}
|
|
9114
8480
|
if (!options.auto) {
|
|
9115
8481
|
console.log(`
|
|
9116
|
-
${this.logo} ${
|
|
8482
|
+
${this.logo} ${APP_NAME4} is ready! (${source})`);
|
|
9117
8483
|
console.log("\nNext steps:");
|
|
9118
8484
|
console.log(` 1. Add your API key to ${configPath}`);
|
|
9119
|
-
console.log(` 2. Chat: ${
|
|
8485
|
+
console.log(` 2. Chat: ${APP_NAME4} agent -m "Hello!"`);
|
|
9120
8486
|
} else {
|
|
9121
8487
|
console.log(
|
|
9122
|
-
`Tip: Run "${
|
|
8488
|
+
`Tip: Run "${APP_NAME4} init${force ? " --force" : ""}" to re-run initialization if needed.`
|
|
9123
8489
|
);
|
|
9124
8490
|
}
|
|
9125
8491
|
}
|
|
@@ -9127,6 +8493,9 @@ ${this.logo} ${APP_NAME6} is ready! (${source})`);
|
|
|
9127
8493
|
await this.init({ source: "login", auto: true });
|
|
9128
8494
|
await this.platformAuthCommands.login(opts);
|
|
9129
8495
|
}
|
|
8496
|
+
async remoteConnect(opts = {}) {
|
|
8497
|
+
await this.remoteCommands.connect(opts);
|
|
8498
|
+
}
|
|
9130
8499
|
async gateway(opts) {
|
|
9131
8500
|
const uiOverrides = {
|
|
9132
8501
|
host: FORCED_PUBLIC_UI_HOST
|
|
@@ -9177,7 +8546,7 @@ ${this.logo} ${APP_NAME6} is ready! (${source})`);
|
|
|
9177
8546
|
await this.writeRestartSentinelFromExecContext("cli.restart");
|
|
9178
8547
|
const state = readServiceState();
|
|
9179
8548
|
if (state && isProcessRunning(state.pid)) {
|
|
9180
|
-
console.log(`Restarting ${
|
|
8549
|
+
console.log(`Restarting ${APP_NAME4}...`);
|
|
9181
8550
|
await this.serviceCommands.stopService();
|
|
9182
8551
|
} else if (state) {
|
|
9183
8552
|
clearServiceState();
|
|
@@ -9216,8 +8585,8 @@ ${this.logo} ${APP_NAME6} is ready! (${source})`);
|
|
|
9216
8585
|
await this.serviceCommands.stopService();
|
|
9217
8586
|
}
|
|
9218
8587
|
async agent(opts) {
|
|
9219
|
-
const configPath =
|
|
9220
|
-
const config2 = resolveConfigSecrets3(
|
|
8588
|
+
const configPath = getConfigPath6();
|
|
8589
|
+
const config2 = resolveConfigSecrets3(loadConfig13(), { configPath });
|
|
9221
8590
|
const workspace = getWorkspacePath10(config2.agents.defaults.workspace);
|
|
9222
8591
|
const pluginRegistry = loadPluginRegistry(config2, workspace);
|
|
9223
8592
|
const extensionRegistry = toExtensionRegistry(pluginRegistry);
|
|
@@ -9225,7 +8594,7 @@ ${this.logo} ${APP_NAME6} is ready! (${source})`);
|
|
|
9225
8594
|
const pluginChannelBindings = getPluginChannelBindings4(pluginRegistry);
|
|
9226
8595
|
setPluginRuntimeBridge2({
|
|
9227
8596
|
loadConfig: () => toPluginConfigView(
|
|
9228
|
-
resolveConfigSecrets3(
|
|
8597
|
+
resolveConfigSecrets3(loadConfig13(), { configPath }),
|
|
9229
8598
|
pluginChannelBindings
|
|
9230
8599
|
),
|
|
9231
8600
|
writeConfigFile: async (nextConfigView) => {
|
|
@@ -9234,13 +8603,13 @@ ${this.logo} ${APP_NAME6} is ready! (${source})`);
|
|
|
9234
8603
|
"plugin runtime writeConfigFile expects an object config"
|
|
9235
8604
|
);
|
|
9236
8605
|
}
|
|
9237
|
-
const current =
|
|
8606
|
+
const current = loadConfig13();
|
|
9238
8607
|
const next = mergePluginConfigView(
|
|
9239
8608
|
current,
|
|
9240
8609
|
nextConfigView,
|
|
9241
8610
|
pluginChannelBindings
|
|
9242
8611
|
);
|
|
9243
|
-
|
|
8612
|
+
saveConfig9(next);
|
|
9244
8613
|
}
|
|
9245
8614
|
});
|
|
9246
8615
|
try {
|
|
@@ -9266,7 +8635,7 @@ ${this.logo} ${APP_NAME6} is ready! (${source})`);
|
|
|
9266
8635
|
resolveMessageToolHints: ({ channel, accountId }) => resolvePluginChannelMessageToolHints2({
|
|
9267
8636
|
registry: pluginRegistry,
|
|
9268
8637
|
channel,
|
|
9269
|
-
cfg: resolveConfigSecrets3(
|
|
8638
|
+
cfg: resolveConfigSecrets3(loadConfig13(), { configPath }),
|
|
9270
8639
|
accountId
|
|
9271
8640
|
})
|
|
9272
8641
|
});
|
|
@@ -9367,7 +8736,7 @@ ${this.logo} ${APP_NAME6} is ready! (${source})`);
|
|
|
9367
8736
|
}
|
|
9368
8737
|
const state = readServiceState();
|
|
9369
8738
|
if (state && isProcessRunning(state.pid)) {
|
|
9370
|
-
console.log(`Tip: restart ${
|
|
8739
|
+
console.log(`Tip: restart ${APP_NAME4} to apply the update.`);
|
|
9371
8740
|
}
|
|
9372
8741
|
}
|
|
9373
8742
|
pluginsList(opts = {}) {
|
|
@@ -9461,7 +8830,7 @@ ${this.logo} ${APP_NAME6} is ready! (${source})`);
|
|
|
9461
8830
|
await this.diagnosticsCommands.doctor(opts);
|
|
9462
8831
|
}
|
|
9463
8832
|
async skillsInstall(options) {
|
|
9464
|
-
const config2 =
|
|
8833
|
+
const config2 = loadConfig13();
|
|
9465
8834
|
const workdir = resolveSkillsInstallWorkdir({
|
|
9466
8835
|
explicitWorkdir: options.workdir,
|
|
9467
8836
|
configuredWorkspace: config2.agents.defaults.workspace
|
|
@@ -9522,32 +8891,23 @@ ${this.logo} ${APP_NAME6} is ready! (${source})`);
|
|
|
9522
8891
|
}
|
|
9523
8892
|
};
|
|
9524
8893
|
|
|
9525
|
-
// src/cli/remote/register-remote-commands.ts
|
|
9526
|
-
function registerRemoteCommands(program2, runtime2) {
|
|
9527
|
-
const remote = program2.command("remote").description("Manage remote access");
|
|
9528
|
-
remote.command("enable").description("Enable service-managed remote access").option("--api-base <url>", "Platform API base (supports /v1 suffix)").option("--name <name>", "Device display name").action(async (opts) => runtime2.enable(opts));
|
|
9529
|
-
remote.command("disable").description("Disable service-managed remote access").action(async () => runtime2.disable());
|
|
9530
|
-
remote.command("status").description("Show remote access status").option("--json", "Print JSON").action(async (opts) => runtime2.status(opts));
|
|
9531
|
-
remote.command("doctor").description("Run remote access diagnostics").option("--json", "Print JSON").action(async (opts) => runtime2.doctor(opts));
|
|
9532
|
-
remote.command("connect").description("Foreground debug mode: register this machine and keep the connector online").option("--api-base <url>", "Platform API base (supports /v1 suffix)").option("--local-origin <url>", "Local NextClaw UI origin (default: active service or http://127.0.0.1:18791)").option("--name <name>", "Device display name").option("--once", "Connect once without auto-reconnect", false).action(async (opts) => runtime2.connect(opts));
|
|
9533
|
-
}
|
|
9534
|
-
|
|
9535
8894
|
// src/cli/index.ts
|
|
9536
8895
|
var program = new Command();
|
|
9537
8896
|
var runtime = new CliRuntime({ logo: LOGO });
|
|
9538
|
-
program.name(
|
|
9539
|
-
program.command("onboard").description(`Initialize ${
|
|
9540
|
-
program.command("init").description(`Initialize ${
|
|
8897
|
+
program.name(APP_NAME5).description(`${LOGO} ${APP_NAME5} - ${APP_TAGLINE}`).version(getPackageVersion(), "-v, --version", "show version");
|
|
8898
|
+
program.command("onboard").description(`Initialize ${APP_NAME5} configuration and workspace`).action(async () => runtime.onboard());
|
|
8899
|
+
program.command("init").description(`Initialize ${APP_NAME5} configuration and workspace`).option("-f, --force", "Overwrite existing template files").action(async (opts) => runtime.init({ force: Boolean(opts.force) }));
|
|
9541
8900
|
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));
|
|
9542
|
-
|
|
9543
|
-
|
|
9544
|
-
program.command("
|
|
9545
|
-
program.command("
|
|
9546
|
-
program.command("
|
|
9547
|
-
program.command("
|
|
9548
|
-
program.command("
|
|
8901
|
+
var remote = program.command("remote").description("Manage remote access");
|
|
8902
|
+
remote.command("connect").description("Register this machine as a remote device and keep the connector online").option("--api-base <url>", "Platform API base (supports /v1 suffix)").option("--local-origin <url>", "Local NextClaw UI origin (default: active service or http://127.0.0.1:18791)").option("--name <name>", "Device display name").option("--once", "Connect once without auto-reconnect", false).action(async (opts) => runtime.remoteConnect(opts));
|
|
8903
|
+
program.command("gateway").description(`Start the ${APP_NAME5} 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));
|
|
8904
|
+
program.command("ui").description(`Start the ${APP_NAME5} UI with gateway`).option("--port <port>", "UI port").option("--no-open", "Disable opening browser").action(async (opts) => runtime.ui(opts));
|
|
8905
|
+
program.command("start").description(`Start the ${APP_NAME5} 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));
|
|
8906
|
+
program.command("restart").description(`Restart the ${APP_NAME5} 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));
|
|
8907
|
+
program.command("serve").description(`Run the ${APP_NAME5} gateway + UI in the foreground`).option("--ui-port <port>", "UI port").option("--open", "Open browser after start", false).action(async (opts) => runtime.serve(opts));
|
|
8908
|
+
program.command("stop").description(`Stop the ${APP_NAME5} background service`).action(async () => runtime.stop());
|
|
9549
8909
|
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));
|
|
9550
|
-
program.command("update").description(`Update ${
|
|
8910
|
+
program.command("update").description(`Update ${APP_NAME5}`).option("--timeout <ms>", "Update command timeout in milliseconds").action(async (opts) => runtime.update(opts));
|
|
9551
8911
|
var skills = program.command("skills").description("Manage skills");
|
|
9552
8912
|
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 }));
|
|
9553
8913
|
var withRepeatableTag = (value, previous = []) => [...previous, value];
|
|
@@ -9592,6 +8952,6 @@ cron.command("add").requiredOption("-n, --name <name>", "Job name").requiredOpti
|
|
|
9592
8952
|
cron.command("remove <jobId>").action((jobId) => runtime.cronRemove(jobId));
|
|
9593
8953
|
cron.command("enable <jobId>").option("--disable", "Disable instead of enable").action((jobId, opts) => runtime.cronEnable(jobId, opts));
|
|
9594
8954
|
cron.command("run <jobId>").option("-f, --force", "Run even if disabled").action(async (jobId, opts) => runtime.cronRun(jobId, opts));
|
|
9595
|
-
program.command("status").description(`Show ${
|
|
9596
|
-
program.command("doctor").description(`Run ${
|
|
8955
|
+
program.command("status").description(`Show ${APP_NAME5} 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));
|
|
8956
|
+
program.command("doctor").description(`Run ${APP_NAME5} 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));
|
|
9597
8957
|
program.parseAsync(process.argv);
|