nextclaw 0.13.1 → 0.13.3

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.
Files changed (29) hide show
  1. package/dist/cli/index.js +622 -243
  2. package/package.json +8 -8
  3. package/templates/USAGE.md +9 -0
  4. package/ui-dist/assets/{ChannelsList-i00X_bon.js → ChannelsList-DH1Ur9XW.js} +1 -1
  5. package/ui-dist/assets/ChatPage-YTDcN7XS.js +38 -0
  6. package/ui-dist/assets/{DocBrowser-DhSfSjNp.js → DocBrowser-Bi-RpLIw.js} +1 -1
  7. package/ui-dist/assets/{LogoBadge-E8XCymGk.js → LogoBadge-BCR9CU7n.js} +1 -1
  8. package/ui-dist/assets/{MarketplacePage-DSa7G0ro.js → MarketplacePage-BgCdiku7.js} +1 -1
  9. package/ui-dist/assets/{McpMarketplacePage-bDGyqSad.js → McpMarketplacePage-nyCbiQH6.js} +1 -1
  10. package/ui-dist/assets/{ModelConfig-Dvsyt1Tg.js → ModelConfig-Cf4AAYaB.js} +1 -1
  11. package/ui-dist/assets/{ProvidersList-DMbXOYsb.js → ProvidersList-CfkfKQbw.js} +1 -1
  12. package/ui-dist/assets/{RuntimeConfig-ToBbIBNn.js → RuntimeConfig-BI-zClCl.js} +1 -1
  13. package/ui-dist/assets/{SearchConfig-BV_SO-L3.js → SearchConfig-MBmvco1J.js} +1 -1
  14. package/ui-dist/assets/{SecretsConfig-D5J_q_Za.js → SecretsConfig-CC2B6pVQ.js} +1 -1
  15. package/ui-dist/assets/{SessionsConfig-B8gi8Vbi.js → SessionsConfig-CTxJeVQs.js} +1 -1
  16. package/ui-dist/assets/{chat-message-v-cXhn7X.js → chat-message-5OiyZViy.js} +1 -1
  17. package/ui-dist/assets/index-C2OKcVdN.css +1 -0
  18. package/ui-dist/assets/{index-BXJPYlRo.js → index-LgjZxLjc.js} +2 -2
  19. package/ui-dist/assets/{label-352ph_Yg.js → label-CPdcDrir.js} +1 -1
  20. package/ui-dist/assets/{page-layout-BwIR7lB8.js → page-layout-B8V5_vM_.js} +1 -1
  21. package/ui-dist/assets/{popover-BdOI7aUv.js → popover-Bk53A74_.js} +1 -1
  22. package/ui-dist/assets/{security-config-BEhAoZux.js → security-config-BNjgoyo4.js} +1 -1
  23. package/ui-dist/assets/{skeleton-C3DLX_PO.js → skeleton-NPxxR-L0.js} +1 -1
  24. package/ui-dist/assets/{switch-CgUsIol7.js → switch-EowdzMK2.js} +1 -1
  25. package/ui-dist/assets/{tabs-custom-Dc_3h5fO.js → tabs-custom-BjLv-uCT.js} +1 -1
  26. package/ui-dist/assets/{useConfirmDialog-DSrny346.js → useConfirmDialog-TJcJQMfu.js} +1 -1
  27. package/ui-dist/index.html +2 -2
  28. package/ui-dist/assets/ChatPage-BEkVUTgU.js +0 -38
  29. package/ui-dist/assets/index-BoDFsNXm.css +0 -1
package/dist/cli/index.js CHANGED
@@ -6,10 +6,10 @@ import { APP_NAME as APP_NAME5, APP_TAGLINE } from "@nextclaw/core";
6
6
 
7
7
  // src/cli/runtime.ts
8
8
  import {
9
- loadConfig as loadConfig11,
10
- saveConfig as saveConfig8,
11
- getConfigPath as getConfigPath4,
12
- getDataDir as getDataDir8,
9
+ loadConfig as loadConfig13,
10
+ saveConfig as saveConfig9,
11
+ getConfigPath as getConfigPath6,
12
+ getDataDir as getDataDir9,
13
13
  ConfigSchema as ConfigSchema2,
14
14
  getWorkspacePath as getWorkspacePath10,
15
15
  expandHome as expandHome2,
@@ -26,9 +26,9 @@ import {
26
26
  resolvePluginChannelMessageToolHints as resolvePluginChannelMessageToolHints2,
27
27
  setPluginRuntimeBridge as setPluginRuntimeBridge2
28
28
  } from "@nextclaw/openclaw-compat";
29
- import { existsSync as existsSync12, mkdirSync as mkdirSync7, readFileSync as readFileSync10, writeFileSync as writeFileSync6 } from "fs";
30
- import { join as join8, resolve as resolve12 } from "path";
31
- import { createInterface as createInterface2 } from "readline";
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 { createInterface as createInterface3 } from "readline";
32
32
  import { fileURLToPath as fileURLToPath4 } from "url";
33
33
  import { spawn as spawn3 } from "child_process";
34
34
 
@@ -74,9 +74,9 @@ var RestartCoordinator = class {
74
74
  message: "Restart already scheduled; skipping duplicate request."
75
75
  };
76
76
  }
77
- const delay = typeof request.delayMs === "number" && Number.isFinite(request.delayMs) ? Math.max(0, Math.floor(request.delayMs)) : 100;
77
+ const delay2 = typeof request.delayMs === "number" && Number.isFinite(request.delayMs) ? Math.max(0, Math.floor(request.delayMs)) : 100;
78
78
  this.exitScheduled = true;
79
- this.deps.scheduleProcessExit(delay, reason);
79
+ this.deps.scheduleProcessExit(delay2, reason);
80
80
  return {
81
81
  status: "exit-scheduled",
82
82
  message: `Restart scheduled (${reason}).`
@@ -1440,27 +1440,7 @@ async function installPluginMutation(pathOrSpec, opts = {}) {
1440
1440
  };
1441
1441
  }
1442
1442
 
