nextclaw 0.13.8 → 0.13.9

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 (32) hide show
  1. package/dist/cli/index.js +398 -77
  2. package/package.json +6 -6
  3. package/ui-dist/assets/ChannelsList-DH1Ur9XW.js +0 -1
  4. package/ui-dist/assets/ChatPage-YTDcN7XS.js +0 -38
  5. package/ui-dist/assets/DocBrowser-Bi-RpLIw.js +0 -1
  6. package/ui-dist/assets/LogoBadge-BCR9CU7n.js +0 -1
  7. package/ui-dist/assets/MarketplacePage-BgCdiku7.js +0 -49
  8. package/ui-dist/assets/McpMarketplacePage-nyCbiQH6.js +0 -40
  9. package/ui-dist/assets/ModelConfig-Cf4AAYaB.js +0 -1
  10. package/ui-dist/assets/ProvidersList-CfkfKQbw.js +0 -1
  11. package/ui-dist/assets/RuntimeConfig-BI-zClCl.js +0 -1
  12. package/ui-dist/assets/SearchConfig-MBmvco1J.js +0 -1
  13. package/ui-dist/assets/SecretsConfig-CC2B6pVQ.js +0 -3
  14. package/ui-dist/assets/SessionsConfig-CTxJeVQs.js +0 -2
  15. package/ui-dist/assets/chat-message-5OiyZViy.js +0 -3
  16. package/ui-dist/assets/config-hints-CApS3K_7.js +0 -1
  17. package/ui-dist/assets/config-layout-BHnOoweL.js +0 -1
  18. package/ui-dist/assets/index-C2OKcVdN.css +0 -1
  19. package/ui-dist/assets/index-ElnZdv5o.js +0 -1
  20. package/ui-dist/assets/index-LgjZxLjc.js +0 -8
  21. package/ui-dist/assets/label-CPdcDrir.js +0 -1
  22. package/ui-dist/assets/marketplace-localization-Dk31LJJJ.js +0 -1
  23. package/ui-dist/assets/page-layout-B8V5_vM_.js +0 -1
  24. package/ui-dist/assets/popover-Bk53A74_.js +0 -1
  25. package/ui-dist/assets/provider-models-D3B_xWXx.js +0 -1
  26. package/ui-dist/assets/security-config-BNjgoyo4.js +0 -1
  27. package/ui-dist/assets/skeleton-NPxxR-L0.js +0 -1
  28. package/ui-dist/assets/switch-EowdzMK2.js +0 -1
  29. package/ui-dist/assets/tabs-custom-BjLv-uCT.js +0 -1
  30. package/ui-dist/assets/useConfirmDialog-TJcJQMfu.js +0 -5
  31. package/ui-dist/assets/vendor-425xp8cv.js +0 -407
  32. package/ui-dist/index.html +0 -18
package/dist/cli/index.js CHANGED
@@ -7,9 +7,9 @@ import { registerRemoteCommands } from "@nextclaw/remote";
7
7
 
8
8
  // src/cli/runtime.ts
