nextclaw 0.34.0 → 0.36.0

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 (55) hide show
  1. package/dist/cli/app/index.js +187 -108
  2. package/dist/cli/app/index.js.map +1 -1
  3. package/package.json +14 -14
  4. package/resources/USAGE.md +63 -1
  5. package/ui-dist/assets/api-CVjdSvJk.js +13 -0
  6. package/ui-dist/assets/{appearance-settings-page-Bpd2RhK5.js → appearance-settings-page-BOorfaUx.js} +1 -1
  7. package/ui-dist/assets/{channels-list-page-CTblam4c.js → channels-list-page-DuZJvYB6.js} +1 -1
  8. package/ui-dist/assets/{chat-page-Bg3_X8G9.js → chat-page-8PTMgI0t.js} +1 -1
  9. package/ui-dist/assets/{config-split-page-ps1Aod3g.js → config-split-page-UuhsVcQD.js} +1 -1
  10. package/ui-dist/assets/{confirm-dialog-DJSUNG33.js → confirm-dialog-Bnosa8UF.js} +1 -1
  11. package/ui-dist/assets/{desktop-update-config-D9AyIhJZ.js → desktop-update-config-CM4lsX4R.js} +1 -1
  12. package/ui-dist/assets/{dist-DkR7_Dpl.js → dist-D17RZAsz.js} +1 -1
  13. package/ui-dist/assets/{dist-BkTYNzm_.js → dist-_zZ8P38T.js} +1 -1
  14. package/ui-dist/assets/{doc-browser-Q67TOGVS.js → doc-browser-CiLvybSC.js} +1 -1
  15. package/ui-dist/assets/{doc-browser-context-BQim8SqD.js → doc-browser-context-CerkiK_N.js} +1 -1
  16. package/ui-dist/assets/doc-browser-z7vdH3Ez.js +1 -0
  17. package/ui-dist/assets/{form-actions-B2-bpFie.js → form-actions-Cmv23dN4.js} +1 -1
  18. package/ui-dist/assets/index-CWdgBpSc.css +1 -0
  19. package/ui-dist/assets/index-DlL8uKXu.js +109 -0
  20. package/ui-dist/assets/loader-circle-vUsFtTfb.js +1 -0
  21. package/ui-dist/assets/mcp-marketplace-page-BV8WB3_L.js +1 -0
  22. package/ui-dist/assets/{mcp-marketplace-page-w8panjZG.js → mcp-marketplace-page-DN_fqcLd.js} +1 -1
  23. package/ui-dist/assets/{model-config-page-B8Aubkwx.js → model-config-page-BxaeCMbJ.js} +1 -1
  24. package/ui-dist/assets/{navigation-link-mpHFXwRr.js → navigation-link-k9rsmtC4.js} +1 -1
  25. package/ui-dist/assets/plus-Dc760kHj.js +1 -0
  26. package/ui-dist/assets/{provider-scoped-model-input-BRnIH-5O.js → provider-scoped-model-input-DLSAB6a5.js} +1 -1
  27. package/ui-dist/assets/{providers-config-page-0r9AZNNZ.js → providers-config-page-DM5N0aeS.js} +1 -1
  28. package/ui-dist/assets/{react-BlXwvOLo.js → react-BgRLh3ee.js} +1 -1
  29. package/ui-dist/assets/{refresh-cw-JFuyDj_P.js → refresh-cw-axvJLmRG.js} +1 -1
  30. package/ui-dist/assets/remote-Cj1GBMaN.js +1 -0
  31. package/ui-dist/assets/rotate-cw-CFByP2O8.js +1 -0
  32. package/ui-dist/assets/{runtime-config-page-BgeYpvMK.js → runtime-config-page-WNzZyjMW.js} +1 -1
  33. package/ui-dist/assets/{save-BL8P8CLX.js → save-DbR4SGk8.js} +1 -1
  34. package/ui-dist/assets/{search-bZL75Pgc.js → search-M9lrb8id.js} +1 -1
  35. package/ui-dist/assets/{search-config-page-CzNzLyd0.js → search-config-page-DmsID8FQ.js} +1 -1
  36. package/ui-dist/assets/{secrets-config-page-C4k3ik0v.js → secrets-config-page-C10OwI0b.js} +1 -1
  37. package/ui-dist/assets/security-config-CvU5JxDT.js +1 -0
  38. package/ui-dist/assets/{select-rUZ8nIZu.js → select-D_YBzMdv.js} +1 -1
  39. package/ui-dist/assets/{settings-2-BI4SCkxQ.js → settings-2-zy40UwTo.js} +1 -1
  40. package/ui-dist/assets/skeleton-BmAtzjDM.js +1 -0
  41. package/ui-dist/assets/{tag-chip-BdI_yIOA.js → tag-chip-D7QRdcph.js} +1 -1
  42. package/ui-dist/assets/x-D9Y9hf_r.js +1 -0
  43. package/ui-dist/index.html +19 -19
  44. package/ui-dist/assets/api-a_GaOFc7.js +0 -13
  45. package/ui-dist/assets/doc-browser-DahzJEWj.js +0 -1
  46. package/ui-dist/assets/index-DSLK7zgR.css +0 -1
  47. package/ui-dist/assets/index-VQAVqwG8.js +0 -109
  48. package/ui-dist/assets/loader-circle-DyXZU92l.js +0 -1
  49. package/ui-dist/assets/mcp-marketplace-page-B0dBE5CV.js +0 -1
  50. package/ui-dist/assets/plus-Dhb4KuMH.js +0 -1
  51. package/ui-dist/assets/remote-DQzE55WW.js +0 -1
  52. package/ui-dist/assets/rotate-cw-DjbbiIW_.js +0 -1
  53. package/ui-dist/assets/security-config-DmJw9QZA.js +0 -1
  54. package/ui-dist/assets/skeleton-CVmEPG43.js +0 -1
  55. package/ui-dist/assets/x-BcEayhV6.js +0 -1
