nextclaw 0.13.5 → 0.13.6

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