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 CHANGED
@@ -2,13 +2,13 @@
2
2
 
3
3
  // src/cli/index.ts
4
4
  import { Command } from "commander";
5
- import { APP_NAME as APP_NAME7, APP_TAGLINE } from "@nextclaw/core";
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 loadConfig15,
10
- saveConfig as saveConfig10,
11
- getConfigPath as getConfigPath7,
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 APP_NAME6,
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 getConfigPath4, loadConfig as loadConfig10, saveConfig as saveConfig7 } from "@nextclaw/core";
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
- var RemoteRelayBridge = class {
3052
- constructor(localOrigin) {
3053
- this.localOrigin = localOrigin;
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
- function maskToken(value) {
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 = normalizeOptionalString3(config2.remote.platformApiBase) ?? (typeof nextclawProvider?.apiBase === "string" ? nextclawProvider.apiBase.trim() : "");
3280
- const rawApiBase = normalizeOptionalString3(opts.apiBase) ?? configuredApiBase;
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, run nextclaw login, or set remote.platformApiBase.");
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
- const explicitOrigin = normalizeOptionalString3(opts.localOrigin);
3292
- if (explicitOrigin) {
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
- resolveDisplayName(config2, opts) {
3303
- return normalizeOptionalString3(opts.name) ?? normalizeOptionalString3(config2.remote.deviceName) ?? hostname();
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
- // src/cli/remote/remote-connector.ts
3308
- var RemoteConnector = class {
3309
- constructor(logger = console) {
3310
- this.logger = logger;
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
- platformClient = new RemotePlatformClient();
3313
- async connectOnce(params) {
3314
- return await new Promise((resolve13, reject) => {
3315
- const socket = new WebSocket(params.wsUrl);
3316
- let settled = false;
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
- handleSocketMessage(params) {
3387
- void (async () => {
3388
- const frame = this.parseRelayFrame(params.data);
3389
- if (!frame) {
3390
- return;
3391
- }
3392
- try {
3393
- await params.relayBridge.forward(frame, params.socket);
3394
- } catch (error) {
3395
- params.socket.send(JSON.stringify({
3396
- type: "response.error",
3397
- requestId: frame.requestId,
3398
- message: error instanceof Error ? error.message : String(error)
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
- async ensureDevice(params) {
3412
- if (params.device) {
3413
- return params.device;
3165
+ if (bridgeCookie) {
3166
+ headers.set("cookie", bridgeCookie);
3414
3167
  }
3415
- const device = await this.platformClient.registerDevice({
3416
- platformBase: params.context.platformBase,
3417
- token: params.context.token,
3418
- deviceInstallId: params.context.deviceInstallId,
3419
- displayName: params.context.displayName,
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
- this.logger.info(`\u2713 Remote device registered: ${device.displayName} (${device.id})`);
3423
- this.logger.info(`\u2713 Local origin: ${params.context.localOrigin}`);
3424
- this.logger.info(`\u2713 Platform: ${params.context.platformBase}`);
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
- // src/cli/remote/remote-status-store.ts
3511
- import { hostname as hostname2 } from "os";
3512
- import { loadConfig as loadConfig9 } from "@nextclaw/core";
3513
- function normalizeOptionalString4(value) {
3514
- if (typeof value !== "string") {
3515
- return void 0;
3516
- }
3517
- const trimmed = value.trim();
3518
- return trimmed.length > 0 ? trimmed : void 0;
3519
- }
3520
- function buildConfiguredRemoteState(config2 = loadConfig9()) {
3521
- const remote = config2.remote;
3522
- return {
3523
- enabled: Boolean(remote.enabled),
3524
- mode: "service",
3525
- state: remote.enabled ? "disconnected" : "disabled",
3526
- ...normalizeOptionalString4(remote.deviceName) ? { deviceName: normalizeOptionalString4(remote.deviceName) } : {},
3527
- ...normalizeOptionalString4(remote.platformApiBase) ? { platformBase: normalizeOptionalString4(remote.platformApiBase) } : {},
3528
- updatedAt: (/* @__PURE__ */ new Date()).toISOString()
3529
- };
3530
- }
3531
- function resolveRemoteStatusSnapshot(config2 = loadConfig9()) {
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
- saveConfig7(next);
3628
- return {
3629
- changed: config2.remote.enabled !== next.remote.enabled,
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 runtime2 = snapshot.runtime;
3648
- console.log("NextClaw Remote Status");
3649
- console.log(`Enabled: ${snapshot.configuredEnabled ? "yes" : "no"}`);
3650
- console.log(`Mode: ${runtime2?.mode ?? "service"}`);
3651
- console.log(`State: ${runtime2?.state ?? "disabled"}`);
3652
- console.log(`Device: ${runtime2?.deviceName ?? normalizeOptionalString5(config2.remote.deviceName) ?? hostname3()}`);
3653
- console.log(
3654
- `Platform: ${runtime2?.platformBase ?? normalizeOptionalString5(config2.remote.platformApiBase) ?? normalizeOptionalString5(config2.providers.nextclaw?.apiBase) ?? "not set"}`
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 doctor(opts = {}) {
3668
- const config2 = loadConfig10(getConfigPath4());
3669
- const snapshot = resolveRemoteStatusSnapshot(config2);
3670
- const localOrigin = resolveConfiguredLocalOrigin(config2);
3671
- const localUi = await probeLocalUi(localOrigin);
3672
- const token = normalizeOptionalString5(config2.providers.nextclaw?.apiKey);
3673
- const platformApiBase = normalizeOptionalString5(config2.remote.platformApiBase) ?? normalizeOptionalString5(config2.providers.nextclaw?.apiBase);
3674
- const checks = [
3675
- {
3676
- name: "remote-enabled",
3677
- ok: snapshot.configuredEnabled,
3678
- detail: snapshot.configuredEnabled ? "enabled in config" : "disabled in config"
3679
- },
3680
- {
3681
- name: "platform-token",
3682
- ok: Boolean(token),
3683
- detail: token ? "token configured" : 'run "nextclaw login" first'
3684
- },
3685
- {
3686
- name: "platform-api-base",
3687
- ok: Boolean(platformApiBase),
3688
- detail: platformApiBase ?? "set remote.platformApiBase or login with --api-base"
3689
- },
3690
- {
3691
- name: "local-ui",
3692
- ok: localUi.ok,
3693
- detail: `${localOrigin} (${localUi.detail})`
3694
- },
3695
- {
3696
- name: "service-runtime",
3697
- ok: snapshot.runtime?.state === "connected",
3698
- detail: snapshot.runtime ? snapshot.runtime.state : "no managed remote runtime detected"
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
- if (opts.json) {
3702
- console.log(JSON.stringify({ generatedAt: (/* @__PURE__ */ new Date()).toISOString(), checks, snapshot }, null, 2));
3703
- return;
3704
- }
3705
- console.log("NextClaw Remote Doctor");
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 as APP_NAME2,
3718
- getConfigPath as getConfigPath5,
3299
+ APP_NAME,
3300
+ getConfigPath as getConfigPath4,
3719
3301
  getDataDir as getDataDir5,
3720
3302
  getWorkspacePath as getWorkspacePath4,
3721
3303
  hasSecretRef,
3722
- loadConfig as loadConfig11
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
- printStatusReport({ logo: this.deps.logo, report, verbose: Boolean(opts.verbose) });
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
- printDoctorReport({
3897
- logo: this.deps.logo,
3898
- generatedAt: report.generatedAt,
3899
- checks,
3900
- recommendations: report.recommendations,
3901
- verbose: Boolean(opts.verbose),
3902
- logTail: report.logTail
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 = getConfigPath5();
3908
- const config2 = loadConfig11();
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 = this.listProviderStatuses(config2);
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
- this.collectRuntimeIssues({
3934
- configPath,
3935
- workspacePath,
3936
- staleState,
3937
- running,
3938
- managedHealth,
3939
- serviceState,
3940
- orphanSuspected,
3941
- providers,
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 = (getConfigPath8) => {
4125
- const path2 = getConfigPath8();
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 loadConfig13 } from "@nextclaw/core";
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 loadConfig12, saveConfig as saveConfig8 } from "@nextclaw/core";
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: () => loadConfig12(),
4705
- saveConfig: (config2) => saveConfig8(config2)
4243
+ getConfig: () => loadConfig10(),
4244
+ saveConfig: (config2) => saveConfig7(config2)
4706
4245
  });
4707
4246
  }
4708
4247
  createDoctorFacade() {
4709
4248
  return new McpDoctorFacade2({
4710
- getConfig: () => loadConfig12()
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(loadConfig13().agents.defaults.workspace);
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(loadConfig13().agents.defaults.workspace);
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 normalizeOptionalString6(value) {
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 = normalizeOptionalString6(session.metadata.preferred_model) ?? profile.model;
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 = normalizeOptionalString6(requestMetadata.channel) ?? normalizeOptionalString6(session.metadata.last_channel) ?? "ui";
6063
- const chatId = normalizeOptionalString6(requestMetadata.chatId) ?? normalizeOptionalString6(requestMetadata.chat_id) ?? normalizeOptionalString6(session.metadata.last_to) ?? "web-ui";
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: APP_NAME3,
6911
+ APP_NAME: APP_NAME2,
7515
6912
  ChannelManager: ChannelManager2,
7516
6913
  CronService: CronService2,
7517
6914
  getApiBase,
7518
- getConfigPath: getConfigPath6,
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: loadConfig14,
6922
+ loadConfig: loadConfig12,
7526
6923
  MessageBus,
7527
6924
  ProviderManager,
7528
6925
  resolveConfigSecrets: resolveConfigSecrets2,
7529
- saveConfig: saveConfig9,
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 = getConfigPath6();
7550
- const config2 = resolveConfigSecrets2(loadConfig14(), { configPath: runtimeConfigPath });
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(loadConfig14(), { configPath: runtimeConfigPath }),
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(loadConfig14(), { configPath: runtimeConfigPath }));
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: getConfigPath6,
7597
- saveConfig: saveConfig9,
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(loadConfig14(), { configPath: runtimeConfigPath }),
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(loadConfig14(), { configPath: runtimeConfigPath }), pluginChannelBindings),
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 = loadConfig14();
7074
+ const current = loadConfig12();
7662
7075
  const next = mergePluginConfigView(current, nextConfigView, pluginChannelBindings);
7663
- saveConfig9(next);
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(loadConfig14(), { configPath: runtimeConfigPath }),
7150
+ () => resolveConfigSecrets2(loadConfig12(), { configPath: runtimeConfigPath }),
7738
7151
  () => extensionRegistry,
7739
7152
  ({ channel, accountId }) => resolvePluginChannelMessageToolHints({
7740
7153
  registry: pluginRegistry,
7741
7154
  channel,
7742
- cfg: resolveConfigSecrets2(loadConfig14(), { configPath: runtimeConfigPath }),
7155
+ cfg: resolveConfigSecrets2(loadConfig12(), { configPath: runtimeConfigPath }),
7743
7156
  accountId
7744
7157
  })
7745
7158
  );
7746
- const remoteModule = createManagedRemoteModule({ config: config2, localOrigin });
7747
- await startGatewaySupportServices({
7748
- cronJobs: cron2.status().jobs,
7749
- remoteModule,
7750
- watchConfigFile: () => this.watchConfigFile(reloader),
7751
- startCron: () => cron2.start(),
7752
- startHeartbeat: () => heartbeat.start()
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(getConfigPath6());
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 = loadConfig14();
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 = loadConfig14();
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 ${APP_NAME3} is already running (PID ${existing.pid})`);
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 ${APP_NAME3} because UI port ${uiConfig.port} is already occupied.`);
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 ${APP_NAME3} background service (readiness timeout ${Math.ceil(readinessTimeoutMs / 1e3)}s)...`);
7985
- const serveArgs = buildServeArgs({ uiPort: uiConfig.port });
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 = writeReadyManagedServiceState({
8053
- readinessTimeoutMs,
8054
- readiness,
8055
- snapshot: { pid: child.pid, uiUrl, apiUrl, uiHost: uiConfig.host, uiPort: uiConfig.port, logPath }
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: ${APP_NAME3} is running (PID ${state.pid}) but not healthy yet after ${Math.ceil(readinessTimeoutMs / 1e3)}s. Marked as degraded.${hint}`
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 "${APP_NAME3} status --json" and check logs: ${logPath}`);
7480
+ console.warn(`Tip: Run "${APP_NAME2} status --json" and check logs: ${logPath}`);
8063
7481
  } else {
8064
- console.log(`\u2713 ${APP_NAME3} started in background (PID ${state.pid})`);
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 ${APP_NAME3} (PID ${state.pid})...`);
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 ${APP_NAME3} stopped`);
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 ${getConfigPath6()} under providers section`);
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: ${APP_NAME3} status`);
8360
- console.log(` - If you need to stop the service, run: ${APP_NAME3} stop`);
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: getConfigPath6(),
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(loadConfig14().agents.defaults.workspace);
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 APP_NAME5, getDataDir as getDataDir8 } from "@nextclaw/core";
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, APP_NAME5);
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 ${APP_NAME5}.`);
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 ${APP_NAME6} background service...`
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: ${APP_NAME6} onboard is deprecated. Use "${APP_NAME6} init" instead.`
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 = getConfigPath7();
8452
+ const configPath = getConfigPath6();
9087
8453
  let createdConfig = false;
9088
8454
  if (!existsSync13(configPath)) {
9089
8455
  const config3 = ConfigSchema2.parse({});
9090
- saveConfig10(config3);
8456
+ saveConfig9(config3);
9091
8457
  createdConfig = true;
9092
8458
  }
9093
- const config2 = loadConfig15();
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} ${APP_NAME6} is ready! (${source})`);
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: ${APP_NAME6} agent -m "Hello!"`);
8485
+ console.log(` 2. Chat: ${APP_NAME4} agent -m "Hello!"`);
9120
8486
  } else {
9121
8487
  console.log(
9122
- `Tip: Run "${APP_NAME6} init${force ? " --force" : ""}" to re-run initialization if needed.`
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 ${APP_NAME6}...`);
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 = getConfigPath7();
9220
- const config2 = resolveConfigSecrets3(loadConfig15(), { configPath });
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(loadConfig15(), { configPath }),
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 = loadConfig15();
8606
+ const current = loadConfig13();
9238
8607
  const next = mergePluginConfigView(
9239
8608
  current,
9240
8609
  nextConfigView,
9241
8610
  pluginChannelBindings
9242
8611
  );
9243
- saveConfig10(next);
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(loadConfig15(), { configPath }),
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 ${APP_NAME6} to apply the update.`);
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 = loadConfig15();
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(APP_NAME7).description(`${LOGO} ${APP_NAME7} - ${APP_TAGLINE}`).version(getPackageVersion(), "-v, --version", "show version");
9539
- program.command("onboard").description(`Initialize ${APP_NAME7} configuration and workspace`).action(async () => runtime.onboard());
9540
- program.command("init").description(`Initialize ${APP_NAME7} configuration and workspace`).option("-f, --force", "Overwrite existing template files").action(async (opts) => runtime.init({ force: Boolean(opts.force) }));
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
- registerRemoteCommands(program, runtime.remote);
9543
- program.command("gateway").description(`Start the ${APP_NAME7} 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));
9544
- program.command("ui").description(`Start the ${APP_NAME7} UI with gateway`).option("--port <port>", "UI port").option("--no-open", "Disable opening browser").action(async (opts) => runtime.ui(opts));
9545
- program.command("start").description(`Start the ${APP_NAME7} 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));
9546
- program.command("restart").description(`Restart the ${APP_NAME7} 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));
9547
- program.command("serve").description(`Run the ${APP_NAME7} gateway + UI in the foreground`).option("--ui-port <port>", "UI port").option("--open", "Open browser after start", false).action(async (opts) => runtime.serve(opts));
9548
- program.command("stop").description(`Stop the ${APP_NAME7} background service`).action(async () => runtime.stop());
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 ${APP_NAME7}`).option("--timeout <ms>", "Update command timeout in milliseconds").action(async (opts) => runtime.update(opts));
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 ${APP_NAME7} 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));
9596
- program.command("doctor").description(`Run ${APP_NAME7} 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));
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);