9
9
  import {
10
- loadConfig as loadConfig14,
10
+ loadConfig as loadConfig15,
11
11
  saveConfig as saveConfig10,
12
- getConfigPath as getConfigPath7,
12
+ getConfigPath as getConfigPath8,
13
13
  getDataDir as getDataDir9,
14
14
  ConfigSchema as ConfigSchema2,
15
15
  getWorkspacePath as getWorkspacePath10,
@@ -32,7 +32,7 @@ import { existsSync as existsSync12, mkdirSync as mkdirSync7, readFileSync as re
32
32
  import { join as join8, resolve as resolve12 } from "path";
33
33
  import { createInterface as createInterface3 } from "readline";
34
34
  import { fileURLToPath as fileURLToPath5 } from "url";
35
- import { spawn as spawn3 } from "child_process";
35
+ import { spawn as spawn4 } from "child_process";
36
36
 
37
37
  // src/cli/restart-coordinator.ts
38
38
  var RestartCoordinator = class {
@@ -3002,7 +3002,7 @@ function readLoginPayload(raw) {
3002
3002
  return { token, role };
3003
3003
  }
3004
3004
  var PlatformAuthCommands = class {
3005
- async login(opts = {}) {
3005
+ async loginResult(opts = {}) {
3006
3006
  const { configPath, config: config2, providers, nextclawProvider, platformBase, v1Base, inputApiBase } = resolveProviderConfig(opts);
3007
3007
  const { email, password } = await resolveCredentials(opts);
3008
3008
  const endpoint = opts.register ? `${platformBase}/platform/auth/register` : `${platformBase}/platform/auth/login`;
@@ -3029,10 +3029,28 @@ var PlatformAuthCommands = class {
3029
3029
  nextclawProvider.apiKey = token;
3030
3030
  providers.nextclaw = nextclawProvider;
3031
3031
  saveConfig6(config2, configPath);
3032
- console.log(`\u2713 Logged in to NextClaw platform (${platformBase})`);
3033
- console.log(`\u2713 Account: ${email} (${role})`);
3032
+ return {
3033
+ token,
3034
+ role,
3035
+ email,
3036
+ platformBase,
3037
+ v1Base
3038
+ };
3039
+ }
3040
+ async login(opts = {}) {
3041
+ const result = await this.loginResult(opts);
3042
+ console.log(`\u2713 Logged in to NextClaw platform (${result.platformBase})`);
3043
+ console.log(`\u2713 Account: ${result.email} (${result.role})`);
3034
3044
  console.log(`\u2713 Token saved into providers.nextclaw.apiKey`);
3035
3045
  }
3046
+ logout() {
3047
+ const { configPath, config: config2, providers, nextclawProvider } = resolveProviderConfig({});
3048
+ const cleared = Boolean(nextclawProvider.apiKey?.trim());
3049
+ nextclawProvider.apiKey = "";
3050
+ providers.nextclaw = nextclawProvider;
3051
+ saveConfig6(config2, configPath);
3052
+ return { cleared };
3053
+ }
3036
3054
  };
3037
3055
 
3038
3056
  // src/cli/commands/remote.ts
@@ -3135,15 +3153,18 @@ async function probeLocalUi(localOrigin) {
3135
3153
  }
3136
3154
  }
3137
3155
  var RemoteCommands = class {
3138
- enableConfig(opts = {}) {
3156
+ updateConfig(params = {}) {
3139
3157
  const config2 = loadConfig9(getConfigPath4());
3158
+ const nextEnabled = typeof params.enabled === "boolean" ? params.enabled : config2.remote.enabled;
3159
+ const nextPlatformApiBase = typeof params.apiBase === "string" ? params.apiBase.trim() : config2.remote.platformApiBase;
3160
+ const nextDeviceName = typeof params.name === "string" ? params.name.trim() : config2.remote.deviceName;
3140
3161
  const next = {
3141
3162
  ...config2,
3142
3163
  remote: {
3143
3164
  ...config2.remote,
3144
- enabled: true,
3145
- ...normalizeOptionalString3(opts.apiBase) ? { platformApiBase: normalizeOptionalString3(opts.apiBase) ?? "" } : {},
3146
- ...normalizeOptionalString3(opts.name) ? { deviceName: normalizeOptionalString3(opts.name) ?? "" } : {}
3165
+ enabled: nextEnabled,
3166
+ platformApiBase: nextPlatformApiBase,
3167
+ deviceName: nextDeviceName
3147
3168
  }
3148
3169
  };
3149
3170
  saveConfig7(next);
@@ -3152,20 +3173,15 @@ var RemoteCommands = class {
3152
3173
  config: next
3153
3174
  };
3154
3175
  }
3176
+ enableConfig(opts = {}) {
3177
+ return this.updateConfig({
3178
+ enabled: true,
3179
+ apiBase: typeof opts.apiBase === "string" ? opts.apiBase : void 0,
3180
+ name: typeof opts.name === "string" ? opts.name : void 0
3181
+ });
3182
+ }
3155
3183
  disableConfig() {
3156
- const config2 = loadConfig9(getConfigPath4());
3157
- const next = {
3158
- ...config2,
3159
- remote: {
3160
- ...config2.remote,
3161
- enabled: false
3162
- }
3163
- };
3164
- saveConfig7(next);
3165
- return {
3166
- changed: config2.remote.enabled !== next.remote.enabled,
3167
- config: next
3168
- };
3184
+ return this.updateConfig({ enabled: false });
3169
3185
  }
3170
3186
  async connect(opts = {}) {
3171
3187
  const connector = createNextclawRemoteConnector();
@@ -3174,23 +3190,31 @@ var RemoteCommands = class {
3174
3190
  mode: "foreground"
3175
3191
  });
3176
3192
  }
3177
- async status(opts = {}) {
3193
+ getStatusView() {
3178
3194
  const config2 = loadConfig9(getConfigPath4());
3179
3195
  const snapshot = resolveNextclawRemoteStatusSnapshot(config2);
3196
+ return {
3197
+ configuredEnabled: snapshot.configuredEnabled,
3198
+ runtime: snapshot.runtime,
3199
+ localOrigin: resolveConfiguredLocalOrigin(config2),
3200
+ deviceName: snapshot.runtime?.deviceName ?? normalizeOptionalString3(config2.remote.deviceName) ?? hostname(),
3201
+ platformBase: snapshot.runtime?.platformBase ?? normalizeOptionalString3(config2.remote.platformApiBase) ?? normalizeOptionalString3(config2.providers.nextclaw?.apiBase) ?? null
3202
+ };
3203
+ }
3204
+ async status(opts = {}) {
3205
+ const view = this.getStatusView();
3180
3206
  if (opts.json) {
3181
- console.log(JSON.stringify(snapshot, null, 2));
3207
+ console.log(JSON.stringify(view, null, 2));
3182
3208
  return;
3183
3209
  }
3184
- const runtime2 = snapshot.runtime;
3210
+ const runtime2 = view.runtime;
3185
3211
  console.log("NextClaw Remote Status");
3186
- console.log(`Enabled: ${snapshot.configuredEnabled ? "yes" : "no"}`);
3212
+ console.log(`Enabled: ${view.configuredEnabled ? "yes" : "no"}`);
3187
3213
  console.log(`Mode: ${runtime2?.mode ?? "service"}`);
3188
3214
  console.log(`State: ${runtime2?.state ?? "disabled"}`);
3189
- console.log(`Device: ${runtime2?.deviceName ?? normalizeOptionalString3(config2.remote.deviceName) ?? hostname()}`);
3190
- console.log(
3191
- `Platform: ${runtime2?.platformBase ?? normalizeOptionalString3(config2.remote.platformApiBase) ?? normalizeOptionalString3(config2.providers.nextclaw?.apiBase) ?? "not set"}`
3192
- );
3193
- console.log(`Local origin: ${runtime2?.localOrigin ?? resolveConfiguredLocalOrigin(config2)}`);
3215
+ console.log(`Device: ${view.deviceName}`);
3216
+ console.log(`Platform: ${view.platformBase ?? "not set"}`);
3217
+ console.log(`Local origin: ${runtime2?.localOrigin ?? view.localOrigin}`);
3194
3218
  if (runtime2?.deviceId) {
3195
3219
  console.log(`Device ID: ${runtime2.deviceId}`);
3196
3220
  }
@@ -3201,7 +3225,7 @@ var RemoteCommands = class {
3201
3225
  console.log(`Last error: ${runtime2.lastError}`);
3202
3226
  }
3203
3227
  }
3204
- async doctor(opts = {}) {
3228
+ async getDoctorView() {
3205
3229
  const config2 = loadConfig9(getConfigPath4());
3206
3230
  const snapshot = resolveNextclawRemoteStatusSnapshot(config2);
3207
3231
  const localOrigin = resolveConfiguredLocalOrigin(config2);
@@ -3235,12 +3259,20 @@ var RemoteCommands = class {
3235
3259
  detail: snapshot.runtime ? snapshot.runtime.state : "no managed remote runtime detected"
3236
3260
  }
3237
3261
  ];
3262
+ return {
3263
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
3264
+ checks,
3265
+ snapshot
3266
+ };
3267
+ }
3268
+ async doctor(opts = {}) {
3269
+ const report = await this.getDoctorView();
3238
3270
  if (opts.json) {
3239
- console.log(JSON.stringify({ generatedAt: (/* @__PURE__ */ new Date()).toISOString(), checks, snapshot }, null, 2));
3271
+ console.log(JSON.stringify(report, null, 2));
3240
3272
  return;
3241
3273
  }
3242
3274
  console.log("NextClaw Remote Doctor");
3243
- for (const check of checks) {
3275
+ for (const check of report.checks) {
3244
3276
  console.log(`${check.ok ? "\u2713" : "\u2717"} ${check.name}: ${check.detail}`);
3245
3277
  }
3246
3278
  }
@@ -3642,7 +3674,7 @@ import {
3642
3674
  import { startUiServer } from "@nextclaw/server";
3643
3675
  import { appendFileSync, closeSync, cpSync as cpSync2, existsSync as existsSync10, mkdirSync as mkdirSync5, openSync } from "fs";
3644
3676
  import { dirname as dirname2, join as join6, resolve as resolve10 } from "path";
3645
- import { spawn as spawn2 } from "child_process";
3677
+ import { spawn as spawn3 } from "child_process";
3646
3678
  import { request as httpRequest } from "http";
3647
3679
  import { request as httpsRequest } from "https";
3648
3680
  import { createServer as createNetServer2 } from "net";
@@ -3658,8 +3690,8 @@ import {
3658
3690
  redactConfigObject
3659
3691
  } from "@nextclaw/core";
3660
3692
  var hashRaw = (raw) => createHash("sha256").update(raw).digest("hex");
3661
- var readConfigSnapshot = (getConfigPath8) => {
3662
- const path2 = getConfigPath8();
3693
+ var readConfigSnapshot = (getConfigPath9) => {
3694
+ const path2 = getConfigPath9();
3663
3695
  let raw = "";
3664
3696
  let parsed = {};
3665
3697
  if (existsSync7(path2)) {
@@ -6368,6 +6400,296 @@ function writeReadyManagedServiceState(params) {
6368
6400
  return state;
6369
6401
  }
6370
6402
 
6403
+ // src/cli/commands/remote-access-host.ts
6404
+ import { getConfigPath as getConfigPath6, loadConfig as loadConfig13 } from "@nextclaw/core";
6405
+ import { spawn as spawn2 } from "child_process";
6406
+ var FORCED_PUBLIC_UI_HOST = "0.0.0.0";
6407
+ function normalizeOptionalString5(value) {
6408
+ if (typeof value !== "string") {
6409
+ return null;
6410
+ }
6411
+ const trimmed = value.trim();
6412
+ return trimmed.length > 0 ? trimmed : null;
6413
+ }
6414
+ function decodeJwtPayload(token) {
6415
+ const segments = token.split(".");
6416
+ if (segments.length < 2) {
6417
+ return null;
6418
+ }
6419
+ try {
6420
+ const raw = Buffer.from(segments[1], "base64url").toString("utf-8");
6421
+ const parsed = JSON.parse(raw);
6422
+ return typeof parsed === "object" && parsed !== null ? parsed : null;
6423
+ } catch {
6424
+ return null;
6425
+ }
6426
+ }
6427
+ function toRemoteRuntimeView(runtime2) {
6428
+ if (!runtime2) {
6429
+ return null;
6430
+ }
6431
+ return {
6432
+ enabled: runtime2.enabled,
6433
+ mode: runtime2.mode,
6434
+ state: runtime2.state,
6435
+ deviceId: runtime2.deviceId,
6436
+ deviceName: runtime2.deviceName,
6437
+ platformBase: runtime2.platformBase,
6438
+ localOrigin: runtime2.localOrigin,
6439
+ lastConnectedAt: runtime2.lastConnectedAt,
6440
+ lastError: runtime2.lastError,
6441
+ updatedAt: runtime2.updatedAt
6442
+ };
6443
+ }
6444
+ var RemoteAccessHost = class {
6445
+ constructor(deps) {
6446
+ this.deps = deps;
6447
+ }
6448
+ getStatus() {
6449
+ const config2 = loadConfig13(getConfigPath6());
6450
+ const status = this.deps.remoteCommands.getStatusView();
6451
+ const serviceState = readServiceState();
6452
+ const serviceRunning = Boolean(serviceState && isProcessRunning(serviceState.pid));
6453
+ const account = this.readAccountView({
6454
+ token: normalizeOptionalString5(config2.providers.nextclaw?.apiKey),
6455
+ apiBase: normalizeOptionalString5(config2.providers.nextclaw?.apiBase),
6456
+ platformBase: status.platformBase
6457
+ });
6458
+ const service = {
6459
+ running: serviceRunning,
6460
+ currentProcess: Boolean(serviceRunning && serviceState?.pid === process.pid),
6461
+ ...serviceState?.pid ? { pid: serviceState.pid } : {},
6462
+ ...serviceState?.uiUrl ? { uiUrl: serviceState.uiUrl } : {},
6463
+ ...typeof serviceState?.uiPort === "number" ? { uiPort: serviceState.uiPort } : {}
6464
+ };
6465
+ return {
6466
+ account,
6467
+ settings: {
6468
+ enabled: config2.remote.enabled,
6469
+ deviceName: config2.remote.deviceName,
6470
+ platformApiBase: config2.remote.platformApiBase
6471
+ },
6472
+ service,
6473
+ localOrigin: status.localOrigin,
6474
+ configuredEnabled: status.configuredEnabled,
6475
+ platformBase: status.platformBase,
6476
+ runtime: toRemoteRuntimeView(status.runtime)
6477
+ };
6478
+ }
6479
+ async login(input) {
6480
+ await this.deps.platformAuthCommands.loginResult(input);
6481
+ return this.getStatus();
6482
+ }
6483
+ logout() {
6484
+ this.deps.platformAuthCommands.logout();
6485
+ return this.getStatus();
6486
+ }
6487
+ updateSettings(input) {
6488
+ this.deps.remoteCommands.updateConfig({
6489
+ enabled: input.enabled,
6490
+ apiBase: input.platformApiBase,
6491
+ name: input.deviceName
6492
+ });
6493
+ return this.getStatus();
6494
+ }
6495
+ async runDoctor() {
6496
+ const report = await this.deps.remoteCommands.getDoctorView();
6497
+ return {
6498
+ generatedAt: report.generatedAt,
6499
+ checks: report.checks,
6500
+ snapshot: {
6501
+ configuredEnabled: report.snapshot.configuredEnabled,
6502
+ runtime: toRemoteRuntimeView(report.snapshot.runtime)
6503
+ }
6504
+ };
6505
+ }
6506
+ async controlService(action) {
6507
+ const state = readServiceState();
6508
+ const running = Boolean(state && isProcessRunning(state.pid));
6509
+ const currentProcess = Boolean(running && state?.pid === process.pid);
6510
+ const uiOverrides = this.resolveManagedUiOverrides();
6511
+ if (action === "start") {
6512
+ if (running) {
6513
+ return {
6514
+ accepted: true,
6515
+ action,
6516
+ message: currentProcess ? "Managed service is already running for this UI." : "Managed service is already running."
6517
+ };
6518
+ }
6519
+ await this.deps.serviceCommands.startService({
6520
+ uiOverrides,
6521
+ open: false
6522
+ });
6523
+ return {
6524
+ accepted: true,
6525
+ action,
6526
+ message: "Managed service started."
6527
+ };
6528
+ }
6529
+ if (!running) {
6530
+ if (action === "restart") {
6531
+ await this.deps.serviceCommands.startService({
6532
+ uiOverrides,
6533
+ open: false
6534
+ });
6535
+ return {
6536
+ accepted: true,
6537
+ action,
6538
+ message: "Managed service was not running and has been started."
6539
+ };
6540
+ }
6541
+ return {
6542
+ accepted: true,
6543
+ action,
6544
+ message: "No managed service is currently running."
6545
+ };
6546
+ }
6547
+ if (currentProcess) {
6548
+ if (action === "restart") {
6549
+ await this.deps.requestManagedServiceRestart({
6550
+ uiPort: uiOverrides.port ?? 18791
6551
+ });
6552
+ } else {
6553
+ this.scheduleSelfStop();
6554
+ }
6555
+ return {
6556
+ accepted: true,
6557
+ action,
6558
+ message: action === "restart" ? "Restart scheduled. This page may disconnect for a few seconds." : "Stop scheduled. This page will disconnect shortly."
6559
+ };
6560
+ }
6561
+ if (action === "stop") {
6562
+ await this.deps.serviceCommands.stopService();
6563
+ return {
6564
+ accepted: true,
6565
+ action,
6566
+ message: "Managed service stopped."
6567
+ };
6568
+ }
6569
+ await this.deps.serviceCommands.stopService();
6570
+ await this.deps.serviceCommands.startService({
6571
+ uiOverrides,
6572
+ open: false
6573
+ });
6574
+ return {
6575
+ accepted: true,
6576
+ action,
6577
+ message: "Managed service restarted."
6578
+ };
6579
+ }
6580
+ resolveManagedUiOverrides() {
6581
+ const config2 = loadConfig13(getConfigPath6());
6582
+ const resolved = resolveUiConfig(config2, {
6583
+ enabled: true,
6584
+ host: FORCED_PUBLIC_UI_HOST,
6585
+ open: false
6586
+ });
6587
+ return {
6588
+ enabled: true,
6589
+ host: FORCED_PUBLIC_UI_HOST,
6590
+ open: false,
6591
+ port: resolved.port
6592
+ };
6593
+ }
6594
+ readAccountView(params) {
6595
+ if (!params.token) {
6596
+ return {
6597
+ loggedIn: false,
6598
+ apiBase: params.apiBase,
6599
+ platformBase: params.platformBase
6600
+ };
6601
+ }
6602
+ const payload = decodeJwtPayload(params.token);
6603
+ const email = typeof payload?.email === "string" ? payload.email : void 0;
6604
+ const role = typeof payload?.role === "string" ? payload.role : void 0;
6605
+ return {
6606
+ loggedIn: true,
6607
+ email,
6608
+ role,
6609
+ apiBase: params.apiBase,
6610
+ platformBase: params.platformBase
6611
+ };
6612
+ }
6613
+ scheduleSelfStop() {
6614
+ this.launchManagedSelfControl();
6615
+ }
6616
+ launchManagedSelfControl(params = {}) {
6617
+ const script = [
6618
+ 'const { spawn } = require("node:child_process");',
6619
+ 'const { rmSync } = require("node:fs");',
6620
+ `const parentPid = ${process.pid};`,
6621
+ `const serviceStatePath = ${JSON.stringify(resolveServiceStatePath())};`,
6622
+ `const command = ${JSON.stringify(params.command ?? null)};`,
6623
+ `const args = ${JSON.stringify(params.args ?? [])};`,
6624
+ `const cwd = ${JSON.stringify(process.cwd())};`,
6625
+ "const env = process.env;",
6626
+ "function isRunning(pid) {",
6627
+ " try {",
6628
+ " process.kill(pid, 0);",
6629
+ " return true;",
6630
+ " } catch {",
6631
+ " return false;",
6632
+ " }",
6633
+ "}",
6634
+ "setTimeout(() => {",
6635
+ " try {",
6636
+ " process.kill(parentPid, 'SIGTERM');",
6637
+ " } catch {}",
6638
+ "}, 150);",
6639
+ "const startedAt = Date.now();",
6640
+ "const maxWaitMs = 30000;",
6641
+ "const timer = setInterval(() => {",
6642
+ " if (isRunning(parentPid)) {",
6643
+ " if (Date.now() - startedAt > maxWaitMs) {",
6644
+ " try {",
6645
+ " process.kill(parentPid, 'SIGKILL');",
6646
+ " } catch {}",
6647
+ " }",
6648
+ " return;",
6649
+ " }",
6650
+ " clearInterval(timer);",
6651
+ " try {",
6652
+ " rmSync(serviceStatePath, { force: true });",
6653
+ " } catch {}",
6654
+ " if (command) {",
6655
+ " const child = spawn(command, args, { detached: true, stdio: 'ignore', cwd, env });",
6656
+ " child.unref();",
6657
+ " }",
6658
+ " process.exit(0);",
6659
+ "}, 250);"
6660
+ ].join("");
6661
+ const helper = spawn2(process.execPath, ["-e", script], {
6662
+ detached: true,
6663
+ stdio: "ignore",
6664
+ env: process.env,
6665
+ cwd: process.cwd()
6666
+ });
6667
+ helper.unref();
6668
+ }
6669
+ };
6670
+
6671
+ // src/cli/commands/service-remote-access.ts
6672
+ function requestManagedServiceRestart(requestRestart, options = {}) {
6673
+ const uiPort = typeof options.uiPort === "number" && Number.isFinite(options.uiPort) ? Math.floor(options.uiPort) : void 0;
6674
+ const reason = options.reason?.trim() || "remote access service restart";
6675
+ const manualMessage = uiPort ? `Restart the managed service to restore the UI on port ${uiPort}.` : "Restart the managed service to restore the UI.";
6676
+ return requestRestart({
6677
+ reason,
6678
+ manualMessage,
6679
+ strategy: "background-service-or-exit",
6680
+ delayMs: 500,
6681
+ silentOnServiceRestart: true
6682
+ });
6683
+ }
6684
+ function createRemoteAccessHost(params) {
6685
+ return new RemoteAccessHost({
6686
+ serviceCommands: params.serviceCommands,
6687
+ requestManagedServiceRestart: (options) => requestManagedServiceRestart(params.requestRestart, options),
6688
+ remoteCommands: new RemoteCommands(),
6689
+ platformAuthCommands: new PlatformAuthCommands()
6690
+ });
6691
+ }
6692
+
6371
6693
  // src/cli/commands/ui-chat-run-coordinator.ts
6372
6694
  import { existsSync as existsSync9, mkdirSync as mkdirSync4, readdirSync as readdirSync2, readFileSync as readFileSync8, writeFileSync as writeFileSync4 } from "fs";
6373
6695
  import { join as join5 } from "path";
@@ -6992,14 +7314,14 @@ var {
6992
7314
  ChannelManager: ChannelManager2,
6993
7315
  CronService: CronService2,
6994
7316
  getApiBase,
6995
- getConfigPath: getConfigPath6,
7317
+ getConfigPath: getConfigPath7,
6996
7318
  getDataDir: getDataDir7,
6997
7319
  getProvider,
6998
7320
  getProviderName,
6999
7321
  getWorkspacePath: getWorkspacePath9,
7000
7322
  HeartbeatService,
7001
7323
  LiteLLMProvider,
7002
- loadConfig: loadConfig13,
7324
+ loadConfig: loadConfig14,
7003
7325
  MessageBus,
7004
7326
  ProviderManager,
7005
7327
  resolveConfigSecrets: resolveConfigSecrets2,
@@ -7023,8 +7345,8 @@ var ServiceCommands = class {
7023
7345
  async startGateway(options = {}) {
7024
7346
  this.applyLiveConfigReload = null;
7025
7347
  this.liveUiNcpAgent = null;
7026
- const runtimeConfigPath = getConfigPath6();
7027
- const config2 = resolveConfigSecrets2(loadConfig13(), { configPath: runtimeConfigPath });
7348
+ const runtimeConfigPath = getConfigPath7();
7349
+ const config2 = resolveConfigSecrets2(loadConfig14(), { configPath: runtimeConfigPath });
7028
7350
  const workspace = getWorkspacePath9(config2.agents.defaults.workspace);
7029
7351
  let pluginRegistry = loadPluginRegistry(config2, workspace);
7030
7352
  let extensionRegistry = toExtensionRegistry(pluginRegistry);
@@ -7053,7 +7375,7 @@ var ServiceCommands = class {
7053
7375
  sessionManager,
7054
7376
  providerManager,
7055
7377
  makeProvider: (nextConfig) => this.makeProvider(nextConfig, { allowMissing: true }) ?? this.makeMissingProvider(nextConfig),
7056
- loadConfig: () => resolveConfigSecrets2(loadConfig13(), { configPath: runtimeConfigPath }),
7378
+ loadConfig: () => resolveConfigSecrets2(loadConfig14(), { configPath: runtimeConfigPath }),
7057
7379
  getExtensionChannels: () => extensionRegistry.channels,
7058
7380
  onRestartRequired: (paths) => {
7059
7381
  void this.deps.requestRestart({
@@ -7064,13 +7386,13 @@ var ServiceCommands = class {
7064
7386
  }
7065
7387
  });
7066
7388
  this.applyLiveConfigReload = async () => {
7067
- await reloader.applyReloadPlan(resolveConfigSecrets2(loadConfig13(), { configPath: runtimeConfigPath }));
7389
+ await reloader.applyReloadPlan(resolveConfigSecrets2(loadConfig14(), { configPath: runtimeConfigPath }));
7068
7390
  };
7069
7391
  const gatewayController = new GatewayControllerImpl({
7070
7392
  reloader,
7071
7393
  cron: cron2,
7072
7394
  sessionManager,
7073
- getConfigPath: getConfigPath6,
7395
+ getConfigPath: getConfigPath7,
7074
7396
  saveConfig: saveConfig9,
7075
7397
  requestRestart: async (options2) => {
7076
7398
  await this.deps.requestRestart({
@@ -7097,7 +7419,7 @@ var ServiceCommands = class {
7097
7419
  resolveMessageToolHints: ({ channel, accountId }) => resolvePluginChannelMessageToolHints({
7098
7420
  registry: pluginRegistry,
7099
7421
  channel,
7100
- cfg: resolveConfigSecrets2(loadConfig13(), { configPath: runtimeConfigPath }),
7422
+ cfg: resolveConfigSecrets2(loadConfig14(), { configPath: runtimeConfigPath }),
7101
7423
  accountId
7102
7424
  })
7103
7425
  });
@@ -7130,12 +7452,12 @@ var ServiceCommands = class {
7130
7452
  });
7131
7453
  let pluginChannelBindings = getPluginChannelBindings3(pluginRegistry);
7132
7454
  setPluginRuntimeBridge({
7133
- loadConfig: () => toPluginConfigView(resolveConfigSecrets2(loadConfig13(), { configPath: runtimeConfigPath }), pluginChannelBindings),
7455
+ loadConfig: () => toPluginConfigView(resolveConfigSecrets2(loadConfig14(), { configPath: runtimeConfigPath }), pluginChannelBindings),
7134
7456
  writeConfigFile: async (nextConfigView) => {
7135
7457
  if (!nextConfigView || typeof nextConfigView !== "object" || Array.isArray(nextConfigView)) {
7136
7458
  throw new Error("plugin runtime writeConfigFile expects an object config");
7137
7459
  }
7138
- const current = loadConfig13();
7460
+ const current = loadConfig14();
7139
7461
  const next = mergePluginConfigView(current, nextConfigView, pluginChannelBindings);
7140
7462
  saveConfig9(next);
7141
7463
  },
@@ -7211,12 +7533,12 @@ var ServiceCommands = class {
7211
7533
  providerManager,
7212
7534
  bus,
7213
7535
  gatewayController,
7214
- () => resolveConfigSecrets2(loadConfig13(), { configPath: runtimeConfigPath }),
7536
+ () => resolveConfigSecrets2(loadConfig14(), { configPath: runtimeConfigPath }),
7215
7537
  () => extensionRegistry,
7216
7538
  ({ channel, accountId }) => resolvePluginChannelMessageToolHints({
7217
7539
  registry: pluginRegistry,
7218
7540
  channel,
7219
- cfg: resolveConfigSecrets2(loadConfig13(), { configPath: runtimeConfigPath }),
7541
+ cfg: resolveConfigSecrets2(loadConfig14(), { configPath: runtimeConfigPath }),
7220
7542
  accountId
7221
7543
  })
7222
7544
  );
@@ -7254,7 +7576,7 @@ var ServiceCommands = class {
7254
7576
  return trimmed || void 0;
7255
7577
  }
7256
7578
  watchConfigFile(reloader) {
7257
- const configPath = resolve10(getConfigPath6());
7579
+ const configPath = resolve10(getConfigPath7());
7258
7580
  const watcher = chokidar.watch(configPath, {
7259
7581
  ignoreInitial: true,
7260
7582
  awaitWriteFinish: { stabilityThreshold: 200, pollInterval: 50 }
@@ -7375,7 +7697,7 @@ var ServiceCommands = class {
7375
7697
  });
7376
7698
  }
7377
7699
  async runForeground(options) {
7378
- const config2 = loadConfig13();
7700
+ const config2 = loadConfig14();
7379
7701
  const uiConfig = resolveUiConfig(config2, options.uiOverrides);
7380
7702
  const uiUrl = resolveUiApiBase(uiConfig.host, uiConfig.port);
7381
7703
  if (options.open) {
@@ -7388,7 +7710,7 @@ var ServiceCommands = class {
7388
7710
  });
7389
7711
  }
7390
7712
  async startService(options) {
7391
- const config2 = loadConfig13();
7713
+ const config2 = loadConfig14();
7392
7714
  const uiConfig = resolveUiConfig(config2, options.uiOverrides);
7393
7715
  const uiUrl = resolveUiApiBase(uiConfig.host, uiConfig.port);
7394
7716
  const apiUrl = `${uiUrl}/api`;
@@ -7461,7 +7783,7 @@ var ServiceCommands = class {
7461
7783
  console.log(`Starting ${APP_NAME3} background service (readiness timeout ${Math.ceil(readinessTimeoutMs / 1e3)}s)...`);
7462
7784
  const serveArgs = buildServeArgs({ uiPort: uiConfig.port });
7463
7785
  this.appendStartupStage(logPath, `spawning background process: ${process.execPath} ${[...process.execArgv, ...serveArgs].join(" ")}`);
7464
- const child = spawn2(process.execPath, [...process.execArgv, ...serveArgs], {
7786
+ const child = spawn3(process.execPath, [...process.execArgv, ...serveArgs], {
7465
7787
  env: process.env,
7466
7788
  stdio: ["ignore", logFd, logFd],
7467
7789
  detached: true
@@ -7796,7 +8118,7 @@ var ServiceCommands = class {
7796
8118
  return null;
7797
8119
  }
7798
8120
  console.error("Error: No API key configured.");
7799
- console.error(`Set one in ${getConfigPath6()} under providers section`);
8121
+ console.error(`Set one in ${getConfigPath7()} under providers section`);
7800
8122
  process.exit(1);
7801
8123
  }
7802
8124
  return new LiteLLMProvider({
@@ -7909,10 +8231,7 @@ var ServiceCommands = class {
7909
8231
  gatewayController,
7910
8232
  getConfig,
7911
8233
  getExtensionRegistry,
7912
- resolveMessageToolHints: ({ channel, accountId }) => resolveMessageToolHints({
7913
- channel,
7914
- accountId
7915
- })
8234
+ resolveMessageToolHints: ({ channel, accountId }) => resolveMessageToolHints({ channel, accountId })
7916
8235
  });
7917
8236
  this.liveUiNcpAgent = ncpAgent;
7918
8237
  const marketplaceInstaller = new ServiceMarketplaceInstaller({
@@ -7920,10 +8239,11 @@ var ServiceCommands = class {
7920
8239
  runCliSubcommand: (args) => this.runCliSubcommand(args),
7921
8240
  installBuiltinSkill: (slug, force) => this.installBuiltinMarketplaceSkill(slug, force)
7922
8241
  }).createInstaller();
8242
+ const remoteAccess = createRemoteAccessHost({ serviceCommands: this, requestRestart: this.deps.requestRestart });
7923
8243
  const uiServer = startUiServer({
7924
8244
  host: uiConfig.host,
7925
8245
  port: uiConfig.port,
7926
- configPath: getConfigPath6(),
8246
+ configPath: getConfigPath7(),
7927
8247
  productVersion: getPackageVersion(),
7928
8248
  staticDir: uiStaticDir ?? void 0,
7929
8249
  cronService,
@@ -7931,6 +8251,7 @@ var ServiceCommands = class {
7931
8251
  apiBaseUrl: process.env.NEXTCLAW_MARKETPLACE_API_BASE,
7932
8252
  installer: marketplaceInstaller
7933
8253
  },
8254
+ remoteAccess,
7934
8255
  ncpAgent,
7935
8256
  chatRuntime: {
7936
8257
  listSessionTypes: async () => {
@@ -8011,7 +8332,7 @@ var ServiceCommands = class {
8011
8332
  }
8012
8333
  }
8013
8334
  installBuiltinMarketplaceSkill(slug, force) {
8014
- const workspace = getWorkspacePath9(loadConfig13().agents.defaults.workspace);
8335
+ const workspace = getWorkspacePath9(loadConfig14().agents.defaults.workspace);
8015
8336
  const destination = join6(workspace, "skills", slug);
8016
8337
  const destinationSkillFile = join6(destination, "SKILL.md");
8017
8338
  if (existsSync10(destinationSkillFile) && !force) {
@@ -8052,7 +8373,7 @@ ${stderr}`.trim();
8052
8373
  runCommand(command, args, options = {}) {
8053
8374
  const timeoutMs = options.timeoutMs ?? 18e4;
8054
8375
  return new Promise((resolvePromise, rejectPromise) => {
8055
- const child = spawn2(command, args, {
8376
+ const child = spawn3(command, args, {
8056
8377
  cwd: options.cwd ?? process.cwd(),
8057
8378
  env: process.env,
8058
8379
  stdio: ["ignore", "pipe", "pipe"]
@@ -8270,7 +8591,7 @@ var WorkspaceManager = class {
8270
8591
  // src/cli/runtime.ts
8271
8592
  var LOGO = "\u{1F916}";
8272
8593
  var EXIT_COMMANDS = /* @__PURE__ */ new Set(["exit", "quit", "/exit", "/quit", ":q"]);
8273
- var FORCED_PUBLIC_UI_HOST = "0.0.0.0";
8594
+ var FORCED_PUBLIC_UI_HOST2 = "0.0.0.0";
8274
8595
  function resolveSkillsInstallWorkdir(params) {
8275
8596
  if (params.explicitWorkdir) {
8276
8597
  return expandHome2(params.explicitWorkdir);
@@ -8350,7 +8671,7 @@ var CliRuntime = class {
8350
8671
  if (!state || !isProcessRunning(state.pid) || state.pid === process.pid) {
8351
8672
  return false;
8352
8673
  }
8353
- const uiHost = FORCED_PUBLIC_UI_HOST;
8674
+ const uiHost = FORCED_PUBLIC_UI_HOST2;
8354
8675
  const uiPort = typeof state.uiPort === "number" && Number.isFinite(state.uiPort) ? state.uiPort : 18791;
8355
8676
  console.log(
8356
8677
  `Applying changes (${reason}): restarting ${APP_NAME5} background service...`
@@ -8447,7 +8768,7 @@ var CliRuntime = class {
8447
8768
  "}, delayMs);"
8448
8769
  ].join("\n");
8449
8770
  try {
8450
- const helper = spawn3(process.execPath, ["-e", helperScript], {
8771
+ const helper = spawn4(process.execPath, ["-e", helperScript], {
8451
8772
  detached: true,
8452
8773
  stdio: "ignore",
8453
8774
  env: process.env
@@ -8516,14 +8837,14 @@ var CliRuntime = class {
8516
8837
  const source = options.source ?? "init";
8517
8838
  const prefix = options.auto ? "Auto init" : "Init";
8518
8839
  const force = Boolean(options.force);
8519
- const configPath = getConfigPath7();
8840
+ const configPath = getConfigPath8();
8520
8841
  let createdConfig = false;
8521
8842
  if (!existsSync12(configPath)) {
8522
8843
  const config3 = ConfigSchema2.parse({});
8523
8844
  saveConfig10(config3);
8524
8845
  createdConfig = true;
8525
8846
  }
8526
- const config2 = loadConfig14();
8847
+ const config2 = loadConfig15();
8527
8848
  const workspaceSetting = config2.agents.defaults.workspace;
8528
8849
  const workspacePath = !workspaceSetting || workspaceSetting === DEFAULT_WORKSPACE_PATH ? join8(getDataDir9(), DEFAULT_WORKSPACE_DIR) : expandHome2(workspaceSetting);
8529
8850
  const workspaceExisted = existsSync12(workspacePath);
@@ -8562,7 +8883,7 @@ ${this.logo} ${APP_NAME5} is ready! (${source})`);
8562
8883
  }
8563
8884
  async gateway(opts) {
8564
8885
  const uiOverrides = {
8565
- host: FORCED_PUBLIC_UI_HOST
8886
+ host: FORCED_PUBLIC_UI_HOST2
8566
8887
  };
8567
8888
  if (opts.ui) {
8568
8889
  uiOverrides.enabled = true;
@@ -8578,7 +8899,7 @@ ${this.logo} ${APP_NAME5} is ready! (${source})`);
8578
8899
  async ui(opts) {
8579
8900
  const uiOverrides = {
8580
8901
  enabled: true,
8581
- host: FORCED_PUBLIC_UI_HOST,
8902
+ host: FORCED_PUBLIC_UI_HOST2,
8582
8903
  open: Boolean(opts.open)
8583
8904
  };
8584
8905
  if (opts.port) {
@@ -8594,7 +8915,7 @@ ${this.logo} ${APP_NAME5} is ready! (${source})`);
8594
8915
  await this.init({ source: "start", auto: true });
8595
8916
  const uiOverrides = {
8596
8917
  enabled: true,
8597
- host: FORCED_PUBLIC_UI_HOST,
8918
+ host: FORCED_PUBLIC_UI_HOST2,
8598
8919
  open: false
8599
8920
  };
8600
8921
  if (opts.uiPort) {
@@ -8623,7 +8944,7 @@ ${this.logo} ${APP_NAME5} is ready! (${source})`);
8623
8944
  async serve(opts) {
8624
8945
  const uiOverrides = {
8625
8946
  enabled: true,
8626
- host: FORCED_PUBLIC_UI_HOST,
8947
+ host: FORCED_PUBLIC_UI_HOST2,
8627
8948
  open: false
8628
8949
  };
8629
8950
  if (opts.uiPort) {
@@ -8649,8 +8970,8 @@ ${this.logo} ${APP_NAME5} is ready! (${source})`);
8649
8970
  await this.serviceCommands.stopService();
8650
8971
  }
8651
8972
  async agent(opts) {
8652
- const configPath = getConfigPath7();
8653
- const config2 = resolveConfigSecrets3(loadConfig14(), { configPath });
8973
+ const configPath = getConfigPath8();
8974
+ const config2 = resolveConfigSecrets3(loadConfig15(), { configPath });
8654
8975
  const workspace = getWorkspacePath10(config2.agents.defaults.workspace);
8655
8976
  const pluginRegistry = loadPluginRegistry(config2, workspace);
8656
8977
  const extensionRegistry = toExtensionRegistry(pluginRegistry);
@@ -8658,7 +8979,7 @@ ${this.logo} ${APP_NAME5} is ready! (${source})`);
8658
8979
  const pluginChannelBindings = getPluginChannelBindings4(pluginRegistry);
8659
8980
  setPluginRuntimeBridge2({
8660
8981
  loadConfig: () => toPluginConfigView(
8661
- resolveConfigSecrets3(loadConfig14(), { configPath }),
8982
+ resolveConfigSecrets3(loadConfig15(), { configPath }),
8662
8983
  pluginChannelBindings
8663
8984
  ),
8664
8985
  writeConfigFile: async (nextConfigView) => {
@@ -8667,7 +8988,7 @@ ${this.logo} ${APP_NAME5} is ready! (${source})`);
8667
8988
  "plugin runtime writeConfigFile expects an object config"
8668
8989
  );
8669
8990
  }
8670
- const current = loadConfig14();
8991
+ const current = loadConfig15();
8671
8992
  const next = mergePluginConfigView(
8672
8993
  current,
8673
8994
  nextConfigView,
@@ -8699,7 +9020,7 @@ ${this.logo} ${APP_NAME5} is ready! (${source})`);
8699
9020
  resolveMessageToolHints: ({ channel, accountId }) => resolvePluginChannelMessageToolHints2({
8700
9021
  registry: pluginRegistry,
8701
9022
  channel,
8702
- cfg: resolveConfigSecrets3(loadConfig14(), { configPath }),
9023
+ cfg: resolveConfigSecrets3(loadConfig15(), { configPath }),
8703
9024
  accountId
8704
9025
  })
8705
9026
  });
@@ -8894,7 +9215,7 @@ ${this.logo} ${APP_NAME5} is ready! (${source})`);
8894
9215
  await this.diagnosticsCommands.doctor(opts);
8895
9216
  }
8896
9217
  async skillsInstall(options) {
8897
- const config2 = loadConfig14();
9218
+ const config2 = loadConfig15();
8898
9219
  const workdir = resolveSkillsInstallWorkdir({
8899
9220
  explicitWorkdir: options.workdir,
8900
9221
  configuredWorkspace: config2.agents.defaults.workspace