@@ -4,16 +4,16 @@ import { createRequire } from "node:module";
4
4
  import { APP_NAME, APP_TAGLINE, getConfigPath, getDataDir, getRunPath, loadConfig, resolveConfigSecrets } from "@nextclaw/core";
5
5
  import { Command, InvalidArgumentError } from "commander";
6
6
  import { constants, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
7
- import { randomBytes } from "node:crypto";
7
+ import { createHash, randomBytes } from "node:crypto";
8
8
  import { execFile } from "node:child_process";
9
9
  import path, { join, resolve } from "node:path";
10
10
  import { tmpdir } from "node:os";
11
11
  import "@nextclaw/server";
12
12
  import { NextclawDistributionService, NextclawServiceRuntime, readLearningLoopRuntimeConfig } from "@nextclaw/service";
13
13
  import { access, mkdtemp, readFile, rm, stat } from "node:fs/promises";
14
+ import { AppHomeService, AppInstanceInventoryService, AppInstanceStorageService, AppPublishService, AppPublishValidationService } from "@nextclaw/app-runtime";
14
15
  import { McpServiceAppRuntimeService, buildServiceActionId, getServiceAppManifestPath, mergeServiceAppRuntimeActions, readServiceAppManifest } from "@nextclaw/kernel";
15
16
  import { promisify } from "node:util";
16
- import { AppPublishService, AppPublishValidationService } from "@nextclaw/app-runtime";
17
17
  var __commonJSMin = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
18
18
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
19
19
  //#endregion
@@ -2020,7 +2020,7 @@ var require_websocket = /* @__PURE__ */ __commonJSMin(((exports, module) => {
2020
2020
  const http$1 = __require("http");
2021
2021
  const net = __require("net");
2022
2022
  const tls = __require("tls");
2023
- const { randomBytes: randomBytes$1, createHash: createHash$1 } = __require("crypto");
2023
+ const { randomBytes: randomBytes$1, createHash: createHash$2 } = __require("crypto");
2024
2024
  const { Duplex: Duplex$2, Readable } = __require("stream");
2025
2025
  const { URL: URL$1 } = __require("url");
2026
2026
  const PerMessageDeflate = require_permessage_deflate();
@@ -2672,7 +2672,7 @@ var require_websocket = /* @__PURE__ */ __commonJSMin(((exports, module) => {
2672
2672
  abortHandshake(websocket, socket, "Invalid Upgrade header");
2673
2673
  return;
2674
2674
  }
2675
- const digest = createHash$1("sha1").update(key + GUID).digest("base64");
2675
+ const digest = createHash$2("sha1").update(key + GUID).digest("base64");
2676
2676
  if (res.headers["sec-websocket-accept"] !== digest) {
2677
2677
  abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
2678
2678
  return;
@@ -3144,7 +3144,7 @@ var require_websocket_server = /* @__PURE__ */ __commonJSMin(((exports, module)
3144
3144
  const EventEmitter = __require("events");
3145
3145
  const http = __require("http");
3146
3146
  const { Duplex } = __require("stream");
3147
- const { createHash } = __require("crypto");
3147
+ const { createHash: createHash$1 } = __require("crypto");
3148
3148
  const extension = require_extension();
3149
3149
  const PerMessageDeflate = require_permessage_deflate();
3150
3150
  const subprotocol = require_subprotocol();
@@ -3403,7 +3403,7 @@ var require_websocket_server = /* @__PURE__ */ __commonJSMin(((exports, module)
3403
3403
  "HTTP/1.1 101 Switching Protocols",
3404
3404
  "Upgrade: websocket",
3405
3405
  "Connection: Upgrade",
3406
- `Sec-WebSocket-Accept: ${createHash("sha1").update(key + GUID).digest("base64")}`
3406
+ `Sec-WebSocket-Accept: ${createHash$1("sha1").update(key + GUID).digest("base64")}`
3407
3407
  ];
3408
3408
  const ws = new this.options.WebSocket(null, void 0, this.options);
3409
3409
  if (protocols.size) {
@@ -3633,24 +3633,31 @@ const registerHostServiceControls = ({ program, nextclaw }) => {
3633
3633
  //#endregion
3634
3634
  //#region src/cli/app/services/service-app-dev.service.ts
3635
3635
  var ServiceAppDevService = class {
3636
+ instanceInventoryService = new AppInstanceInventoryService();
3637
+ instanceStorageService = new AppInstanceStorageService();
3636
3638
  constructor(params = {}) {
3637
3639
  this.params = params;
3638
3640
  }
3639
- inspect = async (target) => {
3641
+ inspect = async (target, options = {}) => {
3640
3642
  const appPath = path.resolve(target);
3641
3643
  const issues = [];
3642
3644
  const loaded = await this.loadServiceApp(appPath, issues);
3643
3645
  if (!loaded) return this.buildDevReport(appPath, void 0, [], issues);
3646
+ if (options.resetData && options.confirmAppId?.trim() !== loaded.manifest.id) issues.push({
3647
+ severity: "error",
3648
+ code: "service.data.confirmationMismatch",
3649
+ message: `--reset-data requires --confirm ${loaded.manifest.id}.`
3650
+ });
3644
3651
  if (this.hasErrors(issues)) return this.buildDevReport(appPath, this.toServiceAppRecord(appPath, loaded.manifest, this.idleRuntimeStatus), [], issues);
3645
3652
  const runtime = this.createRuntimeService();
3646
- const dataDirectory = await this.createDevDataDirectory();
3653
+ const storage = await this.createDevStorage(appPath, loaded.manifest.id, options.resetData === true);
3647
3654
  try {
3648
- const startRecord = this.toServiceAppRecord(appPath, loaded.manifest, runtime, dataDirectory);
3655
+ const startRecord = this.toServiceAppRecord(appPath, loaded.manifest, runtime, storage);
3649
3656
  const runtimeActions = await runtime.listActions({
3650
3657
  app: startRecord,
3651
3658
  manifest: loaded.manifest
3652
3659
  });
3653
- const record = this.toServiceAppRecord(appPath, loaded.manifest, runtime, dataDirectory);
3660
+ const record = this.toServiceAppRecord(appPath, loaded.manifest, runtime, storage);
3654
3661
  const actions = mergeServiceAppRuntimeActions({
3655
3662
  record,
3656
3663
  manifest: loaded.manifest,
@@ -3659,14 +3666,7 @@ var ServiceAppDevService = class {
3659
3666
  this.collectRuntimeIssues(record, actions, issues);
3660
3667
  return this.buildDevReport(appPath, record, actions, issues);
3661
3668
  } finally {
3662
- try {
3663
- await runtime.dispose();
3664
- } finally {
3665
- await rm(dataDirectory, {
3666
- recursive: true,
3667
- force: true
3668
- });
3669
- }
3669
+ await runtime.dispose();
3670
3670
  }
3671
3671
  };
3672
3672
  call = async (target, actionName, input) => {
@@ -3686,19 +3686,19 @@ var ServiceAppDevService = class {
3686
3686
  return this.buildCallReport(appPath, record, void 0, void 0, issues);
3687
3687
  }
3688
3688
  const runtime = this.createRuntimeService();
3689
- const dataDirectory = await this.createDevDataDirectory();
3689
+ const storage = await this.createDevStorage(appPath, loaded.manifest.id);
3690
3690
  try {
3691
- const record = this.toServiceAppRecord(appPath, loaded.manifest, runtime, dataDirectory);
3691
+ const record = this.toServiceAppRecord(appPath, loaded.manifest, runtime, storage);
3692
3692
  const result = await runtime.invokeAction({
3693
3693
  app: record,
3694
3694
  manifest: loaded.manifest,
3695
3695
  actionName: action,
3696
3696
  input
3697
3697
  });
3698
- const nextRecord = this.toServiceAppRecord(appPath, loaded.manifest, runtime, dataDirectory);
3698
+ const nextRecord = this.toServiceAppRecord(appPath, loaded.manifest, runtime, storage);
3699
3699
  return this.buildCallReport(appPath, nextRecord, buildServiceActionId(loaded.manifest.id, action), result, issues);
3700
3700
  } catch (error) {
3701
- const record = this.toServiceAppRecord(appPath, loaded.manifest, runtime, dataDirectory);
3701
+ const record = this.toServiceAppRecord(appPath, loaded.manifest, runtime, storage);
3702
3702
  issues.push({
3703
3703
  severity: "error",
3704
3704
  code: "service.runtime.callFailed",
@@ -3706,14 +3706,7 @@ var ServiceAppDevService = class {
3706
3706
  });
3707
3707
  return this.buildCallReport(appPath, record, buildServiceActionId(loaded.manifest.id, action), void 0, issues);
3708
3708
  } finally {
3709
- try {
3710
- await runtime.dispose();
3711
- } finally {
3712
- await rm(dataDirectory, {
3713
- recursive: true,
3714
- force: true
3715
- });
3716
- }
3709
+ await runtime.dispose();
3717
3710
  }
3718
3711
  };
3719
3712
  loadServiceApp = async (appPath, issues) => {
@@ -3745,8 +3738,37 @@ var ServiceAppDevService = class {
3745
3738
  return resolveConfigSecrets(loadConfig(configPath), { configPath });
3746
3739
  };
3747
3740
  idleRuntimeStatus = { getStatus: () => ({ status: "idle" }) };
3748
- createDevDataDirectory = async () => await mkdtemp(path.join(tmpdir(), "nextclaw-service-app-dev-data-"));
3749
- toServiceAppRecord = (dirPath, manifest, runtime, dataDirectory) => {
3741
+ createDevStorage = async (appPath, appId, resetData = false) => {
3742
+ const sourceId = this.getSourceId(appPath);
3743
+ const appHomeDirectory = new AppHomeService().getAppHomeDirectory();
3744
+ const instanceDirectory = path.join(appHomeDirectory, "dev-instances", appId, sourceId, "default");
3745
+ if (resetData && await this.pathExists(instanceDirectory)) await this.instanceInventoryService.purgeNested({
3746
+ instancesRoot: appHomeDirectory,
3747
+ pathSegments: [
3748
+ "dev-instances",
3749
+ appId,
3750
+ sourceId,
3751
+ "default"
3752
+ ],
3753
+ appId,
3754
+ instanceId: "default"
3755
+ });
3756
+ return (await this.instanceStorageService.materialize({
3757
+ appId,
3758
+ instanceId: "default",
3759
+ instanceDirectory
3760
+ })).storage;
3761
+ };
3762
+ getSourceId = (appPath) => createHash("sha256").update(appPath).digest("hex").slice(0, 16);
3763
+ pathExists = async (targetPath) => {
3764
+ try {
3765
+ await access(targetPath);
3766
+ return true;
3767
+ } catch {
3768
+ return false;
3769
+ }
3770
+ };
3771
+ toServiceAppRecord = (dirPath, manifest, runtime, storage) => {
3750
3772
  const runtimeStatus = runtime.getStatus(manifest.id);
3751
3773
  const record = {
3752
3774
  id: manifest.id,
@@ -3759,7 +3781,10 @@ var ServiceAppDevService = class {
3759
3781
  enabled: manifest.enabled,
3760
3782
  protocol: manifest.protocol,
3761
3783
  status: manifest.enabled ? runtimeStatus.status : "stopped",
3762
- dataDirectory
3784
+ dataDirectory: storage?.dataDirectory,
3785
+ instanceId: storage?.instanceId,
3786
+ storage,
3787
+ isolation: "full-user"
3763
3788
  };
3764
3789
  if (manifest.description) record.description = manifest.description;
3765
3790
  if (runtimeStatus.lastError) record.lastError = runtimeStatus.lastError;
@@ -4419,7 +4444,10 @@ var AppDevCommandController = class {
4419
4444
  this.serviceAppDevService = serviceAppDevService;
4420
4445
  }
4421
4446
  dev = async (target, options) => {
4422
- const report = await this.serviceAppDevService.inspect(target);
4447
+ const report = await this.serviceAppDevService.inspect(target, {
4448
+ resetData: options.resetData,
4449
+ confirmAppId: options.confirm
4450
+ });
4423
4451
  process.stdout.write(options.json ? `${JSON.stringify(report, null, 2)}\n` : this.format(report));
4424
4452
  if (!report.ok) process.exitCode = 1;
4425
4453
  };
@@ -4442,6 +4470,125 @@ var AppDevCommandController = class {
4442
4470
  };
4443
4471
  };
4444
4472
  //#endregion
4473
+ //#region src/cli/app/services/local-api/local-ui-api-client.service.ts
4474
+ function createLocalUiApiClient() {
4475
+ const apiBase = resolveLocalUiApiBase();
4476
+ return apiBase ? new LocalUiApiClient(apiBase) : null;
4477
+ }
4478
+ var LocalUiApiClient = class {
4479
+ cookie;
4480
+ constructor(apiBase) {
4481
+ this.apiBase = apiBase;
4482
+ }
4483
+ request = async (params) => {
4484
+ const { body, method, path } = params;
4485
+ const cookie = await this.getCookie();
4486
+ const headers = {};
4487
+ if (body) headers["Content-Type"] = "application/json";
4488
+ if (cookie) headers.Cookie = cookie;
4489
+ const response = await fetch(`${this.apiBase}${path}`, {
4490
+ method: method ?? "GET",
4491
+ headers,
4492
+ ...body ? { body: JSON.stringify(body) } : {}
4493
+ });
4494
+ if (!response.ok) throw new Error(`api request failed with status ${response.status}`);
4495
+ const payload = await response.json();
4496
+ if (!payload.ok) throw new Error(payload.error?.message ?? "api request failed");
4497
+ return payload.data;
4498
+ };
4499
+ getCookie = async () => {
4500
+ if (this.cookie !== void 0) return this.cookie;
4501
+ const response = await fetch(`${this.apiBase}/api/auth/bridge`, {
4502
+ method: "POST",
4503
+ headers: { "x-nextclaw-ui-bridge-secret": ensureUiBridgeSecret() }
4504
+ });
4505
+ if (!response.ok) throw new Error(`bridge auth failed with status ${response.status}`);
4506
+ const payload = await response.json();
4507
+ if (!payload.ok) throw new Error(payload.error?.message ?? "bridge auth failed");
4508
+ this.cookie = typeof payload.data.cookie === "string" && payload.data.cookie.trim() ? payload.data.cookie.trim() : null;
4509
+ return this.cookie;
4510
+ };
4511
+ };
4512
+ function resolveLocalUiApiBase() {
4513
+ const state = readRunningRuntimeState(resolve(getRunPath(), "ui-runtime.json")) ?? readRunningRuntimeState(resolve(getRunPath(), "service.json"));
4514
+ if (!state) return null;
4515
+ if (typeof state.uiUrl === "string" && state.uiUrl.trim()) return state.uiUrl.replace(/\/+$/, "");
4516
+ if (typeof state.apiUrl === "string" && state.apiUrl.trim()) return state.apiUrl.replace(/\/api\/?$/, "").replace(/\/+$/, "");
4517
+ return null;
4518
+ }
4519
+ function readRunningRuntimeState(statePath) {
4520
+ if (!existsSync(statePath)) return null;
4521
+ try {
4522
+ const state = JSON.parse(readFileSync(statePath, "utf-8"));
4523
+ return typeof state.pid === "number" && isProcessRunning(state.pid) ? state : null;
4524
+ } catch {
4525
+ return null;
4526
+ }
4527
+ }
4528
+ function isProcessRunning(pid) {
4529
+ try {
4530
+ process.kill(pid, 0);
4531
+ return true;
4532
+ } catch {
4533
+ return false;
4534
+ }
4535
+ }
4536
+ function ensureUiBridgeSecret() {
4537
+ const dirPath = join(getDataDir(), "remote");
4538
+ const secretPath = join(dirPath, "ui-bridge-secret");
4539
+ if (existsSync(secretPath)) {
4540
+ const existing = readFileSync(secretPath, "utf-8").trim();
4541
+ if (existing) return existing;
4542
+ }
4543
+ mkdirSync(dirPath, { recursive: true });
4544
+ const secret = randomBytes(24).toString("hex");
4545
+ writeFileSync(secretPath, `${secret}\n`, "utf-8");
4546
+ return secret;
4547
+ }
4548
+ //#endregion
4549
+ //#region src/cli/app/services/local-api/app-data-live.service.ts
4550
+ var AppDataLiveService = class {
4551
+ constructor(params = {}) {
4552
+ this.params = params;
4553
+ }
4554
+ list = async () => await this.requireApiClient().request({ path: "/api/app-data" });
4555
+ deleteRetained = async (dataId, confirmAppId) => {
4556
+ const normalizedDataId = dataId.trim();
4557
+ const normalizedAppId = confirmAppId.trim();
4558
+ if (!normalizedDataId) throw new Error("App data id is required.");
4559
+ if (!normalizedAppId) throw new Error("--confirm <app-id> is required.");
4560
+ return await this.requireApiClient().request({
4561
+ path: `/api/app-data/${encodeURIComponent(normalizedDataId)}`,
4562
+ method: "DELETE",
4563
+ body: { confirmAppId: normalizedAppId }
4564
+ });
4565
+ };
4566
+ requireApiClient = () => {
4567
+ const client = this.params.createApiClient ? this.params.createApiClient() : createLocalUiApiClient();
4568
+ if (!client) throw new Error("NextClaw UI runtime is not running; start NextClaw before managing App data.");
4569
+ return client;
4570
+ };
4571
+ };
4572
+ //#endregion
4573
+ //#region src/cli/app/controllers/app-data-command.controller.ts
4574
+ var AppDataCommandController = class {
4575
+ constructor(service = new AppDataLiveService()) {
4576
+ this.service = service;
4577
+ }
4578
+ list = async (options) => {
4579
+ const result = await this.service.list();
4580
+ process.stdout.write(options.json ? `${JSON.stringify(result, null, 2)}\n` : this.formatList(result));
4581
+ };
4582
+ deleteRetained = async (dataId, options) => {
4583
+ const result = await this.service.deleteRetained(dataId, options.confirm);
4584
+ process.stdout.write(options.json ? `${JSON.stringify(result, null, 2)}\n` : `Deleted App data: ${result.appId}/${result.instanceId}\n`);
4585
+ };
4586
+ formatList = (result) => {
4587
+ if (result.entries.length === 0) return "App data: none\n";
4588
+ return `${result.entries.map((entry) => `${entry.id}\t${entry.lifecycle}\t${entry.source}\t${entry.appId}\t${entry.usage.totalBytes} B\t${entry.storage.instanceDirectory}`).join("\n")}\n`;
4589
+ };
4590
+ };
4591
+ //#endregion
4445
4592
  //#region src/cli/app/services/service-app-live-runtime.service.ts
4446
4593
  function readErrorMessage(error) {
4447
4594
  if (error instanceof Error && error.message.trim().length > 0) return error.message.trim();
@@ -4504,81 +4651,9 @@ var ServiceAppLiveRuntimeService = class {
4504
4651
  };
4505
4652
  createApiClient = () => {
4506
4653
  if (this.params.createApiClient) return this.params.createApiClient();
4507
- const apiBase = resolveLocalUiApiBase();
4508
- return apiBase ? new LocalUiApiClient(apiBase) : null;
4654
+ return createLocalUiApiClient();
4509
4655
  };
4510
4656
  };
4511
- var LocalUiApiClient = class {
4512
- cookie;
4513
- constructor(apiBase) {
4514
- this.apiBase = apiBase;
4515
- }
4516
- request = async (params) => {
4517
- const { body, method, path } = params;
4518
- const cookie = await this.getCookie();
4519
- const headers = {};
4520
- if (body) headers["Content-Type"] = "application/json";
4521
- if (cookie) headers.Cookie = cookie;
4522
- const requestInit = {
4523
- method: method ?? "GET",
4524
- headers
4525
- };
4526
- if (body) requestInit.body = JSON.stringify(body);
4527
- const response = await fetch(`${this.apiBase}${path}`, requestInit);
4528
- if (!response.ok) throw new Error(`api request failed with status ${response.status}`);
4529
- const payload = await response.json();
4530
- if (!payload.ok) throw new Error(payload.error?.message ?? "api request failed");
4531
- return payload.data;
4532
- };
4533
- getCookie = async () => {
4534
- if (this.cookie !== void 0) return this.cookie;
4535
- const response = await fetch(`${this.apiBase}/api/auth/bridge`, {
4536
- method: "POST",
4537
- headers: { "x-nextclaw-ui-bridge-secret": ensureUiBridgeSecret() }
4538
- });
4539
- if (!response.ok) throw new Error(`bridge auth failed with status ${response.status}`);
4540
- const payload = await response.json();
4541
- if (!payload.ok) throw new Error(payload.error?.message ?? "bridge auth failed");
4542
- this.cookie = typeof payload.data.cookie === "string" && payload.data.cookie.trim().length > 0 ? payload.data.cookie.trim() : null;
4543
- return this.cookie;
4544
- };
4545
- };
4546
- function resolveLocalUiApiBase() {
4547
- const state = readRunningRuntimeState(resolve(getRunPath(), "ui-runtime.json")) ?? readRunningRuntimeState(resolve(getRunPath(), "service.json"));
4548
- if (!state) return null;
4549
- if (typeof state.uiUrl === "string" && state.uiUrl.trim().length > 0) return state.uiUrl.replace(/\/+$/, "");
4550
- if (typeof state.apiUrl === "string" && state.apiUrl.trim().length > 0) return state.apiUrl.replace(/\/api\/?$/, "").replace(/\/+$/, "");
4551
- return null;
4552
- }
4553
- function readRunningRuntimeState(statePath) {
4554
- if (!existsSync(statePath)) return null;
4555
- try {
4556
- const state = JSON.parse(readFileSync(statePath, "utf-8"));
4557
- return typeof state.pid === "number" && isProcessRunning(state.pid) ? state : null;
4558
- } catch {
4559
- return null;
4560
- }
4561
- }
4562
- function isProcessRunning(pid) {
4563
- try {
4564
- process.kill(pid, 0);
4565
- return true;
4566
- } catch {
4567
- return false;
4568
- }
4569
- }
4570
- function ensureUiBridgeSecret() {
4571
- const dirPath = join(getDataDir(), "remote");
4572
- const secretPath = join(dirPath, "ui-bridge-secret");
4573
- if (existsSync(secretPath)) {
4574
- const existing = readFileSync(secretPath, "utf-8").trim();
4575
- if (existing.length > 0) return existing;
4576
- }
4577
- mkdirSync(dirPath, { recursive: true });
4578
- const secret = randomBytes(24).toString("hex");
4579
- writeFileSync(secretPath, `${secret}\n`, "utf-8");
4580
- return secret;
4581
- }
4582
4657
  //#endregion
4583
4658
  //#region src/cli/app/controllers/app-restart-command.controller.ts
4584
4659
  var AppRestartCommandController = class {
@@ -4761,6 +4836,7 @@ function registerAppCommands(program) {
4761
4836
  const app = program.command("app").description("Develop, validate, and publish NextClaw apps");
4762
4837
  const appCheck = new AppCheckCommandController();
4763
4838
  const appDev = new AppDevCommandController();
4839
+ const appData = new AppDataCommandController();
4764
4840
  const appCall = new AppCallCommandController();
4765
4841
  const appRestart = new AppRestartCommandController();
4766
4842
  const appValidatePublish = new AppValidatePublishCommandController();
@@ -4768,7 +4844,10 @@ function registerAppCommands(program) {
4768
4844
  app.command("validate-publish <app-dir>").description("Validate a NextClaw Mini App before Marketplace submission").option("--meta <path>", "Use a custom marketplace metadata file").option("--json", "Output JSON", false).action(async (target, opts) => appValidatePublish.validate(target, opts));
4769
4845
  app.command("publish <app-dir>").description("Submit a NextClaw Mini App to the App Marketplace").option("--meta <path>", "Use a custom marketplace metadata file").option("--allow-warnings", "Submit after reviewing validation warnings", false).option("--json", "Output JSON", false).action(async (target, opts) => appPublish.publish(target, opts));
4770
4846
  app.command("check <app-dir>").description("Check a Panel App or Service App directory").option("--json", "Output JSON", false).action(async (target, opts) => appCheck.check(target, opts));
4771
- app.command("dev <service-app-dir>").description("Start a Service App through the real runtime and inspect its actions").option("--json", "Output JSON", false).action(async (target, opts) => appDev.dev(target, opts));
4847
+ app.command("dev <service-app-dir>").description("Start a Service App through the real runtime and inspect its actions").option("--json", "Output JSON", false).option("--reset-data", "Reset the exact development instance before starting", false).option("--confirm <app-id>", "Confirm the Service App id when resetting data").action(async (target, opts) => appDev.dev(target, opts));
4848
+ const data = app.command("data").description("Inspect and manage NextClaw App data");
4849
+ data.command("list").description("List active and retained App data through the running NextClaw host").option("--json", "Output JSON", false).action(async (opts) => appData.list(opts));
4850
+ data.command("delete <data-id>").description("Permanently delete a retained App data instance").requiredOption("--confirm <app-id>", "Confirm the exact App id").option("--json", "Output JSON", false).action(async (dataId, opts) => appData.deleteRetained(dataId, opts));
4772
4851
  app.command("call <service-app-dir> <action-name>").description("Call a Service App action through the real runtime").option("--input <json>", "JSON object input for the action").option("--json", "Output JSON", false).action(async (target, actionName, opts) => appCall.call(target, actionName, opts));
4773
4852
  app.command("restart <app-id>").description("Restart a live Service App runtime in the running NextClaw UI").option("--json", "Output JSON", false).action(async (appId, opts) => appRestart.restart(appId, opts));
4774
4853
  }