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.
- package/dist/cli/app/index.js +187 -108
- package/dist/cli/app/index.js.map +1 -1
- package/package.json +14 -14
- package/resources/USAGE.md +63 -1
- package/ui-dist/assets/api-CVjdSvJk.js +13 -0
- package/ui-dist/assets/{appearance-settings-page-Bpd2RhK5.js → appearance-settings-page-BOorfaUx.js} +1 -1
- package/ui-dist/assets/{channels-list-page-CTblam4c.js → channels-list-page-DuZJvYB6.js} +1 -1
- package/ui-dist/assets/{chat-page-Bg3_X8G9.js → chat-page-8PTMgI0t.js} +1 -1
- package/ui-dist/assets/{config-split-page-ps1Aod3g.js → config-split-page-UuhsVcQD.js} +1 -1
- package/ui-dist/assets/{confirm-dialog-DJSUNG33.js → confirm-dialog-Bnosa8UF.js} +1 -1
- package/ui-dist/assets/{desktop-update-config-D9AyIhJZ.js → desktop-update-config-CM4lsX4R.js} +1 -1
- package/ui-dist/assets/{dist-DkR7_Dpl.js → dist-D17RZAsz.js} +1 -1
- package/ui-dist/assets/{dist-BkTYNzm_.js → dist-_zZ8P38T.js} +1 -1
- package/ui-dist/assets/{doc-browser-Q67TOGVS.js → doc-browser-CiLvybSC.js} +1 -1
- package/ui-dist/assets/{doc-browser-context-BQim8SqD.js → doc-browser-context-CerkiK_N.js} +1 -1
- package/ui-dist/assets/doc-browser-z7vdH3Ez.js +1 -0
- package/ui-dist/assets/{form-actions-B2-bpFie.js → form-actions-Cmv23dN4.js} +1 -1
- package/ui-dist/assets/index-CWdgBpSc.css +1 -0
- package/ui-dist/assets/index-DlL8uKXu.js +109 -0
- package/ui-dist/assets/loader-circle-vUsFtTfb.js +1 -0
- package/ui-dist/assets/mcp-marketplace-page-BV8WB3_L.js +1 -0
- package/ui-dist/assets/{mcp-marketplace-page-w8panjZG.js → mcp-marketplace-page-DN_fqcLd.js} +1 -1
- package/ui-dist/assets/{model-config-page-B8Aubkwx.js → model-config-page-BxaeCMbJ.js} +1 -1
- package/ui-dist/assets/{navigation-link-mpHFXwRr.js → navigation-link-k9rsmtC4.js} +1 -1
- package/ui-dist/assets/plus-Dc760kHj.js +1 -0
- package/ui-dist/assets/{provider-scoped-model-input-BRnIH-5O.js → provider-scoped-model-input-DLSAB6a5.js} +1 -1
- package/ui-dist/assets/{providers-config-page-0r9AZNNZ.js → providers-config-page-DM5N0aeS.js} +1 -1
- package/ui-dist/assets/{react-BlXwvOLo.js → react-BgRLh3ee.js} +1 -1
- package/ui-dist/assets/{refresh-cw-JFuyDj_P.js → refresh-cw-axvJLmRG.js} +1 -1
- package/ui-dist/assets/remote-Cj1GBMaN.js +1 -0
- package/ui-dist/assets/rotate-cw-CFByP2O8.js +1 -0
- package/ui-dist/assets/{runtime-config-page-BgeYpvMK.js → runtime-config-page-WNzZyjMW.js} +1 -1
- package/ui-dist/assets/{save-BL8P8CLX.js → save-DbR4SGk8.js} +1 -1
- package/ui-dist/assets/{search-bZL75Pgc.js → search-M9lrb8id.js} +1 -1
- package/ui-dist/assets/{search-config-page-CzNzLyd0.js → search-config-page-DmsID8FQ.js} +1 -1
- package/ui-dist/assets/{secrets-config-page-C4k3ik0v.js → secrets-config-page-C10OwI0b.js} +1 -1
- package/ui-dist/assets/security-config-CvU5JxDT.js +1 -0
- package/ui-dist/assets/{select-rUZ8nIZu.js → select-D_YBzMdv.js} +1 -1
- package/ui-dist/assets/{settings-2-BI4SCkxQ.js → settings-2-zy40UwTo.js} +1 -1
- package/ui-dist/assets/skeleton-BmAtzjDM.js +1 -0
- package/ui-dist/assets/{tag-chip-BdI_yIOA.js → tag-chip-D7QRdcph.js} +1 -1
- package/ui-dist/assets/x-D9Y9hf_r.js +1 -0
- package/ui-dist/index.html +19 -19
- package/ui-dist/assets/api-a_GaOFc7.js +0 -13
- package/ui-dist/assets/doc-browser-DahzJEWj.js +0 -1
- package/ui-dist/assets/index-DSLK7zgR.css +0 -1
- package/ui-dist/assets/index-VQAVqwG8.js +0 -109
- package/ui-dist/assets/loader-circle-DyXZU92l.js +0 -1
- package/ui-dist/assets/mcp-marketplace-page-B0dBE5CV.js +0 -1
- package/ui-dist/assets/plus-Dhb4KuMH.js +0 -1
- package/ui-dist/assets/remote-DQzE55WW.js +0 -1
- package/ui-dist/assets/rotate-cw-DjbbiIW_.js +0 -1
- package/ui-dist/assets/security-config-DmJw9QZA.js +0 -1
- package/ui-dist/assets/skeleton-CVmEPG43.js +0 -1
- package/ui-dist/assets/x-BcEayhV6.js +0 -1
package/dist/cli/app/index.js
CHANGED
|
@@ -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$
|
|
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$
|
|
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
|
|
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,
|
|
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,
|
|
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
|
-
|
|
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
|
|
3689
|
+
const storage = await this.createDevStorage(appPath, loaded.manifest.id);
|
|
3690
3690
|
try {
|
|
3691
|
-
const record = this.toServiceAppRecord(appPath, loaded.manifest, runtime,
|
|
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,
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
3749
|
-
|
|
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
|
-
|
|
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
|
}
|