1443
- // src/cli/commands/plugins.ts
1444
- function loadPluginRegistry(config2, workspaceDir) {
1445
- const workspaceExtensionsDir = process.env.NEXTCLAW_DEV_FIRST_PARTY_PLUGIN_DIR;
1446
- const configWithDevPluginPaths = applyDevFirstPartyPluginLoadPaths(
1447
- config2,
1448
- workspaceExtensionsDir
1449
- );
1450
- const excludedRoots = resolveDevFirstPartyPluginInstallRoots(config2, workspaceExtensionsDir);
1451
- return loadOpenClawPlugins({
1452
- config: configWithDevPluginPaths,
1453
- workspaceDir,
1454
- excludeRoots: excludedRoots,
1455
- ...buildReservedPluginLoadOptions(),
1456
- logger: {
1457
- info: (message) => console.log(message),
1458
- warn: (message) => console.warn(message),
1459
- error: (message) => console.error(message),
1460
- debug: (message) => console.debug(message)
1461
- }
1462
- });
1463
- }
1443
+ // src/cli/commands/plugin-extension-registry.ts
1464
1444
  function toExtensionRegistry(pluginRegistry) {
1465
1445
  return {
1466
1446
  tools: pluginRegistry.tools.map((tool) => ({
@@ -1486,6 +1466,7 @@ function toExtensionRegistry(pluginRegistry) {
1486
1466
  kind: runtime2.kind,
1487
1467
  label: runtime2.label,
1488
1468
  createRuntime: runtime2.createRuntime,
1469
+ describeSessionType: runtime2.describeSessionType,
1489
1470
  source: runtime2.source
1490
1471
  })),
1491
1472
  diagnostics: pluginRegistry.diagnostics.map((diag) => ({
@@ -1496,6 +1477,28 @@ function toExtensionRegistry(pluginRegistry) {
1496
1477
  }))
1497
1478
  };
1498
1479
  }
1480
+
1481
+ // src/cli/commands/plugins.ts
1482
+ function loadPluginRegistry(config2, workspaceDir) {
1483
+ const workspaceExtensionsDir = process.env.NEXTCLAW_DEV_FIRST_PARTY_PLUGIN_DIR;
1484
+ const configWithDevPluginPaths = applyDevFirstPartyPluginLoadPaths(
1485
+ config2,
1486
+ workspaceExtensionsDir
1487
+ );
1488
+ const excludedRoots = resolveDevFirstPartyPluginInstallRoots(config2, workspaceExtensionsDir);
1489
+ return loadOpenClawPlugins({
1490
+ config: configWithDevPluginPaths,
1491
+ workspaceDir,
1492
+ excludeRoots: excludedRoots,
1493
+ ...buildReservedPluginLoadOptions(),
1494
+ logger: {
1495
+ info: (message) => console.log(message),
1496
+ warn: (message) => console.warn(message),
1497
+ error: (message) => console.error(message),
1498
+ debug: (message) => console.debug(message)
1499
+ }
1500
+ });
1501
+ }
1499
1502
  function logPluginDiagnostics(registry) {
1500
1503
  for (const diag of registry.diagnostics) {
1501
1504
  const prefix = diag.pluginId ? `${diag.pluginId}: ` : "";
@@ -2858,17 +2861,438 @@ var CronCommands = class {
2858
2861
  }
2859
2862
  };
2860
2863
 
2864
+ // src/cli/commands/platform-auth.ts
2865
+ import { getConfigPath as getConfigPath2, loadConfig as loadConfig7, saveConfig as saveConfig6 } from "@nextclaw/core";
2866
+ import { createInterface as createInterface2 } from "readline";
2867
+
2868
+ // src/cli/commands/platform-api-base.ts
2869
+ var DEFAULT_PLATFORM_API_BASE = "https://ai-gateway-api.nextclaw.io/v1";
2870
+ var INVALID_PLATFORM_HINT = `Use ${DEFAULT_PLATFORM_API_BASE} or the platform root URL without a trailing path.`;
2871
+ function trimTrailingSlash(value) {
2872
+ return value.replace(/\/+$/, "");
2873
+ }
2874
+ function normalizeExplicitApiBase(rawApiBase) {
2875
+ const trimmed = trimTrailingSlash(rawApiBase.trim());
2876
+ if (!trimmed) {
2877
+ return "";
2878
+ }
2879
+ return trimmed.replace(/\/v1?$/i, "");
2880
+ }
2881
+ function resolvePlatformApiBase(params) {
2882
+ const explicitApiBase = typeof params.explicitApiBase === "string" ? params.explicitApiBase.trim() : "";
2883
+ const configuredApiBase = typeof params.configuredApiBase === "string" ? params.configuredApiBase.trim() : "";
2884
+ const fallbackApiBase = params.fallbackApiBase ?? DEFAULT_PLATFORM_API_BASE;
2885
+ const inputApiBase = explicitApiBase || configuredApiBase || (params.requireConfigured ? "" : fallbackApiBase);
2886
+ if (!inputApiBase) {
2887
+ throw new Error("Platform API base is missing. Pass --api-base or run nextclaw login.");
2888
+ }
2889
+ const platformBase = normalizeExplicitApiBase(inputApiBase);
2890
+ if (!platformBase) {
2891
+ throw new Error(`Invalid --api-base "${inputApiBase}". ${INVALID_PLATFORM_HINT}`);
2892
+ }
2893
+ let parsedUrl;
2894
+ try {
2895
+ parsedUrl = new URL(platformBase);
2896
+ } catch {
2897
+ throw new Error(`Invalid --api-base "${inputApiBase}". ${INVALID_PLATFORM_HINT}`);
2898
+ }
2899
+ if (parsedUrl.pathname !== "" && parsedUrl.pathname !== "/") {
2900
+ throw new Error(`Invalid --api-base "${inputApiBase}". ${INVALID_PLATFORM_HINT}`);
2901
+ }
2902
+ const normalizedPlatformBase = trimTrailingSlash(parsedUrl.toString());
2903
+ return {
2904
+ platformBase: normalizedPlatformBase,
2905
+ v1Base: `${normalizedPlatformBase}/v1`,
2906
+ inputApiBase
2907
+ };
2908
+ }
2909
+ function buildPlatformApiBaseErrorMessage(inputApiBase, rawMessage) {
2910
+ if (rawMessage.includes("Remote session cookie missing") || rawMessage.includes("endpoint not found") || rawMessage.includes("NOT_FOUND")) {
2911
+ return `Invalid --api-base "${inputApiBase}". ${INVALID_PLATFORM_HINT}`;
2912
+ }
2913
+ return rawMessage;
2914
+ }
2915
+
2916
+ // src/cli/commands/platform-auth.ts
2917
+ function resolveProviderConfig(opts) {
2918
+ const configPath = getConfigPath2();
2919
+ const config2 = loadConfig7(configPath);
2920
+ const providers = config2.providers;
2921
+ const nextclawProvider = providers.nextclaw ?? {
2922
+ displayName: "",
2923
+ apiKey: "",
2924
+ apiBase: null,
2925
+ extraHeaders: null,
2926
+ wireApi: "auto",
2927
+ models: []
2928
+ };
2929
+ const configuredApiBase = typeof nextclawProvider.apiBase === "string" && nextclawProvider.apiBase.trim().length > 0 ? nextclawProvider.apiBase.trim() : "https://ai-gateway-api.nextclaw.io/v1";
2930
+ const requestedApiBase = typeof opts.apiBase === "string" && opts.apiBase.trim().length > 0 ? opts.apiBase.trim() : configuredApiBase;
2931
+ const { platformBase, v1Base, inputApiBase } = resolvePlatformApiBase({
2932
+ explicitApiBase: requestedApiBase,
2933
+ fallbackApiBase: "https://ai-gateway-api.nextclaw.io/v1"
2934
+ });
2935
+ return {
2936
+ configPath,
2937
+ config: config2,
2938
+ providers,
2939
+ nextclawProvider,
2940
+ platformBase,
2941
+ v1Base,
2942
+ inputApiBase
2943
+ };
2944
+ }
2945
+ async function resolveCredentials(opts) {
2946
+ let email = typeof opts.email === "string" ? opts.email.trim() : "";
2947
+ let password = typeof opts.password === "string" ? opts.password : "";
2948
+ if (email && password) {
2949
+ return { email, password };
2950
+ }
2951
+ const rl = createInterface2({
2952
+ input: process.stdin,
2953
+ output: process.stdout
2954
+ });
2955
+ try {
2956
+ if (!email) {
2957
+ email = (await prompt(rl, "Email: ")).trim();
2958
+ }
2959
+ if (!password) {
2960
+ password = await prompt(rl, "Password: ");
2961
+ }
2962
+ } finally {
2963
+ rl.close();
2964
+ }
2965
+ if (!email || !password) {
2966
+ throw new Error("Email and password are required.");
2967
+ }
2968
+ return { email, password };
2969
+ }
2970
+ function readLoginPayload(raw) {
2971
+ let parsed = null;
2972
+ try {
2973
+ parsed = JSON.parse(raw);
2974
+ } catch {
2975
+ parsed = null;
2976
+ }
2977
+ const token = typeof parsed === "object" && parsed && "data" in parsed && typeof parsed.data?.token === "string" ? parsed.data.token : "";
2978
+ const role = typeof parsed === "object" && parsed && "data" in parsed && typeof parsed.data?.user?.role === "string" ? parsed.data.user.role : "user";
2979
+ if (!token) {
2980
+ throw new Error("Login succeeded but token is missing.");
2981
+ }
2982
+ return { token, role };
2983
+ }
2984
+ var PlatformAuthCommands = class {
2985
+ async login(opts = {}) {
2986
+ const { configPath, config: config2, providers, nextclawProvider, platformBase, v1Base, inputApiBase } = resolveProviderConfig(opts);
2987
+ const { email, password } = await resolveCredentials(opts);
2988
+ const endpoint = opts.register ? `${platformBase}/platform/auth/register` : `${platformBase}/platform/auth/login`;
2989
+ const response = await fetch(endpoint, {
2990
+ method: "POST",
2991
+ headers: {
2992
+ "Content-Type": "application/json"
2993
+ },
2994
+ body: JSON.stringify({ email, password })
2995
+ });
2996
+ const raw = await response.text();
2997
+ if (!response.ok) {
2998
+ let parsed = null;
2999
+ try {
3000
+ parsed = JSON.parse(raw);
3001
+ } catch {
3002
+ parsed = null;
3003
+ }
3004
+ const maybeMessage = typeof parsed === "object" && parsed && "error" in parsed && typeof parsed.error?.message === "string" ? parsed.error.message : raw || `Request failed (${response.status})`;
3005
+ throw new Error(buildPlatformApiBaseErrorMessage(inputApiBase, maybeMessage));
3006
+ }
3007
+ const { token, role } = readLoginPayload(raw);
3008
+ nextclawProvider.apiBase = v1Base;
3009
+ nextclawProvider.apiKey = token;
3010
+ providers.nextclaw = nextclawProvider;
3011
+ saveConfig6(config2, configPath);
3012
+ console.log(`\u2713 Logged in to NextClaw platform (${platformBase})`);
3013
+ console.log(`\u2713 Account: ${email} (${role})`);
3014
+ console.log(`\u2713 Token saved into providers.nextclaw.apiKey`);
3015
+ }
3016
+ };
3017
+
3018
+ // src/cli/commands/remote.ts
3019
+ import { getConfigPath as getConfigPath3, getDataDir as getDataDir4, loadConfig as loadConfig8 } from "@nextclaw/core";
3020
+ import { ensureUiBridgeSecret } from "@nextclaw/server";
3021
+ import { existsSync as existsSync6, mkdirSync as mkdirSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
3022
+ import { dirname as dirname2, join as join4 } from "path";
3023
+ import { hostname, platform as readPlatform } from "os";
3024
+ function encodeBase64(bytes) {
3025
+ return Buffer.from(bytes).toString("base64");
3026
+ }
3027
+ function decodeBase64(base64) {
3028
+ if (!base64) {
3029
+ return new Uint8Array();
3030
+ }
3031
+ return new Uint8Array(Buffer.from(base64, "base64"));
3032
+ }
3033
+ function delay(ms) {
3034
+ return new Promise((resolveDelay) => setTimeout(resolveDelay, ms));
3035
+ }
3036
+ function ensureDir(path2) {
3037
+ mkdirSync4(path2, { recursive: true });
3038
+ }
3039
+ function readJsonFile2(path2) {
3040
+ if (!existsSync6(path2)) {
3041
+ return null;
3042
+ }
3043
+ try {
3044
+ return JSON.parse(readFileSync6(path2, "utf-8"));
3045
+ } catch {
3046
+ return null;
3047
+ }
3048
+ }
3049
+ function writeJsonFile(path2, value) {
3050
+ ensureDir(dirname2(path2));
3051
+ writeFileSync4(path2, `${JSON.stringify(value, null, 2)}
3052
+ `, "utf-8");
3053
+ }
3054
+ var RemoteCommands = class {
3055
+ remoteDir = join4(getDataDir4(), "remote");
3056
+ devicePath = join4(this.remoteDir, "device.json");
3057
+ ensureDeviceInstallId() {
3058
+ const existing = readJsonFile2(this.devicePath);
3059
+ if (existing?.deviceInstallId?.trim()) {
3060
+ return existing.deviceInstallId.trim();
3061
+ }
3062
+ const deviceInstallId = crypto.randomUUID();
3063
+ ensureDir(this.remoteDir);
3064
+ writeJsonFile(this.devicePath, { deviceInstallId });
3065
+ return deviceInstallId;
3066
+ }
3067
+ resolvePlatformAccess(opts) {
3068
+ const config2 = loadConfig8(getConfigPath3());
3069
+ const providers = config2.providers;
3070
+ const nextclawProvider = providers.nextclaw;
3071
+ const token = typeof nextclawProvider?.apiKey === "string" ? nextclawProvider.apiKey.trim() : "";
3072
+ if (!token) {
3073
+ throw new Error('NextClaw platform token is missing. Run "nextclaw login" first.');
3074
+ }
3075
+ const configuredApiBase = typeof nextclawProvider?.apiBase === "string" ? nextclawProvider.apiBase.trim() : "";
3076
+ const rawApiBase = typeof opts.apiBase === "string" && opts.apiBase.trim().length > 0 ? opts.apiBase.trim() : configuredApiBase;
3077
+ if (!rawApiBase) {
3078
+ throw new Error("Platform API base is missing. Pass --api-base or run nextclaw login.");
3079
+ }
3080
+ const { platformBase } = resolvePlatformApiBase({
3081
+ explicitApiBase: rawApiBase,
3082
+ requireConfigured: true
3083
+ });
3084
+ return { platformBase, token, config: config2 };
3085
+ }
3086
+ resolveLocalOrigin(config2, opts) {
3087
+ if (typeof opts.localOrigin === "string" && opts.localOrigin.trim().length > 0) {
3088
+ return opts.localOrigin.trim().replace(/\/$/, "");
3089
+ }
3090
+ const state = readServiceState();
3091
+ if (state && isProcessRunning(state.pid) && Number.isFinite(state.uiPort)) {
3092
+ return `http://127.0.0.1:${state.uiPort}`;
3093
+ }
3094
+ const configuredPort = typeof config2.ui?.port === "number" && Number.isFinite(config2.ui.port) ? config2.ui.port : 18791;
3095
+ return `http://127.0.0.1:${configuredPort}`;
3096
+ }
3097
+ async ensureLocalUiHealthy(localOrigin) {
3098
+ const response = await fetch(`${localOrigin}/api/health`);
3099
+ if (!response.ok) {
3100
+ throw new Error(`Local UI is not healthy at ${localOrigin}. Start NextClaw first.`);
3101
+ }
3102
+ }
3103
+ async registerDevice(params) {
3104
+ const response = await fetch(`${params.platformBase}/platform/remote/devices/register`, {
3105
+ method: "POST",
3106
+ headers: {
3107
+ "content-type": "application/json",
3108
+ authorization: `Bearer ${params.token}`
3109
+ },
3110
+ body: JSON.stringify({
3111
+ deviceInstallId: params.deviceInstallId,
3112
+ displayName: params.displayName,
3113
+ platform: readPlatform(),
3114
+ appVersion: getPackageVersion(),
3115
+ localOrigin: params.localOrigin
3116
+ })
3117
+ });
3118
+ const payload = await response.json();
3119
+ if (!response.ok || !payload.ok || !payload.data?.device) {
3120
+ throw new Error(payload.error?.message ?? `Failed to register remote device (${response.status}).`);
3121
+ }
3122
+ return payload.data.device;
3123
+ }
3124
+ async requestBridgeCookie(localOrigin) {
3125
+ const response = await fetch(`${localOrigin}/api/auth/bridge`, {
3126
+ method: "POST",
3127
+ headers: {
3128
+ "x-nextclaw-ui-bridge-secret": ensureUiBridgeSecret()
3129
+ }
3130
+ });
3131
+ const payload = await response.json();
3132
+ if (!response.ok || !payload.ok) {
3133
+ throw new Error(payload.error?.message ?? `Failed to request local auth bridge (${response.status}).`);
3134
+ }
3135
+ return typeof payload.data?.cookie === "string" && payload.data.cookie.trim().length > 0 ? payload.data.cookie.trim() : null;
3136
+ }
3137
+ async handleRelayRequest(params) {
3138
+ const bridgeCookie = await this.requestBridgeCookie(params.localOrigin);
3139
+ const url = new URL(params.frame.path, params.localOrigin);
3140
+ const headers = new Headers();
3141
+ for (const [key, value] of params.frame.headers) {
3142
+ const lower = key.toLowerCase();
3143
+ if ([
3144
+ "host",
3145
+ "connection",
3146
+ "content-length",
3147
+ "cookie",
3148
+ "x-forwarded-for",
3149
+ "x-forwarded-proto",
3150
+ "cf-connecting-ip"
3151
+ ].includes(lower)) {
3152
+ continue;
3153
+ }
3154
+ headers.set(key, value);
3155
+ }
3156
+ if (bridgeCookie) {
3157
+ headers.set("cookie", bridgeCookie);
3158
+ }
3159
+ const bodyBytes = decodeBase64(params.frame.bodyBase64);
3160
+ const response = await fetch(url, {
3161
+ method: params.frame.method,
3162
+ headers,
3163
+ body: params.frame.method === "GET" || params.frame.method === "HEAD" ? void 0 : bodyBytes
3164
+ });
3165
+ const responseHeaders = Array.from(response.headers.entries()).filter(([key]) => {
3166
+ const lower = key.toLowerCase();
3167
+ return !["content-length", "connection", "transfer-encoding", "set-cookie"].includes(lower);
3168
+ });
3169
+ const contentType = response.headers.get("content-type")?.toLowerCase() ?? "";
3170
+ if (response.body && contentType.startsWith("text/event-stream")) {
3171
+ params.socket.send(JSON.stringify({
3172
+ type: "response.start",
3173
+ requestId: params.frame.requestId,
3174
+ status: response.status,
3175
+ headers: responseHeaders
3176
+ }));
3177
+ const reader = response.body.getReader();
3178
+ try {
3179
+ while (true) {
3180
+ const { value, done } = await reader.read();
3181
+ if (done) {
3182
+ break;
3183
+ }
3184
+ if (value && value.length > 0) {
3185
+ params.socket.send(JSON.stringify({
3186
+ type: "response.chunk",
3187
+ requestId: params.frame.requestId,
3188
+ bodyBase64: encodeBase64(value)
3189
+ }));
3190
+ }
3191
+ }
3192
+ } finally {
3193
+ reader.releaseLock();
3194
+ }
3195
+ params.socket.send(JSON.stringify({
3196
+ type: "response.end",
3197
+ requestId: params.frame.requestId
3198
+ }));
3199
+ return;
3200
+ }
3201
+ const responseBody = response.body ? new Uint8Array(await response.arrayBuffer()) : new Uint8Array();
3202
+ params.socket.send(JSON.stringify({
3203
+ type: "response",
3204
+ requestId: params.frame.requestId,
3205
+ status: response.status,
3206
+ headers: responseHeaders,
3207
+ bodyBase64: encodeBase64(responseBody)
3208
+ }));
3209
+ }
3210
+ async connectOnce(params) {
3211
+ await new Promise((resolve13, reject) => {
3212
+ const socket = new WebSocket(params.wsUrl);
3213
+ const pingTimer = setInterval(() => {
3214
+ if (socket.readyState === WebSocket.OPEN) {
3215
+ socket.send(JSON.stringify({ type: "ping", at: (/* @__PURE__ */ new Date()).toISOString() }));
3216
+ }
3217
+ }, 15e3);
3218
+ socket.addEventListener("open", () => {
3219
+ console.log(`\u2713 Remote connector connected: ${params.wsUrl}`);
3220
+ });
3221
+ socket.addEventListener("message", (event) => {
3222
+ void (async () => {
3223
+ let frame = null;
3224
+ try {
3225
+ frame = JSON.parse(String(event.data ?? ""));
3226
+ } catch {
3227
+ return;
3228
+ }
3229
+ if (!frame || frame.type !== "request") {
3230
+ return;
3231
+ }
3232
+ try {
3233
+ await this.handleRelayRequest({ frame, localOrigin: params.localOrigin, socket });
3234
+ } catch (error) {
3235
+ socket.send(JSON.stringify({
3236
+ type: "response.error",
3237
+ requestId: frame.requestId,
3238
+ message: error instanceof Error ? error.message : String(error)
3239
+ }));
3240
+ }
3241
+ })();
3242
+ });
3243
+ socket.addEventListener("close", () => {
3244
+ clearInterval(pingTimer);
3245
+ resolve13();
3246
+ });
3247
+ socket.addEventListener("error", () => {
3248
+ clearInterval(pingTimer);
3249
+ reject(new Error("Remote connector websocket failed."));
3250
+ });
3251
+ });
3252
+ }
3253
+ async connect(opts = {}) {
3254
+ const { platformBase, token, config: config2 } = this.resolvePlatformAccess(opts);
3255
+ const localOrigin = this.resolveLocalOrigin(config2, opts);
3256
+ await this.ensureLocalUiHealthy(localOrigin);
3257
+ const deviceInstallId = this.ensureDeviceInstallId();
3258
+ const displayName = typeof opts.name === "string" && opts.name.trim().length > 0 ? opts.name.trim() : hostname();
3259
+ const device = await this.registerDevice({
3260
+ platformBase,
3261
+ token,
3262
+ deviceInstallId,
3263
+ displayName,
3264
+ localOrigin
3265
+ });
3266
+ console.log(`\u2713 Remote device registered: ${device.displayName} (${device.id})`);
3267
+ console.log(`\u2713 Local origin: ${localOrigin}`);
3268
+ console.log(`\u2713 Platform: ${platformBase}`);
3269
+ const wsUrl = `${platformBase.replace(/^http/i, "ws")}/platform/remote/connect?deviceId=${encodeURIComponent(device.id)}&token=${encodeURIComponent(token)}`;
3270
+ do {
3271
+ try {
3272
+ await this.connectOnce({ wsUrl, localOrigin });
3273
+ } catch (error) {
3274
+ console.error(`Remote connector error: ${error instanceof Error ? error.message : String(error)}`);
3275
+ }
3276
+ if (opts.once) {
3277
+ break;
3278
+ }
3279
+ console.log("Remote connector disconnected. Reconnecting in 3s...");
3280
+ await delay(3e3);
3281
+ } while (!opts.once);
3282
+ }
3283
+ };
3284
+
2861
3285
  // src/cli/commands/diagnostics.ts
2862
3286
  import { createServer as createNetServer } from "net";
2863
- import { existsSync as existsSync6, readFileSync as readFileSync6 } from "fs";
3287
+ import { existsSync as existsSync7, readFileSync as readFileSync7 } from "fs";
2864
3288
  import { resolve as resolve8 } from "path";
2865
3289
  import {
2866
3290
  APP_NAME,
2867
- getConfigPath as getConfigPath2,
2868
- getDataDir as getDataDir4,
3291
+ getConfigPath as getConfigPath4,
3292
+ getDataDir as getDataDir5,
2869
3293
  getWorkspacePath as getWorkspacePath4,
2870
3294
  hasSecretRef,
2871
- loadConfig as loadConfig7
3295
+ loadConfig as loadConfig9
2872
3296
  } from "@nextclaw/core";
2873
3297
  import { listBuiltinProviders } from "@nextclaw/runtime";
2874
3298
  var DiagnosticsCommands = class {
@@ -3029,10 +3453,10 @@ var DiagnosticsCommands = class {
3029
3453
  process.exitCode = exitCode;
3030
3454
  }
3031
3455
  async collectRuntimeStatus(params) {
3032
- const configPath = getConfigPath2();
3033
- const config2 = loadConfig7();
3456
+ const configPath = getConfigPath4();
3457
+ const config2 = loadConfig9();
3034
3458
  const workspacePath = getWorkspacePath4(config2.agents.defaults.workspace);
3035
- const serviceStatePath = resolve8(getDataDir4(), "run", "service.json");
3459
+ const serviceStatePath = resolve8(getDataDir5(), "run", "service.json");
3036
3460
  const fixActions = [];
3037
3461
  let serviceState = readServiceState();
3038
3462
  if (params.fix && serviceState && !isProcessRunning(serviceState.pid)) {
@@ -3072,11 +3496,11 @@ var DiagnosticsCommands = class {
3072
3496
  });
3073
3497
  const issues = [];
3074
3498
  const recommendations = [];
3075
- if (!existsSync6(configPath)) {
3499
+ if (!existsSync7(configPath)) {
3076
3500
  issues.push("Config file is missing.");
3077
3501
  recommendations.push(`Run ${APP_NAME} init to create config files.`);
3078
3502
  }
3079
- if (!existsSync6(workspacePath)) {
3503
+ if (!existsSync7(workspacePath)) {
3080
3504
  issues.push("Workspace directory does not exist.");
3081
3505
  recommendations.push(`Run ${APP_NAME} init to create workspace templates.`);
3082
3506
  }
@@ -3109,13 +3533,13 @@ var DiagnosticsCommands = class {
3109
3533
  return {
3110
3534
  generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
3111
3535
  configPath,
3112
- configExists: existsSync6(configPath),
3536
+ configExists: existsSync7(configPath),
3113
3537
  workspacePath,
3114
- workspaceExists: existsSync6(workspacePath),
3538
+ workspaceExists: existsSync7(workspacePath),
3115
3539
  model: config2.agents.defaults.model,
3116
3540
  providers,
3117
3541
  serviceStatePath,
3118
- serviceStateExists: existsSync6(serviceStatePath),
3542
+ serviceStateExists: existsSync7(serviceStatePath),
3119
3543
  fixActions,
3120
3544
  process: {
3121
3545
  managedByState,
@@ -3165,11 +3589,11 @@ var DiagnosticsCommands = class {
3165
3589
  }
3166
3590
  }
3167
3591
  readLogTail(path2, maxLines = 25) {
3168
- if (!existsSync6(path2)) {
3592
+ if (!existsSync7(path2)) {
3169
3593
  return [];
3170
3594
  }
3171
3595
  try {
3172
- const lines = readFileSync6(path2, "utf-8").split(/\r?\n/).filter(Boolean);
3596
+ const lines = readFileSync7(path2, "utf-8").split(/\r?\n/).filter(Boolean);
3173
3597
  if (lines.length <= maxLines) {
3174
3598
  return lines;
3175
3599
  }
@@ -3209,8 +3633,8 @@ import {
3209
3633
  stopPluginChannelGateways as stopPluginChannelGateways2
3210
3634
  } from "@nextclaw/openclaw-compat";
3211
3635
  import { startUiServer } from "@nextclaw/server";
3212
- import { appendFileSync, closeSync, cpSync as cpSync2, existsSync as existsSync10, mkdirSync as mkdirSync5, openSync } from "fs";
3213
- import { dirname as dirname2, join as join6, resolve as resolve10 } from "path";
3636
+ import { appendFileSync, closeSync, cpSync as cpSync2, existsSync as existsSync11, mkdirSync as mkdirSync6, openSync } from "fs";
3637
+ import { dirname as dirname3, join as join7, resolve as resolve10 } from "path";
3214
3638
  import { spawn as spawn2 } from "child_process";
3215
3639
  import { request as httpRequest } from "http";
3216
3640
  import { request as httpsRequest } from "https";
@@ -3219,7 +3643,7 @@ import chokidar from "chokidar";
3219
3643
 
3220
3644
  // src/cli/gateway/controller.ts
3221
3645
  import { createHash } from "crypto";
3222
- import { existsSync as existsSync7, readFileSync as readFileSync7 } from "fs";
3646
+ import { existsSync as existsSync8, readFileSync as readFileSync8 } from "fs";
3223
3647
  import {
3224
3648
  buildConfigSchema,
3225
3649
  ConfigSchema,
@@ -3227,12 +3651,12 @@ import {
3227
3651
  redactConfigObject
3228
3652
  } from "@nextclaw/core";
3229
3653
  var hashRaw = (raw) => createHash("sha256").update(raw).digest("hex");
3230
- var readConfigSnapshot = (getConfigPath5) => {
3231
- const path2 = getConfigPath5();
3654
+ var readConfigSnapshot = (getConfigPath7) => {
3655
+ const path2 = getConfigPath7();
3232
3656
  let raw = "";
3233
3657
  let parsed = {};
3234
- if (existsSync7(path2)) {
3235
- raw = readFileSync7(path2, "utf-8");
3658
+ if (existsSync8(path2)) {
3659
+ raw = readFileSync8(path2, "utf-8");
3236
3660
  try {
3237
3661
  parsed = JSON.parse(raw);
3238
3662
  } catch {
@@ -3339,11 +3763,11 @@ var GatewayControllerImpl = class {
3339
3763
  await this.deps.requestRestart(options);
3340
3764
  return;
3341
3765
  }
3342
- const delay = typeof options?.delayMs === "number" && Number.isFinite(options.delayMs) ? Math.max(0, options.delayMs) : 100;
3766
+ const delay2 = typeof options?.delayMs === "number" && Number.isFinite(options.delayMs) ? Math.max(0, options.delayMs) : 100;
3343
3767
  console.log(`Gateway restart requested via tool${options?.reason ? ` (${options.reason})` : ""}.`);
3344
3768
  setTimeout(() => {
3345
3769
  process.exit(0);
3346
- }, delay);
3770
+ }, delay2);
3347
3771
  }
3348
3772
  status() {
3349
3773
  return {
@@ -3689,9 +4113,9 @@ var MissingProvider = class extends LLMProvider {
3689
4113
  };
3690
4114
 
3691
4115
  // src/cli/commands/service-marketplace-installer.ts
3692
- import { getWorkspacePath as getWorkspacePath5, loadConfig as loadConfig9 } from "@nextclaw/core";
3693
- import { existsSync as existsSync8, rmSync as rmSync4 } from "fs";
3694
- import { join as join4 } from "path";
4116
+ import { getWorkspacePath as getWorkspacePath5, loadConfig as loadConfig11 } from "@nextclaw/core";
4117
+ import { existsSync as existsSync9, rmSync as rmSync4 } from "fs";
4118
+ import { join as join5 } from "path";
3695
4119
 
3696
4120
  // src/cli/commands/service-marketplace-helpers.ts
3697
4121
  var containsAbsoluteFsPath = (line) => {
@@ -3739,7 +4163,7 @@ var buildMarketplaceSkillInstallArgs = (params) => {
3739
4163
  };
3740
4164
 
3741
4165
  // src/cli/commands/service-mcp-marketplace-ops.ts
3742
- import { loadConfig as loadConfig8, saveConfig as saveConfig6 } from "@nextclaw/core";
4166
+ import { loadConfig as loadConfig10, saveConfig as saveConfig7 } from "@nextclaw/core";
3743
4167
  import { McpDoctorFacade as McpDoctorFacade2, McpMutationService as McpMutationService2 } from "@nextclaw/mcp";
3744
4168
  var ServiceMcpMarketplaceOps = class {
3745
4169
  constructor(options) {
@@ -3807,13 +4231,13 @@ var ServiceMcpMarketplaceOps = class {
3807
4231
  }
3808
4232
  createMutationService() {
3809
4233
  return new McpMutationService2({
3810
- getConfig: () => loadConfig8(),
3811
- saveConfig: (config2) => saveConfig6(config2)
4234
+ getConfig: () => loadConfig10(),
4235
+ saveConfig: (config2) => saveConfig7(config2)
3812
4236
  });
3813
4237
  }
3814
4238
  createDoctorFacade() {
3815
4239
  return new McpDoctorFacade2({
3816
- getConfig: () => loadConfig8()
4240
+ getConfig: () => loadConfig10()
3817
4241
  });
3818
4242
  }
3819
4243
  };
@@ -3854,7 +4278,7 @@ var ServiceMarketplaceInstaller = class {
3854
4278
  if (params.kind && params.kind !== "marketplace") {
3855
4279
  throw new Error(`Unsupported marketplace skill kind: ${params.kind}`);
3856
4280
  }
3857
- const workspace = getWorkspacePath5(loadConfig9().agents.defaults.workspace);
4281
+ const workspace = getWorkspacePath5(loadConfig11().agents.defaults.workspace);
3858
4282
  const args = buildMarketplaceSkillInstallArgs({
3859
4283
  slug: params.slug,
3860
4284
  workspace,
@@ -3893,9 +4317,9 @@ var ServiceMarketplaceInstaller = class {
3893
4317
  return { message: result.message };
3894
4318
  }
3895
4319
  async uninstallSkill(slug) {
3896
- const workspace = getWorkspacePath5(loadConfig9().agents.defaults.workspace);
3897
- const targetDir = join4(workspace, "skills", slug);
3898
- if (!existsSync8(targetDir)) {
4320
+ const workspace = getWorkspacePath5(loadConfig11().agents.defaults.workspace);
4321
+ const targetDir = join5(workspace, "skills", slug);
4322
+ if (!existsSync9(targetDir)) {
3899
4323
  throw new Error(`Skill not installed in workspace: ${slug}`);
3900
4324
  }
3901
4325
  rmSync4(targetDir, { recursive: true, force: true });
@@ -5675,22 +6099,33 @@ var UiNcpRuntimeRegistry = class {
5675
6099
  sessionMetadata: nextSessionMetadata
5676
6100
  });
5677
6101
  }
5678
- listSessionTypes() {
5679
- const options = [...this.registrations.values()].map((registration) => ({
5680
- value: registration.kind,
5681
- label: registration.label
5682
- })).sort((left, right) => {
5683
- if (left.value === this.defaultKind) {
5684
- return -1;
5685
- }
5686
- if (right.value === this.defaultKind) {
5687
- return 1;
5688
- }
5689
- return left.value.localeCompare(right.value);
5690
- });
6102
+ async listSessionTypes() {
6103
+ const options = await Promise.all(
6104
+ [...this.registrations.values()].map(async (registration) => {
6105
+ const descriptor = await registration.describeSessionType?.();
6106
+ return {
6107
+ value: registration.kind,
6108
+ label: registration.label,
6109
+ ready: descriptor?.ready ?? true,
6110
+ reason: descriptor?.reason ?? null,
6111
+ reasonMessage: descriptor?.reasonMessage ?? null,
6112
+ recommendedModel: descriptor?.recommendedModel ?? null,
6113
+ cta: descriptor?.cta ?? null,
6114
+ ...descriptor?.supportedModels ? { supportedModels: descriptor.supportedModels } : {}
6115
+ };
6116
+ })
6117
+ );
5691
6118
  return {
5692
6119
  defaultType: this.defaultKind,
5693
- options
6120
+ options: options.sort((left, right) => {
6121
+ if (left.value === this.defaultKind) {
6122
+ return -1;
6123
+ }
6124
+ if (right.value === this.defaultKind) {
6125
+ return 1;
6126
+ }
6127
+ return left.value.localeCompare(right.value);
6128
+ })
5694
6129
  };
5695
6130
  }
5696
6131
  };
@@ -5796,7 +6231,8 @@ async function createUiNcpAgent(params) {
5796
6231
  scope.add(runtimeRegistry.register({
5797
6232
  kind: registration.kind,
5798
6233
  label: registration.label,
5799
- createRuntime: registration.createRuntime
6234
+ createRuntime: registration.createRuntime,
6235
+ describeSessionType: registration.describeSessionType
5800
6236
  }));
5801
6237
  }
5802
6238
  };
@@ -5844,14 +6280,14 @@ async function createUiNcpAgent(params) {
5844
6280
  }
5845
6281
 
5846
6282
  // src/cli/commands/ui-chat-run-coordinator.ts
5847
- import { existsSync as existsSync9, mkdirSync as mkdirSync4, readdirSync as readdirSync2, readFileSync as readFileSync8, writeFileSync as writeFileSync4 } from "fs";
5848
- import { join as join5 } from "path";
6283
+ import { existsSync as existsSync10, mkdirSync as mkdirSync5, readdirSync as readdirSync2, readFileSync as readFileSync9, writeFileSync as writeFileSync5 } from "fs";
6284
+ import { join as join6 } from "path";
5849
6285
  import {
5850
- getDataDir as getDataDir5,
6286
+ getDataDir as getDataDir6,
5851
6287
  parseAgentScopedSessionKey as parseAgentScopedSessionKey2,
5852
6288
  safeFilename
5853
6289
  } from "@nextclaw/core";
5854
- var RUNS_DIR = join5(getDataDir5(), "runs");
6290
+ var RUNS_DIR = join6(getDataDir6(), "runs");
5855
6291
  var NON_TERMINAL_STATES = /* @__PURE__ */ new Set(["queued", "running"]);
5856
6292
  var DEFAULT_SESSION_TYPE = "native";
5857
6293
  var SESSION_TYPE_METADATA_KEY = "session_type";
@@ -5910,7 +6346,7 @@ function hasToolSessionEvent(run) {
5910
6346
  var UiChatRunCoordinator = class {
5911
6347
  constructor(options) {
5912
6348
  this.options = options;
5913
- mkdirSync4(RUNS_DIR, { recursive: true });
6349
+ mkdirSync5(RUNS_DIR, { recursive: true });
5914
6350
  this.loadPersistedRuns();
5915
6351
  }
5916
6352
  runs = /* @__PURE__ */ new Map();
@@ -6377,7 +6813,7 @@ var UiChatRunCoordinator = class {
6377
6813
  };
6378
6814
  }
6379
6815
  getRunPath(runId) {
6380
- return join5(RUNS_DIR, `${safeFilename(runId)}.json`);
6816
+ return join6(RUNS_DIR, `${safeFilename(runId)}.json`);
6381
6817
  }
6382
6818
  persistRun(run) {
6383
6819
  const persisted = {
@@ -6395,20 +6831,20 @@ var UiChatRunCoordinator = class {
6395
6831
  ...typeof run.reply === "string" ? { reply: run.reply } : {},
6396
6832
  events: run.events
6397
6833
  };
6398
- writeFileSync4(this.getRunPath(run.runId), `${JSON.stringify(persisted, null, 2)}
6834
+ writeFileSync5(this.getRunPath(run.runId), `${JSON.stringify(persisted, null, 2)}
6399
6835
  `);
6400
6836
  }
6401
6837
  loadPersistedRuns() {
6402
- if (!existsSync9(RUNS_DIR)) {
6838
+ if (!existsSync10(RUNS_DIR)) {
6403
6839
  return;
6404
6840
  }
6405
6841
  for (const entry of readdirSync2(RUNS_DIR, { withFileTypes: true })) {
6406
6842
  if (!entry.isFile() || !entry.name.endsWith(".json")) {
6407
6843
  continue;
6408
6844
  }
6409
- const path2 = join5(RUNS_DIR, entry.name);
6845
+ const path2 = join6(RUNS_DIR, entry.name);
6410
6846
  try {
6411
- const parsed = JSON.parse(readFileSync8(path2, "utf-8"));
6847
+ const parsed = JSON.parse(readFileSync9(path2, "utf-8"));
6412
6848
  const runId = readOptionalString(parsed.runId);
6413
6849
  const sessionKey = readOptionalString(parsed.sessionKey);
6414
6850
  if (!runId || !sessionKey) {
@@ -6467,18 +6903,18 @@ var {
6467
6903
  ChannelManager: ChannelManager2,
6468
6904
  CronService: CronService2,
6469
6905
  getApiBase,
6470
- getConfigPath: getConfigPath3,
6471
- getDataDir: getDataDir6,
6906
+ getConfigPath: getConfigPath5,
6907
+ getDataDir: getDataDir7,
6472
6908
  getProvider,
6473
6909
  getProviderName,
6474
6910
  getWorkspacePath: getWorkspacePath9,
6475
6911
  HeartbeatService,
6476
6912
  LiteLLMProvider,
6477
- loadConfig: loadConfig10,
6913
+ loadConfig: loadConfig12,
6478
6914
  MessageBus,
6479
6915
  ProviderManager,
6480
6916
  resolveConfigSecrets: resolveConfigSecrets2,
6481
- saveConfig: saveConfig7,
6917
+ saveConfig: saveConfig8,
6482
6918
  SessionManager,
6483
6919
  parseAgentScopedSessionKey: parseAgentScopedSessionKey3
6484
6920
  } = NextclawCore;
@@ -6498,8 +6934,8 @@ var ServiceCommands = class {
6498
6934
  async startGateway(options = {}) {
6499
6935
  this.applyLiveConfigReload = null;
6500
6936
  this.liveUiNcpAgent = null;
6501
- const runtimeConfigPath = getConfigPath3();
6502
- const config2 = resolveConfigSecrets2(loadConfig10(), { configPath: runtimeConfigPath });
6937
+ const runtimeConfigPath = getConfigPath5();
6938
+ const config2 = resolveConfigSecrets2(loadConfig12(), { configPath: runtimeConfigPath });
6503
6939
  const workspace = getWorkspacePath9(config2.agents.defaults.workspace);
6504
6940
  let pluginRegistry = loadPluginRegistry(config2, workspace);
6505
6941
  let extensionRegistry = toExtensionRegistry(pluginRegistry);
@@ -6529,7 +6965,7 @@ var ServiceCommands = class {
6529
6965
  }
6530
6966
  }
6531
6967
  };
6532
- const cronStorePath = join6(getDataDir6(), "cron", "jobs.json");
6968
+ const cronStorePath = join7(getDataDir7(), "cron", "jobs.json");
6533
6969
  const cron2 = new CronService2(cronStorePath);
6534
6970
  const uiConfig = resolveUiConfig(config2, options.uiOverrides);
6535
6971
  const uiStaticDir = options.uiStaticDir === void 0 ? resolveUiStaticDir() : options.uiStaticDir;
@@ -6544,7 +6980,7 @@ var ServiceCommands = class {
6544
6980
  sessionManager,
6545
6981
  providerManager,
6546
6982
  makeProvider: (nextConfig) => this.makeProvider(nextConfig, { allowMissing: true }) ?? this.makeMissingProvider(nextConfig),
6547
- loadConfig: () => resolveConfigSecrets2(loadConfig10(), { configPath: runtimeConfigPath }),
6983
+ loadConfig: () => resolveConfigSecrets2(loadConfig12(), { configPath: runtimeConfigPath }),
6548
6984
  getExtensionChannels: () => extensionRegistry.channels,
6549
6985
  onRestartRequired: (paths) => {
6550
6986
  void this.deps.requestRestart({
@@ -6555,14 +6991,14 @@ var ServiceCommands = class {
6555
6991
  }
6556
6992
  });
6557
6993
  this.applyLiveConfigReload = async () => {
6558
- await reloader.applyReloadPlan(resolveConfigSecrets2(loadConfig10(), { configPath: runtimeConfigPath }));
6994
+ await reloader.applyReloadPlan(resolveConfigSecrets2(loadConfig12(), { configPath: runtimeConfigPath }));
6559
6995
  };
6560
6996
  const gatewayController = new GatewayControllerImpl({
6561
6997
  reloader,
6562
6998
  cron: cron2,
6563
6999
  sessionManager,
6564
- getConfigPath: getConfigPath3,
6565
- saveConfig: saveConfig7,
7000
+ getConfigPath: getConfigPath5,
7001
+ saveConfig: saveConfig8,
6566
7002
  requestRestart: async (options2) => {
6567
7003
  await this.deps.requestRestart({
6568
7004
  reason: options2?.reason ?? "gateway tool restart",
@@ -6588,7 +7024,7 @@ var ServiceCommands = class {
6588
7024
  resolveMessageToolHints: ({ channel, accountId }) => resolvePluginChannelMessageToolHints({
6589
7025
  registry: pluginRegistry,
6590
7026
  channel,
6591
- cfg: resolveConfigSecrets2(loadConfig10(), { configPath: runtimeConfigPath }),
7027
+ cfg: resolveConfigSecrets2(loadConfig12(), { configPath: runtimeConfigPath }),
6592
7028
  accountId
6593
7029
  })
6594
7030
  });
@@ -6621,14 +7057,14 @@ var ServiceCommands = class {
6621
7057
  });
6622
7058
  let pluginChannelBindings = getPluginChannelBindings3(pluginRegistry);
6623
7059
  setPluginRuntimeBridge({
6624
- loadConfig: () => toPluginConfigView(resolveConfigSecrets2(loadConfig10(), { configPath: runtimeConfigPath }), pluginChannelBindings),
7060
+ loadConfig: () => toPluginConfigView(resolveConfigSecrets2(loadConfig12(), { configPath: runtimeConfigPath }), pluginChannelBindings),
6625
7061
  writeConfigFile: async (nextConfigView) => {
6626
7062
  if (!nextConfigView || typeof nextConfigView !== "object" || Array.isArray(nextConfigView)) {
6627
7063
  throw new Error("plugin runtime writeConfigFile expects an object config");
6628
7064
  }
6629
- const current = loadConfig10();
7065
+ const current = loadConfig12();
6630
7066
  const next = mergePluginConfigView(current, nextConfigView, pluginChannelBindings);
6631
- saveConfig7(next);
7067
+ saveConfig8(next);
6632
7068
  },
6633
7069
  dispatchReplyWithBufferedBlockDispatcher: async ({ ctx, dispatcherOptions }) => {
6634
7070
  const bodyForAgent = typeof ctx.BodyForAgent === "string" ? ctx.BodyForAgent : "";
@@ -6702,12 +7138,12 @@ var ServiceCommands = class {
6702
7138
  providerManager,
6703
7139
  bus,
6704
7140
  gatewayController,
6705
- () => resolveConfigSecrets2(loadConfig10(), { configPath: runtimeConfigPath }),
7141
+ () => resolveConfigSecrets2(loadConfig12(), { configPath: runtimeConfigPath }),
6706
7142
  () => extensionRegistry,
6707
7143
  ({ channel, accountId }) => resolvePluginChannelMessageToolHints({
6708
7144
  registry: pluginRegistry,
6709
7145
  channel,
6710
- cfg: resolveConfigSecrets2(loadConfig10(), { configPath: runtimeConfigPath }),
7146
+ cfg: resolveConfigSecrets2(loadConfig12(), { configPath: runtimeConfigPath }),
6711
7147
  accountId
6712
7148
  })
6713
7149
  );
@@ -6744,7 +7180,7 @@ var ServiceCommands = class {
6744
7180
  return trimmed || void 0;
6745
7181
  }
6746
7182
  watchConfigFile(reloader) {
6747
- const configPath = resolve10(getConfigPath3());
7183
+ const configPath = resolve10(getConfigPath5());
6748
7184
  const watcher = chokidar.watch(configPath, {
6749
7185
  ignoreInitial: true,
6750
7186
  awaitWriteFinish: { stabilityThreshold: 200, pollInterval: 50 }
@@ -6865,7 +7301,7 @@ var ServiceCommands = class {
6865
7301
  });
6866
7302
  }
6867
7303
  async runForeground(options) {
6868
- const config2 = loadConfig10();
7304
+ const config2 = loadConfig12();
6869
7305
  const uiConfig = resolveUiConfig(config2, options.uiOverrides);
6870
7306
  const uiUrl = resolveUiApiBase(uiConfig.host, uiConfig.port);
6871
7307
  if (options.open) {
@@ -6878,7 +7314,7 @@ var ServiceCommands = class {
6878
7314
  });
6879
7315
  }
6880
7316
  async startService(options) {
6881
- const config2 = loadConfig10();
7317
+ const config2 = loadConfig12();
6882
7318
  const uiConfig = resolveUiConfig(config2, options.uiOverrides);
6883
7319
  const uiUrl = resolveUiApiBase(uiConfig.host, uiConfig.port);
6884
7320
  const apiUrl = `${uiUrl}/api`;
@@ -6939,7 +7375,7 @@ var ServiceCommands = class {
6939
7375
  }
6940
7376
  const logPath = resolveServiceLogPath();
6941
7377
  const logDir = resolve10(logPath, "..");
6942
- mkdirSync5(logDir, { recursive: true });
7378
+ mkdirSync6(logDir, { recursive: true });
6943
7379
  const logFd = openSync(logPath, "a");
6944
7380
  const readinessTimeoutMs = this.resolveStartupTimeoutMs(options.startupTimeoutMs);
6945
7381
  const quickPhaseTimeoutMs = Math.min(8e3, readinessTimeoutMs);
@@ -7292,7 +7728,7 @@ var ServiceCommands = class {
7292
7728
  return null;
7293
7729
  }
7294
7730
  console.error("Error: No API key configured.");
7295
- console.error(`Set one in ${getConfigPath3()} under providers section`);
7731
+ console.error(`Set one in ${getConfigPath5()} under providers section`);
7296
7732
  process.exit(1);
7297
7733
  }
7298
7734
  return new LiteLLMProvider({
@@ -7419,7 +7855,7 @@ var ServiceCommands = class {
7419
7855
  const uiServer = startUiServer({
7420
7856
  host: uiConfig.host,
7421
7857
  port: uiConfig.port,
7422
- configPath: getConfigPath3(),
7858
+ configPath: getConfigPath5(),
7423
7859
  productVersion: getPackageVersion(),
7424
7860
  staticDir: uiStaticDir ?? void 0,
7425
7861
  cronService,
@@ -7507,10 +7943,10 @@ var ServiceCommands = class {
7507
7943
  }
7508
7944
  }
7509
7945
  installBuiltinMarketplaceSkill(slug, force) {
7510
- const workspace = getWorkspacePath9(loadConfig10().agents.defaults.workspace);
7511
- const destination = join6(workspace, "skills", slug);
7512
- const destinationSkillFile = join6(destination, "SKILL.md");
7513
- if (existsSync10(destinationSkillFile) && !force) {
7946
+ const workspace = getWorkspacePath9(loadConfig12().agents.defaults.workspace);
7947
+ const destination = join7(workspace, "skills", slug);
7948
+ const destinationSkillFile = join7(destination, "SKILL.md");
7949
+ if (existsSync11(destinationSkillFile) && !force) {
7514
7950
  return {
7515
7951
  message: `${slug} is already installed`
7516
7952
  };
@@ -7518,15 +7954,15 @@ var ServiceCommands = class {
7518
7954
  const loader = createSkillsLoader(workspace);
7519
7955
  const builtin = (loader?.listSkills(false) ?? []).find((skill) => skill.name === slug && skill.source === "builtin");
7520
7956
  if (!builtin) {
7521
- if (existsSync10(destinationSkillFile)) {
7957
+ if (existsSync11(destinationSkillFile)) {
7522
7958
  return {
7523
7959
  message: `${slug} is already installed`
7524
7960
  };
7525
7961
  }
7526
7962
  return null;
7527
7963
  }
7528
- mkdirSync5(join6(workspace, "skills"), { recursive: true });
7529
- cpSync2(dirname2(builtin.path), destination, { recursive: true, force: true });
7964
+ mkdirSync6(join7(workspace, "skills"), { recursive: true });
7965
+ cpSync2(dirname3(builtin.path), destination, { recursive: true, force: true });
7530
7966
  return {
7531
7967
  message: `Installed skill: ${slug}`
7532
7968
  };
@@ -7585,11 +8021,11 @@ ${stderr}`.trim();
7585
8021
  };
7586
8022
 
7587
8023
  // src/cli/workspace.ts
7588
- import { cpSync as cpSync3, existsSync as existsSync11, mkdirSync as mkdirSync6, readFileSync as readFileSync9, readdirSync as readdirSync3, rmSync as rmSync5, writeFileSync as writeFileSync5 } from "fs";
8024
+ import { cpSync as cpSync3, existsSync as existsSync12, mkdirSync as mkdirSync7, readFileSync as readFileSync10, readdirSync as readdirSync3, rmSync as rmSync5, writeFileSync as writeFileSync6 } from "fs";
7589
8025
  import { createRequire as createRequire2 } from "module";
7590
- import { dirname as dirname3, join as join7, resolve as resolve11 } from "path";
8026
+ import { dirname as dirname4, join as join8, resolve as resolve11 } from "path";
7591
8027
  import { fileURLToPath as fileURLToPath3 } from "url";
7592
- import { APP_NAME as APP_NAME3, getDataDir as getDataDir7 } from "@nextclaw/core";
8028
+ import { APP_NAME as APP_NAME3, getDataDir as getDataDir8 } from "@nextclaw/core";
7593
8029
  import { spawnSync as spawnSync3 } from "child_process";
7594
8030
  var WorkspaceManager = class {
7595
8031
  constructor(logo) {
@@ -7617,30 +8053,30 @@ var WorkspaceManager = class {
7617
8053
  { source: "memory/MEMORY.md", target: "memory/MEMORY.md" }
7618
8054
  ];
7619
8055
  for (const entry of templateFiles) {
7620
- const filePath = join7(workspace, entry.target);
7621
- if (!force && existsSync11(filePath)) {
8056
+ const filePath = join8(workspace, entry.target);
8057
+ if (!force && existsSync12(filePath)) {
7622
8058
  continue;
7623
8059
  }
7624
- const templatePath = join7(templateDir, entry.source);
7625
- if (!existsSync11(templatePath)) {
8060
+ const templatePath = join8(templateDir, entry.source);
8061
+ if (!existsSync12(templatePath)) {
7626
8062
  console.warn(`Warning: Template file missing: ${templatePath}`);
7627
8063
  continue;
7628
8064
  }
7629
- const raw = readFileSync9(templatePath, "utf-8");
8065
+ const raw = readFileSync10(templatePath, "utf-8");
7630
8066
  const content = raw.replace(/\$\{APP_NAME\}/g, APP_NAME3);
7631
- mkdirSync6(dirname3(filePath), { recursive: true });
7632
- writeFileSync5(filePath, content);
8067
+ mkdirSync7(dirname4(filePath), { recursive: true });
8068
+ writeFileSync6(filePath, content);
7633
8069
  created.push(entry.target);
7634
8070
  }
7635
- const memoryDir = join7(workspace, "memory");
7636
- if (!existsSync11(memoryDir)) {
7637
- mkdirSync6(memoryDir, { recursive: true });
7638
- created.push(join7("memory", ""));
8071
+ const memoryDir = join8(workspace, "memory");
8072
+ if (!existsSync12(memoryDir)) {
8073
+ mkdirSync7(memoryDir, { recursive: true });
8074
+ created.push(join8("memory", ""));
7639
8075
  }
7640
- const skillsDir = join7(workspace, "skills");
7641
- if (!existsSync11(skillsDir)) {
7642
- mkdirSync6(skillsDir, { recursive: true });
7643
- created.push(join7("skills", ""));
8076
+ const skillsDir = join8(workspace, "skills");
8077
+ if (!existsSync12(skillsDir)) {
8078
+ mkdirSync7(skillsDir, { recursive: true });
8079
+ created.push(join8("skills", ""));
7644
8080
  }
7645
8081
  const seeded = this.seedBuiltinSkills(skillsDir, { force });
7646
8082
  if (seeded > 0) {
@@ -7659,12 +8095,12 @@ var WorkspaceManager = class {
7659
8095
  if (!entry.isDirectory()) {
7660
8096
  continue;
7661
8097
  }
7662
- const src = join7(sourceDir, entry.name);
7663
- if (!existsSync11(join7(src, "SKILL.md"))) {
8098
+ const src = join8(sourceDir, entry.name);
8099
+ if (!existsSync12(join8(src, "SKILL.md"))) {
7664
8100
  continue;
7665
8101
  }
7666
- const dest = join7(targetDir, entry.name);
7667
- if (!force && existsSync11(dest)) {
8102
+ const dest = join8(targetDir, entry.name);
8103
+ if (!force && existsSync12(dest)) {
7668
8104
  continue;
7669
8105
  }
7670
8106
  try {
@@ -7681,13 +8117,13 @@ var WorkspaceManager = class {
7681
8117
  try {
7682
8118
  const require3 = createRequire2(import.meta.url);
7683
8119
  const entry = require3.resolve("@nextclaw/core");
7684
- const pkgRoot = resolve11(dirname3(entry), "..");
7685
- const distSkills = join7(pkgRoot, "dist", "skills");
7686
- if (existsSync11(distSkills)) {
8120
+ const pkgRoot = resolve11(dirname4(entry), "..");
8121
+ const distSkills = join8(pkgRoot, "dist", "skills");
8122
+ if (existsSync12(distSkills)) {
7687
8123
  return distSkills;
7688
8124
  }
7689
- const srcSkills = join7(pkgRoot, "src", "agent", "skills");
7690
- if (existsSync11(srcSkills)) {
8125
+ const srcSkills = join8(pkgRoot, "src", "agent", "skills");
8126
+ if (existsSync12(srcSkills)) {
7691
8127
  return srcSkills;
7692
8128
  }
7693
8129
  return null;
@@ -7702,17 +8138,17 @@ var WorkspaceManager = class {
7702
8138
  }
7703
8139
  const cliDir = resolve11(fileURLToPath3(new URL(".", import.meta.url)));
7704
8140
  const pkgRoot = resolve11(cliDir, "..", "..");
7705
- const candidates = [join7(pkgRoot, "templates")];
8141
+ const candidates = [join8(pkgRoot, "templates")];
7706
8142
  for (const candidate of candidates) {
7707
- if (existsSync11(candidate)) {
8143
+ if (existsSync12(candidate)) {
7708
8144
  return candidate;
7709
8145
  }
7710
8146
  }
7711
8147
  return null;
7712
8148
  }
7713
8149
  getBridgeDir() {
7714
- const userBridge = join7(getDataDir7(), "bridge");
7715
- if (existsSync11(join7(userBridge, "dist", "index.js"))) {
8150
+ const userBridge = join8(getDataDir8(), "bridge");
8151
+ if (existsSync12(join8(userBridge, "dist", "index.js"))) {
7716
8152
  return userBridge;
7717
8153
  }
7718
8154
  if (!which("npm")) {
@@ -7721,12 +8157,12 @@ var WorkspaceManager = class {
7721
8157
  }
7722
8158
  const cliDir = resolve11(fileURLToPath3(new URL(".", import.meta.url)));
7723
8159
  const pkgRoot = resolve11(cliDir, "..", "..");
7724
- const pkgBridge = join7(pkgRoot, "bridge");
7725
- const srcBridge = join7(pkgRoot, "..", "..", "bridge");
8160
+ const pkgBridge = join8(pkgRoot, "bridge");
8161
+ const srcBridge = join8(pkgRoot, "..", "..", "bridge");
7726
8162
  let source = null;
7727
- if (existsSync11(join7(pkgBridge, "package.json"))) {
8163
+ if (existsSync12(join8(pkgBridge, "package.json"))) {
7728
8164
  source = pkgBridge;
7729
- } else if (existsSync11(join7(srcBridge, "package.json"))) {
8165
+ } else if (existsSync12(join8(srcBridge, "package.json"))) {
7730
8166
  source = srcBridge;
7731
8167
  }
7732
8168
  if (!source) {
@@ -7734,8 +8170,8 @@ var WorkspaceManager = class {
7734
8170
  process.exit(1);
7735
8171
  }
7736
8172
  console.log(`${this.logo} Setting up bridge...`);
7737
- mkdirSync6(resolve11(userBridge, ".."), { recursive: true });
7738
- if (existsSync11(userBridge)) {
8173
+ mkdirSync7(resolve11(userBridge, ".."), { recursive: true });
8174
+ if (existsSync12(userBridge)) {
7739
8175
  rmSync5(userBridge, { recursive: true, force: true });
7740
8176
  }
7741
8177
  cpSync3(source, userBridge, {
@@ -7786,6 +8222,8 @@ var CliRuntime = class {
7786
8222
  pluginCommands;
7787
8223
  channelCommands;
7788
8224
  cronCommands;
8225
+ platformAuthCommands;
8226
+ remoteCommands;
7789
8227
  diagnosticsCommands;
7790
8228
  constructor(options = {}) {
7791
8229
  this.logo = options.logo ?? LOGO;
@@ -7807,6 +8245,8 @@ var CliRuntime = class {
7807
8245
  requestRestart: (params) => this.requestRestart(params)
7808
8246
  });
7809
8247
  this.cronCommands = new CronCommands();
8248
+ this.platformAuthCommands = new PlatformAuthCommands();
8249
+ this.remoteCommands = new RemoteCommands();
7810
8250
  this.diagnosticsCommands = new DiagnosticsCommands({ logo: this.logo });
7811
8251
  this.restartCoordinator = new RestartCoordinator({
7812
8252
  readServiceState,
@@ -7872,7 +8312,7 @@ var CliRuntime = class {
7872
8312
  const delayMs = typeof params.delayMs === "number" && Number.isFinite(params.delayMs) ? Math.max(0, Math.floor(params.delayMs)) : 100;
7873
8313
  const cliPath = process.env.NEXTCLAW_SELF_RELAUNCH_CLI?.trim() || fileURLToPath4(new URL("./index.js", import.meta.url));
7874
8314
  const startArgs = [cliPath, "start", "--ui-port", String(uiPort)];
7875
- const serviceStatePath = resolve12(getDataDir8(), "run", "service.json");
8315
+ const serviceStatePath = resolve12(getDataDir9(), "run", "service.json");
7876
8316
  const helperScript = [
7877
8317
  'const { spawnSync } = require("node:child_process");',
7878
8318
  'const { readFileSync } = require("node:fs");',
@@ -8000,18 +8440,18 @@ var CliRuntime = class {
8000
8440
  const source = options.source ?? "init";
8001
8441
  const prefix = options.auto ? "Auto init" : "Init";
8002
8442
  const force = Boolean(options.force);
8003
- const configPath = getConfigPath4();
8443
+ const configPath = getConfigPath6();
8004
8444
  let createdConfig = false;
8005
- if (!existsSync12(configPath)) {
8445
+ if (!existsSync13(configPath)) {
8006
8446
  const config3 = ConfigSchema2.parse({});
8007
- saveConfig8(config3);
8447
+ saveConfig9(config3);
8008
8448
  createdConfig = true;
8009
8449
  }
8010
- const config2 = loadConfig11();
8450
+ const config2 = loadConfig13();
8011
8451
  const workspaceSetting = config2.agents.defaults.workspace;
8012
- const workspacePath = !workspaceSetting || workspaceSetting === DEFAULT_WORKSPACE_PATH ? join8(getDataDir8(), DEFAULT_WORKSPACE_DIR) : expandHome2(workspaceSetting);
8013
- const workspaceExisted = existsSync12(workspacePath);
8014
- mkdirSync7(workspacePath, { recursive: true });
8452
+ const workspacePath = !workspaceSetting || workspaceSetting === DEFAULT_WORKSPACE_PATH ? join9(getDataDir9(), DEFAULT_WORKSPACE_DIR) : expandHome2(workspaceSetting);
8453
+ const workspaceExisted = existsSync13(workspacePath);
8454
+ mkdirSync8(workspacePath, { recursive: true });
8015
8455
  const templateResult = this.workspaceManager.createWorkspaceTemplates(
8016
8456
  workspacePath,
8017
8457
  { force }
@@ -8042,73 +8482,10 @@ ${this.logo} ${APP_NAME4} is ready! (${source})`);
8042
8482
  }
8043
8483
  async login(opts = {}) {
8044
8484
  await this.init({ source: "login", auto: true });
8045
- const configPath = getConfigPath4();
8046
- const config2 = loadConfig11(configPath);
8047
- const providers = config2.providers;
8048
- const nextclawProvider = providers.nextclaw ?? {
8049
- displayName: "",
8050
- apiKey: "",
8051
- apiBase: null,
8052
- extraHeaders: null,
8053
- wireApi: "auto",
8054
- models: []
8055
- };
8056
- const configuredApiBase = typeof nextclawProvider.apiBase === "string" && nextclawProvider.apiBase.trim().length > 0 ? nextclawProvider.apiBase.trim() : "https://ai-gateway-api.nextclaw.io/v1";
8057
- const requestedApiBase = typeof opts.apiBase === "string" && opts.apiBase.trim().length > 0 ? opts.apiBase.trim() : configuredApiBase;
8058
- const platformBase = requestedApiBase.replace(/\/v1\/?$/i, "");
8059
- const v1Base = `${platformBase}/v1`;
8060
- let email = typeof opts.email === "string" ? opts.email.trim() : "";
8061
- let password = typeof opts.password === "string" ? opts.password : "";
8062
- if (!email || !password) {
8063
- const rl = createInterface2({
8064
- input: process.stdin,
8065
- output: process.stdout
8066
- });
8067
- try {
8068
- if (!email) {
8069
- email = (await prompt(rl, "Email: ")).trim();
8070
- }
8071
- if (!password) {
8072
- password = await prompt(rl, "Password: ");
8073
- }
8074
- } finally {
8075
- rl.close();
8076
- }
8077
- }
8078
- if (!email || !password) {
8079
- throw new Error("Email and password are required.");
8080
- }
8081
- const endpoint = opts.register ? `${platformBase}/platform/auth/register` : `${platformBase}/platform/auth/login`;
8082
- const response = await fetch(endpoint, {
8083
- method: "POST",
8084
- headers: {
8085
- "Content-Type": "application/json"
8086
- },
8087
- body: JSON.stringify({ email, password })
8088
- });
8089
- const raw = await response.text();
8090
- let parsed = null;
8091
- try {
8092
- parsed = JSON.parse(raw);
8093
- } catch {
8094
- parsed = null;
8095
- }
8096
- if (!response.ok) {
8097
- const maybeMessage = typeof parsed === "object" && parsed && "error" in parsed && typeof parsed.error?.message === "string" ? parsed.error.message : raw || `Request failed (${response.status})`;
8098
- throw new Error(maybeMessage);
8099
- }
8100
- const token = typeof parsed === "object" && parsed && "data" in parsed && typeof parsed.data?.token === "string" ? parsed.data.token : "";
8101
- const role = typeof parsed === "object" && parsed && "data" in parsed && typeof parsed.data?.user?.role === "string" ? parsed.data.user.role : "user";
8102
- if (!token) {
8103
- throw new Error("Login succeeded but token is missing.");
8104
- }
8105
- nextclawProvider.apiBase = v1Base;
8106
- nextclawProvider.apiKey = token;
8107
- providers.nextclaw = nextclawProvider;
8108
- saveConfig8(config2, configPath);
8109
- console.log(`\u2713 Logged in to NextClaw platform (${platformBase})`);
8110
- console.log(`\u2713 Account: ${email} (${role})`);
8111
- console.log(`\u2713 Token saved into providers.nextclaw.apiKey`);
8485
+ await this.platformAuthCommands.login(opts);
8486
+ }
8487
+ async remoteConnect(opts = {}) {
8488
+ await this.remoteCommands.connect(opts);
8112
8489
  }
8113
8490
  async gateway(opts) {
8114
8491
  const uiOverrides = {
@@ -8199,8 +8576,8 @@ ${this.logo} ${APP_NAME4} is ready! (${source})`);
8199
8576
  await this.serviceCommands.stopService();
8200
8577
  }
8201
8578
  async agent(opts) {
8202
- const configPath = getConfigPath4();
8203
- const config2 = resolveConfigSecrets3(loadConfig11(), { configPath });
8579
+ const configPath = getConfigPath6();
8580
+ const config2 = resolveConfigSecrets3(loadConfig13(), { configPath });
8204
8581
  const workspace = getWorkspacePath10(config2.agents.defaults.workspace);
8205
8582
  const pluginRegistry = loadPluginRegistry(config2, workspace);
8206
8583
  const extensionRegistry = toExtensionRegistry(pluginRegistry);
@@ -8208,7 +8585,7 @@ ${this.logo} ${APP_NAME4} is ready! (${source})`);
8208
8585
  const pluginChannelBindings = getPluginChannelBindings4(pluginRegistry);
8209
8586
  setPluginRuntimeBridge2({
8210
8587
  loadConfig: () => toPluginConfigView(
8211
- resolveConfigSecrets3(loadConfig11(), { configPath }),
8588
+ resolveConfigSecrets3(loadConfig13(), { configPath }),
8212
8589
  pluginChannelBindings
8213
8590
  ),
8214
8591
  writeConfigFile: async (nextConfigView) => {
@@ -8217,13 +8594,13 @@ ${this.logo} ${APP_NAME4} is ready! (${source})`);
8217
8594
  "plugin runtime writeConfigFile expects an object config"
8218
8595
  );
8219
8596
  }
8220
- const current = loadConfig11();
8597
+ const current = loadConfig13();
8221
8598
  const next = mergePluginConfigView(
8222
8599
  current,
8223
8600
  nextConfigView,
8224
8601
  pluginChannelBindings
8225
8602
  );
8226
- saveConfig8(next);
8603
+ saveConfig9(next);
8227
8604
  }
8228
8605
  });
8229
8606
  try {
@@ -8249,7 +8626,7 @@ ${this.logo} ${APP_NAME4} is ready! (${source})`);
8249
8626
  resolveMessageToolHints: ({ channel, accountId }) => resolvePluginChannelMessageToolHints2({
8250
8627
  registry: pluginRegistry,
8251
8628
  channel,
8252
- cfg: resolveConfigSecrets3(loadConfig11(), { configPath }),
8629
+ cfg: resolveConfigSecrets3(loadConfig13(), { configPath }),
8253
8630
  accountId
8254
8631
  })
8255
8632
  });
@@ -8268,11 +8645,11 @@ ${this.logo} ${APP_NAME4} is ready! (${source})`);
8268
8645
  `${this.logo} Interactive mode (type exit or Ctrl+C to quit)
8269
8646
  `
8270
8647
  );
8271
- const historyFile = join8(getDataDir8(), "history", "cli_history");
8648
+ const historyFile = join9(getDataDir9(), "history", "cli_history");
8272
8649
  const historyDir = resolve12(historyFile, "..");
8273
- mkdirSync7(historyDir, { recursive: true });
8274
- const history = existsSync12(historyFile) ? readFileSync10(historyFile, "utf-8").split("\n").filter(Boolean) : [];
8275
- const rl = createInterface2({
8650
+ mkdirSync8(historyDir, { recursive: true });
8651
+ const history = existsSync13(historyFile) ? readFileSync11(historyFile, "utf-8").split("\n").filter(Boolean) : [];
8652
+ const rl = createInterface3({
8276
8653
  input: process.stdin,
8277
8654
  output: process.stdout
8278
8655
  });
@@ -8280,7 +8657,7 @@ ${this.logo} ${APP_NAME4} is ready! (${source})`);
8280
8657
  const merged = history.concat(
8281
8658
  rl.history ?? []
8282
8659
  );
8283
- writeFileSync6(historyFile, merged.join("\n"));
8660
+ writeFileSync7(historyFile, merged.join("\n"));
8284
8661
  process.exit(0);
8285
8662
  });
8286
8663
  let running = true;
@@ -8444,7 +8821,7 @@ ${this.logo} ${APP_NAME4} is ready! (${source})`);
8444
8821
  await this.diagnosticsCommands.doctor(opts);
8445
8822
  }
8446
8823
  async skillsInstall(options) {
8447
- const config2 = loadConfig11();
8824
+ const config2 = loadConfig13();
8448
8825
  const workdir = resolveSkillsInstallWorkdir({
8449
8826
  explicitWorkdir: options.workdir,
8450
8827
  configuredWorkspace: config2.agents.defaults.workspace
@@ -8512,6 +8889,8 @@ program.name(APP_NAME5).description(`${LOGO} ${APP_NAME5} - ${APP_TAGLINE}`).ver
8512
8889
  program.command("onboard").description(`Initialize ${APP_NAME5} configuration and workspace`).action(async () => runtime.onboard());
8513
8890
  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) }));
8514
8891
  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));
8892
+ var remote = program.command("remote").description("Manage remote access");
8893
+ 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));
8515
8894
  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));
8516
8895
  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));
8517
8896
  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));