nextclaw 0.45.4 → 0.46.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 +2 -2
- package/dist/cli/launcher/index.js +10 -4
- package/dist/cli/launcher/index.js.map +1 -1
- package/dist/distribution-BAU3XUQJ.js +2 -0
- package/dist/{nextclaw-distribution.utils-Dryg4WzA.js → distribution-CND3AWXk.js} +3 -1
- package/dist/distribution-CND3AWXk.js.map +1 -0
- package/dist/{nextclaw-cli-app-Bwa2L2Jv.js → nextclaw-cli-app-BVdP2e0w.js} +535 -74
- package/dist/nextclaw-cli-app-BVdP2e0w.js.map +1 -0
- package/package.json +13 -13
- package/resources/USAGE.md +118 -113
- package/ui-dist/assets/{api-DU3C4HfX.js → api-aEu9cEI8.js} +1 -1
- package/ui-dist/assets/api-aEu9cEI8.js.gz +0 -0
- package/ui-dist/assets/{appearance-settings-page-Bh_65RFw.js → appearance-settings-page-z4DaHc_E.js} +1 -1
- package/ui-dist/assets/appearance-settings-page-z4DaHc_E.js.gz +0 -0
- package/ui-dist/assets/{book-open-DMnwJfVM.js → book-open-DYRapW_h.js} +1 -1
- package/ui-dist/assets/{channels-list-page-Bd3_YlyV.js → channels-list-page-x07sGqsT.js} +1 -1
- package/ui-dist/assets/channels-list-page-x07sGqsT.js.gz +0 -0
- package/ui-dist/assets/{config-split-page-DON62DcK.js → config-split-page-DJDp86Ne.js} +1 -1
- package/ui-dist/assets/config-split-page-DJDp86Ne.js.gz +0 -0
- package/ui-dist/assets/{confirm-dialog-B539oJpr.js → confirm-dialog-BALTnP33.js} +1 -1
- package/ui-dist/assets/confirm-dialog-BALTnP33.js.gz +0 -0
- package/ui-dist/assets/{createLucideIcon-CLfqNlvk.js → createLucideIcon-CgM0rnsa.js} +1 -1
- package/ui-dist/assets/{desktop-capabilities-page-CQxp8NvQ.js → desktop-capabilities-page-4-v3wKLV.js} +1 -1
- package/ui-dist/assets/desktop-capabilities-page-4-v3wKLV.js.gz +0 -0
- package/ui-dist/assets/{desktop-update-config-DsyKUJbp.js → desktop-update-config-Cl-_XHqU.js} +1 -1
- package/ui-dist/assets/desktop-update-config-Cl-_XHqU.js.gz +0 -0
- package/ui-dist/assets/{dist-GWWehc4w.js → dist-BjYvUhPL.js} +1 -1
- package/ui-dist/assets/dist-BjYvUhPL.js.gz +0 -0
- package/ui-dist/assets/{dist-WPei0-v8.js → dist-N9zc5bkM.js} +1 -1
- package/ui-dist/assets/dist-N9zc5bkM.js.gz +0 -0
- package/ui-dist/assets/{doc-browser-CKpps-rb.js → doc-browser-DwRLokCI.js} +1 -1
- package/ui-dist/assets/doc-browser-DwRLokCI.js.gz +0 -0
- package/ui-dist/assets/doc-browser-JlXVLNRe.js +1 -0
- package/ui-dist/assets/{doc-browser-context-Vx418ptC.js → doc-browser-context-BDuypObS.js} +1 -1
- package/ui-dist/assets/doc-browser-context-BDuypObS.js.gz +0 -0
- package/ui-dist/assets/{extensions-C_FUBhUf.js → extensions-DFQXmSwG.js} +1 -1
- package/ui-dist/assets/extensions-DFQXmSwG.js.gz +0 -0
- package/ui-dist/assets/{form-actions-DJlfyLEH.js → form-actions-CzGmrPAv.js} +1 -1
- package/ui-dist/assets/{index-C83VUh83.js → index-zuqzAvNx.js} +6 -6
- package/ui-dist/assets/index-zuqzAvNx.js.gz +0 -0
- package/ui-dist/assets/loader-circle-D9etMT9f.js +1 -0
- package/ui-dist/assets/{mcp-marketplace-page-CEr3FgVp.js → mcp-marketplace-page-COUopIOc.js} +1 -1
- package/ui-dist/assets/mcp-marketplace-page-COUopIOc.js.gz +0 -0
- package/ui-dist/assets/mcp-marketplace-page-DxPPmYqh.js +1 -0
- package/ui-dist/assets/{model-config-page-B_w0J2p1.js → model-config-page-g9_-hZqZ.js} +1 -1
- package/ui-dist/assets/model-config-page-g9_-hZqZ.js.gz +0 -0
- package/ui-dist/assets/{navigation-link-DI0mOIVl.js → navigation-link-Ci4Z7Phi.js} +1 -1
- package/ui-dist/assets/navigation-link-Ci4Z7Phi.js.gz +0 -0
- package/ui-dist/assets/plus-DKGD04AO.js +1 -0
- package/ui-dist/assets/{privacy-settings-page-D3RzjGyz.js → privacy-settings-page-pNhQQT4r.js} +1 -1
- package/ui-dist/assets/privacy-settings-page-pNhQQT4r.js.gz +0 -0
- package/ui-dist/assets/{providers-config-page-ONfW4tjw.js → providers-config-page-DftW_6KU.js} +1 -1
- package/ui-dist/assets/providers-config-page-DftW_6KU.js.gz +0 -0
- package/ui-dist/assets/{react-BwGMzqXA.js → react-CVFx225w.js} +1 -1
- package/ui-dist/assets/{react-BwGMzqXA.js.gz → react-CVFx225w.js.gz} +0 -0
- package/ui-dist/assets/{refresh-cw-DmR6-f9A.js → refresh-cw-C-3_thgr.js} +1 -1
- package/ui-dist/assets/remote-DS2TkMPm.js +1 -0
- package/ui-dist/assets/{rotate-cw-BUWqEgob.js → rotate-cw-DSFTq_yu.js} +1 -1
- package/ui-dist/assets/{runtime-config-page-CdwKIwZc.js → runtime-config-page-Cd9sOYvn.js} +1 -1
- package/ui-dist/assets/runtime-config-page-Cd9sOYvn.js.gz +0 -0
- package/ui-dist/assets/{save-DdohrEEJ.js → save-CpPr78_S.js} +1 -1
- package/ui-dist/assets/{search-config-page-C0YgfpcA.js → search-config-page-BqoDbKpM.js} +1 -1
- package/ui-dist/assets/search-config-page-BqoDbKpM.js.gz +0 -0
- package/ui-dist/assets/{search-BVeIVVGT.js → search-msqk2-rd.js} +1 -1
- package/ui-dist/assets/{secrets-config-page-Bb28CTv4.js → secrets-config-page-GsahsEVw.js} +1 -1
- package/ui-dist/assets/secrets-config-page-GsahsEVw.js.gz +0 -0
- package/ui-dist/assets/security-config-BnHQyrtG.js +1 -0
- package/ui-dist/assets/{select-DvuHOwPH.js → select-DPKCWajZ.js} +1 -1
- package/ui-dist/assets/select-DPKCWajZ.js.gz +0 -0
- package/ui-dist/assets/skeleton-BsR5rwB7.js +1 -0
- package/ui-dist/assets/{tag-chip-BI9NffVf.js → tag-chip-ClBWeQ4p.js} +1 -1
- package/ui-dist/assets/x-Dp5GAFmP.js +1 -0
- package/ui-dist/index.html +20 -20
- package/ui-dist/index.html.gz +0 -0
- package/dist/nextclaw-cli-app-Bwa2L2Jv.js.map +0 -1
- package/dist/nextclaw-distribution.utils-Dryg4WzA.js.map +0 -1
- package/ui-dist/assets/api-DU3C4HfX.js.gz +0 -0
- package/ui-dist/assets/appearance-settings-page-Bh_65RFw.js.gz +0 -0
- package/ui-dist/assets/channels-list-page-Bd3_YlyV.js.gz +0 -0
- package/ui-dist/assets/config-split-page-DON62DcK.js.gz +0 -0
- package/ui-dist/assets/confirm-dialog-B539oJpr.js.gz +0 -0
- package/ui-dist/assets/desktop-capabilities-page-CQxp8NvQ.js.gz +0 -0
- package/ui-dist/assets/desktop-update-config-DsyKUJbp.js.gz +0 -0
- package/ui-dist/assets/dist-GWWehc4w.js.gz +0 -0
- package/ui-dist/assets/dist-WPei0-v8.js.gz +0 -0
- package/ui-dist/assets/doc-browser-CKpps-rb.js.gz +0 -0
- package/ui-dist/assets/doc-browser-context-Vx418ptC.js.gz +0 -0
- package/ui-dist/assets/doc-browser-y3e2_MXe.js +0 -1
- package/ui-dist/assets/extensions-C_FUBhUf.js.gz +0 -0
- package/ui-dist/assets/index-C83VUh83.js.gz +0 -0
- package/ui-dist/assets/loader-circle-DXH2fVRq.js +0 -1
- package/ui-dist/assets/mcp-marketplace-page-CEr3FgVp.js.gz +0 -0
- package/ui-dist/assets/mcp-marketplace-page-Dj0xiFAV.js +0 -1
- package/ui-dist/assets/model-config-page-B_w0J2p1.js.gz +0 -0
- package/ui-dist/assets/navigation-link-DI0mOIVl.js.gz +0 -0
- package/ui-dist/assets/plus-C6Kx4b6T.js +0 -1
- package/ui-dist/assets/privacy-settings-page-D3RzjGyz.js.gz +0 -0
- package/ui-dist/assets/providers-config-page-ONfW4tjw.js.gz +0 -0
- package/ui-dist/assets/remote-DRh0aAHG.js +0 -1
- package/ui-dist/assets/runtime-config-page-CdwKIwZc.js.gz +0 -0
- package/ui-dist/assets/search-config-page-C0YgfpcA.js.gz +0 -0
- package/ui-dist/assets/secrets-config-page-Bb28CTv4.js.gz +0 -0
- package/ui-dist/assets/security-config-CNdun2s-.js +0 -1
- package/ui-dist/assets/select-DvuHOwPH.js.gz +0 -0
- package/ui-dist/assets/skeleton-u2lc3JJg.js +0 -1
- package/ui-dist/assets/x-27QFIvUt.js +0 -1
|
@@ -9,7 +9,7 @@ import { execFile } from "node:child_process";
|
|
|
9
9
|
import { tmpdir } from "node:os";
|
|
10
10
|
import "@nextclaw/server";
|
|
11
11
|
import { access, mkdtemp, readFile, rm, stat } from "node:fs/promises";
|
|
12
|
-
import { AppBundleService, AppHomeService, AppInstanceInventoryService, AppInstanceStorageService, AppManifestService, AppPlatformTargetService, AppPublishService, AppPublishValidationService, isAppComponentManifestBundle } from "@nextclaw/app-runtime";
|
|
12
|
+
import { AppBuildService, AppBundleService, AppHomeService, AppInstanceInventoryService, AppInstanceStorageService, AppManifestService, AppPlatformTargetService, AppPublishService, AppPublishValidationService, AppRuntimeToolchainService, AppScaffoldService, isAppComponentManifestBundle } from "@nextclaw/app-runtime";
|
|
13
13
|
import { NextclawHarnessError, ServiceAppRuntimeService, buildServiceActionId, getServiceAppManifestPath, mergeServiceAppRuntimeActions, readServiceAppManifest } from "@nextclaw/kernel";
|
|
14
14
|
import { format, promisify } from "node:util";
|
|
15
15
|
import { formatNextClawAppInstallCommand } from "@nextclaw/shared";
|
|
@@ -3630,16 +3630,101 @@ const registerHostServiceControls = ({ program, nextclaw }) => {
|
|
|
3630
3630
|
autostart.command("doctor").description("Diagnose host autostart setup").option("--user", "Inspect the user-level autostart owner", false).option("--system", "Inspect the system-level autostart owner", false).option("--json", "Output JSON", false).action(async (opts) => serviceCommands.autostartDoctor(opts));
|
|
3631
3631
|
};
|
|
3632
3632
|
//#endregion
|
|
3633
|
+
//#region src/cli/app/services/development/service-app-package-target.service.ts
|
|
3634
|
+
var ServiceAppPackageTargetService = class {
|
|
3635
|
+
resolve = async (target, componentId, issues) => {
|
|
3636
|
+
const targetPath = path.resolve(target);
|
|
3637
|
+
if (await this.pathExists(getServiceAppManifestPath(targetPath))) return { appPath: targetPath };
|
|
3638
|
+
if (!await this.pathExists(path.join(targetPath, "manifest.json"))) return { appPath: targetPath };
|
|
3639
|
+
try {
|
|
3640
|
+
const manifestService = new AppManifestService();
|
|
3641
|
+
const bundle = await manifestService.load(targetPath);
|
|
3642
|
+
if (!isAppComponentManifestBundle(bundle)) {
|
|
3643
|
+
issues.push({
|
|
3644
|
+
severity: "error",
|
|
3645
|
+
code: "service.package.schemaUnsupported",
|
|
3646
|
+
message: "app dev/call requires a schema v2 package when the target is a package directory.",
|
|
3647
|
+
fixHint: "Use a Service App directory or a schema v2 package with a Service component."
|
|
3648
|
+
});
|
|
3649
|
+
return null;
|
|
3650
|
+
}
|
|
3651
|
+
const services = bundle.components.filter((component) => component.kind === "service");
|
|
3652
|
+
const selected = this.selectService(services, componentId, issues);
|
|
3653
|
+
if (!selected) return null;
|
|
3654
|
+
return {
|
|
3655
|
+
appPath: selected.componentDirectory,
|
|
3656
|
+
packageContext: { permissions: manifestService.resolvePlatformSecurity(bundle.manifest).permissions }
|
|
3657
|
+
};
|
|
3658
|
+
} catch (error) {
|
|
3659
|
+
issues.push({
|
|
3660
|
+
severity: "error",
|
|
3661
|
+
code: "service.package.invalid",
|
|
3662
|
+
message: `Cannot load schema v2 package: ${error instanceof Error ? error.message : String(error)}`
|
|
3663
|
+
});
|
|
3664
|
+
return null;
|
|
3665
|
+
}
|
|
3666
|
+
};
|
|
3667
|
+
findOwningPackage = async (appPath, issues) => {
|
|
3668
|
+
let candidate = path.dirname(appPath);
|
|
3669
|
+
while (true) {
|
|
3670
|
+
const permissions = await this.readOwningPermissions(candidate, appPath, issues);
|
|
3671
|
+
if (permissions) return { permissions };
|
|
3672
|
+
const parent = path.dirname(candidate);
|
|
3673
|
+
if (parent === candidate) return void 0;
|
|
3674
|
+
candidate = parent;
|
|
3675
|
+
}
|
|
3676
|
+
};
|
|
3677
|
+
selectService = (services, componentId, issues) => {
|
|
3678
|
+
const requested = componentId?.trim();
|
|
3679
|
+
const selected = requested ? services.find((component) => component.id === requested) : services.length === 1 ? services[0] : void 0;
|
|
3680
|
+
if (selected) return selected;
|
|
3681
|
+
const available = services.map((component) => component.id);
|
|
3682
|
+
issues.push({
|
|
3683
|
+
severity: "error",
|
|
3684
|
+
code: requested ? "service.package.componentNotFound" : "service.package.componentRequired",
|
|
3685
|
+
message: requested ? `Service component does not exist in package: ${requested}.` : services.length === 0 ? "The package does not contain a Service component." : "The package contains multiple Service components; choose one with --component.",
|
|
3686
|
+
fixHint: available.length > 0 ? `Available Service components: ${available.join(", ")}.` : void 0
|
|
3687
|
+
});
|
|
3688
|
+
};
|
|
3689
|
+
readOwningPermissions = async (candidate, appPath, issues) => {
|
|
3690
|
+
if (!await this.pathExists(path.join(candidate, "manifest.json"))) return;
|
|
3691
|
+
const manifestService = new AppManifestService();
|
|
3692
|
+
try {
|
|
3693
|
+
const bundle = await manifestService.load(candidate);
|
|
3694
|
+
if (!isAppComponentManifestBundle(bundle)) return void 0;
|
|
3695
|
+
return bundle.components.some((component) => path.resolve(component.componentDirectory) === path.resolve(appPath)) ? manifestService.resolvePlatformSecurity(bundle.manifest).permissions : void 0;
|
|
3696
|
+
} catch (error) {
|
|
3697
|
+
issues.push({
|
|
3698
|
+
severity: "error",
|
|
3699
|
+
code: "service.package.invalid",
|
|
3700
|
+
message: `Cannot load owning schema v2 package at ${candidate}: ${error instanceof Error ? error.message : String(error)}`
|
|
3701
|
+
});
|
|
3702
|
+
return;
|
|
3703
|
+
}
|
|
3704
|
+
};
|
|
3705
|
+
pathExists = async (targetPath) => {
|
|
3706
|
+
try {
|
|
3707
|
+
await access(targetPath);
|
|
3708
|
+
return true;
|
|
3709
|
+
} catch {
|
|
3710
|
+
return false;
|
|
3711
|
+
}
|
|
3712
|
+
};
|
|
3713
|
+
};
|
|
3714
|
+
//#endregion
|
|
3633
3715
|
//#region src/cli/app/services/service-app-dev.service.ts
|
|
3634
3716
|
var ServiceAppDevService = class {
|
|
3635
3717
|
instanceInventoryService = new AppInstanceInventoryService();
|
|
3636
3718
|
instanceStorageService = new AppInstanceStorageService();
|
|
3719
|
+
packageTargetService = new ServiceAppPackageTargetService();
|
|
3637
3720
|
constructor(params = {}) {
|
|
3638
3721
|
this.params = params;
|
|
3639
3722
|
}
|
|
3640
3723
|
inspect = async (target, options = {}) => {
|
|
3641
|
-
const appPath = path.resolve(target);
|
|
3642
3724
|
const issues = [];
|
|
3725
|
+
const resolvedTarget = await this.packageTargetService.resolve(target, options.componentId, issues);
|
|
3726
|
+
const appPath = resolvedTarget?.appPath ?? path.resolve(target);
|
|
3727
|
+
if (!resolvedTarget) return this.buildDevReport(appPath, void 0, [], issues);
|
|
3643
3728
|
const loaded = await this.loadServiceApp(appPath, issues);
|
|
3644
3729
|
if (!loaded) return this.buildDevReport(appPath, void 0, [], issues);
|
|
3645
3730
|
if (options.resetData && options.confirmAppId?.trim() !== loaded.manifest.id) issues.push({
|
|
@@ -3648,10 +3733,15 @@ var ServiceAppDevService = class {
|
|
|
3648
3733
|
message: `--reset-data requires --confirm ${loaded.manifest.id}.`
|
|
3649
3734
|
});
|
|
3650
3735
|
if (this.hasErrors(issues)) return this.buildDevReport(appPath, this.toServiceAppRecord(appPath, loaded.manifest, this.idleRuntimeStatus), [], issues);
|
|
3651
|
-
const portable = await this.loadPortableDevContext(appPath, loaded.manifest, issues);
|
|
3736
|
+
const portable = await this.loadPortableDevContext(appPath, loaded.manifest, issues, resolvedTarget.packageContext);
|
|
3652
3737
|
if (this.hasErrors(issues)) return this.buildDevReport(appPath, this.toServiceAppRecord(appPath, loaded.manifest, this.idleRuntimeStatus), [], issues);
|
|
3653
3738
|
const runtime = this.createRuntimeService();
|
|
3654
|
-
const
|
|
3739
|
+
const transientDirectory = options.transientData ? await mkdtemp(path.join(tmpdir(), "nextclaw-app-check-")) : void 0;
|
|
3740
|
+
const storage = transientDirectory ? (await this.instanceStorageService.materialize({
|
|
3741
|
+
appId: loaded.manifest.id,
|
|
3742
|
+
instanceId: "check",
|
|
3743
|
+
instanceDirectory: path.join(transientDirectory, "instance")
|
|
3744
|
+
})).storage : await this.createDevStorage(appPath, loaded.manifest.id, options.resetData === true);
|
|
3655
3745
|
try {
|
|
3656
3746
|
const startRecord = this.toServiceAppRecord(appPath, loaded.manifest, runtime, storage, portable);
|
|
3657
3747
|
const runtimeActions = await runtime.listActions({
|
|
@@ -3668,11 +3758,17 @@ var ServiceAppDevService = class {
|
|
|
3668
3758
|
return this.buildDevReport(appPath, record, actions, issues);
|
|
3669
3759
|
} finally {
|
|
3670
3760
|
await runtime.dispose();
|
|
3761
|
+
if (transientDirectory) await rm(transientDirectory, {
|
|
3762
|
+
recursive: true,
|
|
3763
|
+
force: true
|
|
3764
|
+
});
|
|
3671
3765
|
}
|
|
3672
3766
|
};
|
|
3673
|
-
call = async (target, actionName, input) => {
|
|
3674
|
-
const appPath = path.resolve(target);
|
|
3767
|
+
call = async (target, actionName, input, options = {}) => {
|
|
3675
3768
|
const issues = [];
|
|
3769
|
+
const resolvedTarget = await this.packageTargetService.resolve(target, options.componentId, issues);
|
|
3770
|
+
const appPath = resolvedTarget?.appPath ?? path.resolve(target);
|
|
3771
|
+
if (!resolvedTarget) return this.buildCallReport(appPath, void 0, void 0, void 0, issues);
|
|
3676
3772
|
const loaded = await this.loadServiceApp(appPath, issues);
|
|
3677
3773
|
if (!loaded) return this.buildCallReport(appPath, void 0, void 0, void 0, issues);
|
|
3678
3774
|
if (this.hasErrors(issues)) return this.buildCallReport(appPath, this.toServiceAppRecord(appPath, loaded.manifest, this.idleRuntimeStatus), void 0, void 0, issues);
|
|
@@ -3686,7 +3782,7 @@ var ServiceAppDevService = class {
|
|
|
3686
3782
|
const record = this.toServiceAppRecord(appPath, loaded.manifest, this.idleRuntimeStatus);
|
|
3687
3783
|
return this.buildCallReport(appPath, record, void 0, void 0, issues);
|
|
3688
3784
|
}
|
|
3689
|
-
const portable = await this.loadPortableDevContext(appPath, loaded.manifest, issues);
|
|
3785
|
+
const portable = await this.loadPortableDevContext(appPath, loaded.manifest, issues, resolvedTarget.packageContext);
|
|
3690
3786
|
if (this.hasErrors(issues)) return this.buildCallReport(appPath, this.toServiceAppRecord(appPath, loaded.manifest, this.idleRuntimeStatus), void 0, void 0, issues);
|
|
3691
3787
|
const runtime = this.createRuntimeService();
|
|
3692
3788
|
const storage = await this.createDevStorage(appPath, loaded.manifest.id);
|
|
@@ -3699,15 +3795,16 @@ var ServiceAppDevService = class {
|
|
|
3699
3795
|
input
|
|
3700
3796
|
});
|
|
3701
3797
|
const nextRecord = this.toServiceAppRecord(appPath, loaded.manifest, runtime, storage, portable);
|
|
3702
|
-
return this.buildCallReport(appPath, nextRecord, buildServiceActionId(loaded.manifest.id, action), result, issues);
|
|
3798
|
+
return this.buildCallReport(appPath, nextRecord, buildServiceActionId(loaded.manifest.id, action), result, issues, runtime.getLastObservation?.());
|
|
3703
3799
|
} catch (error) {
|
|
3704
3800
|
const record = this.toServiceAppRecord(appPath, loaded.manifest, runtime, storage, portable);
|
|
3705
3801
|
issues.push({
|
|
3706
3802
|
severity: "error",
|
|
3707
|
-
code:
|
|
3708
|
-
message: error instanceof Error ? error.message : String(error)
|
|
3803
|
+
code: this.readRuntimeErrorCode(error),
|
|
3804
|
+
message: error instanceof Error ? error.message : String(error),
|
|
3805
|
+
fixHint: this.readRuntimeErrorHint(error)
|
|
3709
3806
|
});
|
|
3710
|
-
return this.buildCallReport(appPath, record, buildServiceActionId(loaded.manifest.id, action), void 0, issues);
|
|
3807
|
+
return this.buildCallReport(appPath, record, buildServiceActionId(loaded.manifest.id, action), void 0, issues, runtime.getLastObservation?.());
|
|
3711
3808
|
} finally {
|
|
3712
3809
|
await runtime.dispose();
|
|
3713
3810
|
}
|
|
@@ -3742,7 +3839,7 @@ var ServiceAppDevService = class {
|
|
|
3742
3839
|
portableServiceRunnerPath: this.params.portableServiceRunnerPath
|
|
3743
3840
|
});
|
|
3744
3841
|
};
|
|
3745
|
-
loadPortableDevContext = async (appPath, manifest, issues) => {
|
|
3842
|
+
loadPortableDevContext = async (appPath, manifest, issues, resolvedPackageContext) => {
|
|
3746
3843
|
if (manifest.protocol !== "wasi-component") return void 0;
|
|
3747
3844
|
const componentPath = path.resolve(appPath, manifest.componentEntry ?? "");
|
|
3748
3845
|
if (!await this.pathExists(componentPath)) {
|
|
@@ -3753,13 +3850,13 @@ var ServiceAppDevService = class {
|
|
|
3753
3850
|
});
|
|
3754
3851
|
return;
|
|
3755
3852
|
}
|
|
3756
|
-
const packageContext = await this.findOwningPackage(appPath);
|
|
3853
|
+
const packageContext = resolvedPackageContext ?? await this.packageTargetService.findOwningPackage(appPath, issues);
|
|
3757
3854
|
if (!packageContext) {
|
|
3758
|
-
issues.push({
|
|
3855
|
+
if (!issues.some((issue) => issue.code === "service.package.invalid")) issues.push({
|
|
3759
3856
|
severity: "error",
|
|
3760
3857
|
code: "service.package.required",
|
|
3761
3858
|
message: "Portable Service App development requires an owning schema v2 Mini App so permissions have one product owner.",
|
|
3762
|
-
fixHint: "Place the service under a manifest.json components entry
|
|
3859
|
+
fixHint: "Place the service under a manifest.json components entry, or run app dev on the package directory."
|
|
3763
3860
|
});
|
|
3764
3861
|
return;
|
|
3765
3862
|
}
|
|
@@ -3768,27 +3865,6 @@ var ServiceAppDevService = class {
|
|
|
3768
3865
|
permissions: packageContext.permissions
|
|
3769
3866
|
};
|
|
3770
3867
|
};
|
|
3771
|
-
findOwningPackage = async (appPath) => {
|
|
3772
|
-
let candidate = path.dirname(appPath);
|
|
3773
|
-
while (true) {
|
|
3774
|
-
const permissions = await this.readOwningPackagePermissions(candidate, appPath);
|
|
3775
|
-
if (permissions) return { permissions };
|
|
3776
|
-
const parent = path.dirname(candidate);
|
|
3777
|
-
if (parent === candidate) return void 0;
|
|
3778
|
-
candidate = parent;
|
|
3779
|
-
}
|
|
3780
|
-
};
|
|
3781
|
-
readOwningPackagePermissions = async (candidate, appPath) => {
|
|
3782
|
-
if (!await this.pathExists(path.join(candidate, "manifest.json"))) return void 0;
|
|
3783
|
-
const manifestService = new AppManifestService();
|
|
3784
|
-
try {
|
|
3785
|
-
const bundle = await manifestService.load(candidate);
|
|
3786
|
-
if (!isAppComponentManifestBundle(bundle)) return void 0;
|
|
3787
|
-
return bundle.components.some((component) => path.resolve(component.componentDirectory) === path.resolve(appPath)) ? manifestService.resolvePlatformSecurity(bundle.manifest).permissions : void 0;
|
|
3788
|
-
} catch {
|
|
3789
|
-
return;
|
|
3790
|
-
}
|
|
3791
|
-
};
|
|
3792
3868
|
loadRuntimeConfig = () => {
|
|
3793
3869
|
const configPath = getConfigPath();
|
|
3794
3870
|
return resolveConfigSecrets(loadConfig(configPath), { configPath });
|
|
@@ -3863,12 +3939,12 @@ var ServiceAppDevService = class {
|
|
|
3863
3939
|
for (const action of actions) {
|
|
3864
3940
|
if (action.runtimeState === "missing") issues.push({
|
|
3865
3941
|
severity: "error",
|
|
3866
|
-
code: "service.action.runtimeMissing",
|
|
3942
|
+
code: record.protocol === "wasi-component" ? "WASI_GUEST_EXPORT_MISSING" : "service.action.runtimeMissing",
|
|
3867
3943
|
message: `Declared action is missing from runtime tools/list: ${action.name}.`
|
|
3868
3944
|
});
|
|
3869
3945
|
if (action.runtimeState === "undeclared") issues.push({
|
|
3870
3946
|
severity: "error",
|
|
3871
|
-
code: "service.action.runtimeUndeclared",
|
|
3947
|
+
code: record.protocol === "wasi-component" ? "WASI_GUEST_EXPORT_UNDECLARED" : "service.action.runtimeUndeclared",
|
|
3872
3948
|
message: `Runtime exposes an undeclared action: ${action.name}.`,
|
|
3873
3949
|
fixHint: `Add "${action.name}" to service-app.json actions or remove it from the MCP server.`
|
|
3874
3950
|
});
|
|
@@ -3881,12 +3957,23 @@ var ServiceAppDevService = class {
|
|
|
3881
3957
|
actions,
|
|
3882
3958
|
issues
|
|
3883
3959
|
});
|
|
3884
|
-
|
|
3960
|
+
readRuntimeErrorCode = (error) => {
|
|
3961
|
+
const code = typeof error === "object" && error !== null ? error.code : void 0;
|
|
3962
|
+
return typeof code === "string" ? code : "service.runtime.callFailed";
|
|
3963
|
+
};
|
|
3964
|
+
readRuntimeErrorHint = (error) => {
|
|
3965
|
+
const details = typeof error === "object" && error !== null ? error.details : void 0;
|
|
3966
|
+
if (!details || typeof details !== "object" || !("logs" in details)) return void 0;
|
|
3967
|
+
const logs = details.logs;
|
|
3968
|
+
return Array.isArray(logs) && logs.every((entry) => typeof entry === "string") ? `Runner log tail:\n${logs.join("\n")}` : void 0;
|
|
3969
|
+
};
|
|
3970
|
+
buildCallReport = (target, app, actionId, result, issues, observation) => ({
|
|
3885
3971
|
ok: !issues.some((issue) => issue.severity === "error"),
|
|
3886
3972
|
target,
|
|
3887
3973
|
actionId,
|
|
3888
3974
|
app,
|
|
3889
3975
|
result,
|
|
3976
|
+
observation,
|
|
3890
3977
|
issues
|
|
3891
3978
|
});
|
|
3892
3979
|
hasErrors = (issues) => issues.some((issue) => issue.severity === "error");
|
|
@@ -3898,14 +3985,15 @@ var AppCallCommandController = class {
|
|
|
3898
3985
|
this.serviceAppDevService = serviceAppDevService;
|
|
3899
3986
|
}
|
|
3900
3987
|
call = async (target, actionName, options) => {
|
|
3901
|
-
const input =
|
|
3988
|
+
const { component, input: rawInput, json } = options;
|
|
3989
|
+
const input = this.parseInput(rawInput);
|
|
3902
3990
|
if (!input.ok) {
|
|
3903
|
-
this.writeInputError(input.message, Boolean(
|
|
3991
|
+
this.writeInputError(input.message, Boolean(json));
|
|
3904
3992
|
process.exitCode = 1;
|
|
3905
3993
|
return;
|
|
3906
3994
|
}
|
|
3907
|
-
const report = await this.serviceAppDevService.call(target, actionName, input.value);
|
|
3908
|
-
process.stdout.write(
|
|
3995
|
+
const report = await this.serviceAppDevService.call(target, actionName, input.value, { componentId: component });
|
|
3996
|
+
process.stdout.write(json ? `${JSON.stringify(report, null, 2)}\n` : this.format(report));
|
|
3909
3997
|
if (!report.ok) process.exitCode = 1;
|
|
3910
3998
|
};
|
|
3911
3999
|
parseInput = (raw) => {
|
|
@@ -3963,6 +4051,23 @@ var AppCallCommandController = class {
|
|
|
3963
4051
|
};
|
|
3964
4052
|
};
|
|
3965
4053
|
//#endregion
|
|
4054
|
+
//#region src/cli/app/controllers/development/app-build-command.controller.ts
|
|
4055
|
+
var AppBuildCommandController = class {
|
|
4056
|
+
constructor(buildService = new AppBuildService()) {
|
|
4057
|
+
this.buildService = buildService;
|
|
4058
|
+
}
|
|
4059
|
+
build = async (target, options) => {
|
|
4060
|
+
const result = await this.buildService.build({
|
|
4061
|
+
appDirectory: target,
|
|
4062
|
+
install: true
|
|
4063
|
+
});
|
|
4064
|
+
process.stdout.write(options.json ? `${JSON.stringify({
|
|
4065
|
+
ok: true,
|
|
4066
|
+
build: result
|
|
4067
|
+
}, null, 2)}\n` : `Built ${result.mainKind}: ${result.mainEntryPath}\n`);
|
|
4068
|
+
};
|
|
4069
|
+
};
|
|
4070
|
+
//#endregion
|
|
3966
4071
|
//#region src/cli/app/utils/app-check.utils.ts
|
|
3967
4072
|
const PANEL_MANIFEST_FILE = "panel-app.json";
|
|
3968
4073
|
const SERVICE_MANIFEST_FILE = "service-app.json";
|
|
@@ -4162,11 +4267,11 @@ var PanelAppCheckService = class {
|
|
|
4162
4267
|
for (const action of actions) {
|
|
4163
4268
|
const match = action.match(SERVICE_ACTION_ID_PATTERN);
|
|
4164
4269
|
if (!match) continue;
|
|
4165
|
-
await this.checkWorkspaceServiceAction(workspaceRoot, match[1] ?? "", match[2] ?? "", cache, issues);
|
|
4270
|
+
await this.checkWorkspaceServiceAction(workspaceRoot, appPath, match[1] ?? "", match[2] ?? "", cache, issues);
|
|
4166
4271
|
}
|
|
4167
4272
|
};
|
|
4168
|
-
checkWorkspaceServiceAction = async (workspaceRoot, serviceId, actionName, cache, issues) => {
|
|
4169
|
-
const manifest = await this.readCachedServiceManifest(workspaceRoot, serviceId, cache, issues);
|
|
4273
|
+
checkWorkspaceServiceAction = async (workspaceRoot, appPath, serviceId, actionName, cache, issues) => {
|
|
4274
|
+
const manifest = await this.readCachedServiceManifest(workspaceRoot, appPath, serviceId, cache, issues);
|
|
4170
4275
|
if (!manifest) return;
|
|
4171
4276
|
const actions = manifest.actions;
|
|
4172
4277
|
if (!isRecord(actions) || !isRecord(actions[actionName])) issues.push({
|
|
@@ -4264,8 +4369,13 @@ var PanelAppCheckService = class {
|
|
|
4264
4369
|
fixHint: `Add "${action}" to panel-app.json actions or update the invoke call.`
|
|
4265
4370
|
});
|
|
4266
4371
|
};
|
|
4267
|
-
readCachedServiceManifest = async (workspaceRoot, serviceId, cache, issues) => {
|
|
4372
|
+
readCachedServiceManifest = async (workspaceRoot, appPath, serviceId, cache, issues) => {
|
|
4268
4373
|
if (cache.has(serviceId)) return cache.get(serviceId) ?? null;
|
|
4374
|
+
const packageManifest = await this.readOwningPackageServiceManifest(appPath, serviceId, issues);
|
|
4375
|
+
if (packageManifest !== void 0) {
|
|
4376
|
+
cache.set(serviceId, packageManifest);
|
|
4377
|
+
return packageManifest;
|
|
4378
|
+
}
|
|
4269
4379
|
const manifestPath = path.join(workspaceRoot, "service-apps", serviceId, SERVICE_MANIFEST_FILE);
|
|
4270
4380
|
if (!await fileExists(manifestPath)) {
|
|
4271
4381
|
issues.push({
|
|
@@ -4289,6 +4399,46 @@ var PanelAppCheckService = class {
|
|
|
4289
4399
|
cache.set(serviceId, manifestResult.value);
|
|
4290
4400
|
return manifestResult.value;
|
|
4291
4401
|
};
|
|
4402
|
+
readOwningPackageServiceManifest = async (appPath, serviceId, issues) => {
|
|
4403
|
+
const bundle = await this.findOwningComponentPackage(appPath, issues);
|
|
4404
|
+
if (bundle === void 0 || bundle === null) return bundle;
|
|
4405
|
+
const service = bundle.components.find((component) => component.kind === "service" && component.id === serviceId);
|
|
4406
|
+
if (!service) {
|
|
4407
|
+
issues.push({
|
|
4408
|
+
severity: "error",
|
|
4409
|
+
code: "panel.action.serviceMissing",
|
|
4410
|
+
message: `Declared Service component does not exist in the owning package: ${serviceId}.`,
|
|
4411
|
+
fixHint: `Add ${serviceId} to manifest.json components or fix panel-app.json actions.`
|
|
4412
|
+
});
|
|
4413
|
+
return null;
|
|
4414
|
+
}
|
|
4415
|
+
const result = await readJsonObject(service.manifestPath);
|
|
4416
|
+
if (result.issue) issues.push({
|
|
4417
|
+
...result.issue,
|
|
4418
|
+
code: `panel.action.${result.issue.code}`
|
|
4419
|
+
});
|
|
4420
|
+
return result.value ?? null;
|
|
4421
|
+
};
|
|
4422
|
+
findOwningComponentPackage = async (appPath, issues) => {
|
|
4423
|
+
let candidate = path.dirname(appPath);
|
|
4424
|
+
while (true) {
|
|
4425
|
+
if (await fileExists(path.join(candidate, "manifest.json"))) try {
|
|
4426
|
+
const bundle = await new AppManifestService().load(candidate);
|
|
4427
|
+
if (isAppComponentManifestBundle(bundle) && this.packageOwnsPanel(bundle, appPath)) return bundle;
|
|
4428
|
+
} catch (error) {
|
|
4429
|
+
issues.push({
|
|
4430
|
+
severity: "error",
|
|
4431
|
+
code: "panel.package.invalid",
|
|
4432
|
+
message: `Cannot load owning schema v2 package at ${candidate}: ${error instanceof Error ? error.message : String(error)}`
|
|
4433
|
+
});
|
|
4434
|
+
return null;
|
|
4435
|
+
}
|
|
4436
|
+
const parent = path.dirname(candidate);
|
|
4437
|
+
if (parent === candidate) return void 0;
|
|
4438
|
+
candidate = parent;
|
|
4439
|
+
}
|
|
4440
|
+
};
|
|
4441
|
+
packageOwnsPanel = (bundle, appPath) => bundle.components.some((component) => component.kind === "panel" && path.resolve(component.componentDirectory) === path.resolve(appPath));
|
|
4292
4442
|
pushIssue = (issues, issue) => {
|
|
4293
4443
|
if (issue) issues.push(issue);
|
|
4294
4444
|
};
|
|
@@ -4453,9 +4603,10 @@ var ServiceAppCheckService = class {
|
|
|
4453
4603
|
//#endregion
|
|
4454
4604
|
//#region src/cli/app/services/app-check.service.ts
|
|
4455
4605
|
var AppCheckService = class {
|
|
4456
|
-
constructor(panelAppCheckService = new PanelAppCheckService(), serviceAppCheckService = new ServiceAppCheckService()) {
|
|
4606
|
+
constructor(panelAppCheckService = new PanelAppCheckService(), serviceAppCheckService = new ServiceAppCheckService(), serviceAppDevService) {
|
|
4457
4607
|
this.panelAppCheckService = panelAppCheckService;
|
|
4458
4608
|
this.serviceAppCheckService = serviceAppCheckService;
|
|
4609
|
+
this.serviceAppDevService = serviceAppDevService;
|
|
4459
4610
|
}
|
|
4460
4611
|
check = async (target) => {
|
|
4461
4612
|
const appPath = path.resolve(target);
|
|
@@ -4465,6 +4616,7 @@ var AppCheckService = class {
|
|
|
4465
4616
|
if (directoryIssue) return this.buildReport(appPath, "unknown", issues);
|
|
4466
4617
|
const hasPanelManifest = await fileExists(path.join(appPath, PANEL_MANIFEST_FILE));
|
|
4467
4618
|
const hasServiceManifest = await fileExists(path.join(appPath, SERVICE_MANIFEST_FILE));
|
|
4619
|
+
if (await fileExists(path.join(appPath, "manifest.json")) && !hasPanelManifest && !hasServiceManifest) return await this.checkPackage(appPath, issues);
|
|
4468
4620
|
if (hasPanelManifest && hasServiceManifest) {
|
|
4469
4621
|
issues.push({
|
|
4470
4622
|
severity: "error",
|
|
@@ -4490,6 +4642,44 @@ var AppCheckService = class {
|
|
|
4490
4642
|
});
|
|
4491
4643
|
return this.buildReport(appPath, "unknown", issues);
|
|
4492
4644
|
};
|
|
4645
|
+
checkPackage = async (appPath, issues) => {
|
|
4646
|
+
try {
|
|
4647
|
+
const bundle = await new AppManifestService().load(appPath);
|
|
4648
|
+
if (!isAppComponentManifestBundle(bundle)) {
|
|
4649
|
+
issues.push({
|
|
4650
|
+
severity: "error",
|
|
4651
|
+
code: "app.package.schemaUnsupported",
|
|
4652
|
+
message: "app check package mode requires schemaVersion 2.",
|
|
4653
|
+
fixHint: "Use napp inspect for a schema v1 App, or migrate it to schema v2 components."
|
|
4654
|
+
});
|
|
4655
|
+
return this.buildReport(appPath, "package", issues);
|
|
4656
|
+
}
|
|
4657
|
+
for (const component of bundle.components) {
|
|
4658
|
+
const componentIssues = await (component.kind === "panel" ? this.panelAppCheckService.check(component.componentDirectory) : this.serviceAppCheckService.check(component.componentDirectory));
|
|
4659
|
+
issues.push(...componentIssues);
|
|
4660
|
+
if (component.kind === "service" && this.serviceAppDevService && !componentIssues.some((issue) => issue.severity === "error")) {
|
|
4661
|
+
const runtimeReport = await this.serviceAppDevService.inspect(appPath, {
|
|
4662
|
+
componentId: component.id,
|
|
4663
|
+
transientData: true
|
|
4664
|
+
});
|
|
4665
|
+
issues.push(...runtimeReport.issues.map((issue) => ({
|
|
4666
|
+
severity: issue.severity,
|
|
4667
|
+
code: issue.code,
|
|
4668
|
+
message: issue.message,
|
|
4669
|
+
...issue.fixHint ? { fixHint: issue.fixHint } : {}
|
|
4670
|
+
})));
|
|
4671
|
+
}
|
|
4672
|
+
}
|
|
4673
|
+
return this.buildReport(appPath, "package", issues);
|
|
4674
|
+
} catch (error) {
|
|
4675
|
+
issues.push({
|
|
4676
|
+
severity: "error",
|
|
4677
|
+
code: "app.package.invalid",
|
|
4678
|
+
message: error instanceof Error ? error.message : String(error)
|
|
4679
|
+
});
|
|
4680
|
+
return this.buildReport(appPath, "package", issues);
|
|
4681
|
+
}
|
|
4682
|
+
};
|
|
4493
4683
|
buildReport = (target, kind, issues) => ({
|
|
4494
4684
|
ok: !issues.some((issue) => issue.severity === "error"),
|
|
4495
4685
|
kind,
|
|
@@ -4529,17 +4719,75 @@ var AppCheckCommandController = class {
|
|
|
4529
4719
|
};
|
|
4530
4720
|
};
|
|
4531
4721
|
//#endregion
|
|
4722
|
+
//#region src/cli/app/controllers/development/app-create-command.controller.ts
|
|
4723
|
+
var AppCreateCommandController = class {
|
|
4724
|
+
constructor(scaffoldService = new AppScaffoldService()) {
|
|
4725
|
+
this.scaffoldService = scaffoldService;
|
|
4726
|
+
}
|
|
4727
|
+
create = async (target, options) => {
|
|
4728
|
+
const result = await this.scaffoldService.scaffold(target, { template: this.parseTemplate(options.template ?? "rust-wasi") });
|
|
4729
|
+
if (options.json) {
|
|
4730
|
+
process.stdout.write(`${JSON.stringify({
|
|
4731
|
+
ok: true,
|
|
4732
|
+
created: result
|
|
4733
|
+
}, null, 2)}\n`);
|
|
4734
|
+
return;
|
|
4735
|
+
}
|
|
4736
|
+
process.stdout.write([
|
|
4737
|
+
`Created NextClaw App at ${result.appDirectory}`,
|
|
4738
|
+
`Template: ${result.template}`,
|
|
4739
|
+
`Manifest: ${result.manifestPath}`,
|
|
4740
|
+
"Next: follow README.md, then run nextclaw app check <app-dir>",
|
|
4741
|
+
""
|
|
4742
|
+
].join("\n"));
|
|
4743
|
+
};
|
|
4744
|
+
parseTemplate = (value) => {
|
|
4745
|
+
if ([
|
|
4746
|
+
"rust-wasi",
|
|
4747
|
+
"starter",
|
|
4748
|
+
"ts-http",
|
|
4749
|
+
"ts-http-lite"
|
|
4750
|
+
].includes(value)) return value;
|
|
4751
|
+
throw new Error(`Unknown App template: ${value}.`);
|
|
4752
|
+
};
|
|
4753
|
+
};
|
|
4754
|
+
//#endregion
|
|
4755
|
+
//#region src/cli/app/controllers/development/app-doctor-command.controller.ts
|
|
4756
|
+
var AppDoctorCommandController = class {
|
|
4757
|
+
constructor(toolchainService = new AppRuntimeToolchainService()) {
|
|
4758
|
+
this.toolchainService = toolchainService;
|
|
4759
|
+
}
|
|
4760
|
+
doctor = async (options) => {
|
|
4761
|
+
const profile = this.parseProfile(options.profile ?? "wasi");
|
|
4762
|
+
const result = await this.toolchainService.doctor(profile);
|
|
4763
|
+
if (options.json) process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
4764
|
+
else process.stdout.write([
|
|
4765
|
+
`NextClaw App doctor (${options.profile ?? "wasi"}): ${result.ok ? "ready" : "not ready"}`,
|
|
4766
|
+
...result.tools.flatMap((tool) => [`${tool.ok ? "ok" : "missing"} ${tool.name}${tool.version ? `: ${tool.version}` : ""}`, ...!tool.ok ? [` Fix: ${tool.installHint}`] : []]),
|
|
4767
|
+
""
|
|
4768
|
+
].join("\n"));
|
|
4769
|
+
if (!result.ok) process.exitCode = 1;
|
|
4770
|
+
};
|
|
4771
|
+
parseProfile = (profile) => {
|
|
4772
|
+
if (profile === "wasi") return "wasi-component";
|
|
4773
|
+
if (profile === "wasi-http" || profile === "all") return profile;
|
|
4774
|
+
throw new Error(`Unsupported App doctor profile: ${profile}. Use wasi, wasi-http, or all.`);
|
|
4775
|
+
};
|
|
4776
|
+
};
|
|
4777
|
+
//#endregion
|
|
4532
4778
|
//#region src/cli/app/controllers/app-dev-command.controller.ts
|
|
4533
4779
|
var AppDevCommandController = class {
|
|
4534
4780
|
constructor(serviceAppDevService = new ServiceAppDevService()) {
|
|
4535
4781
|
this.serviceAppDevService = serviceAppDevService;
|
|
4536
4782
|
}
|
|
4537
4783
|
dev = async (target, options) => {
|
|
4784
|
+
const { component, confirm, json, resetData } = options;
|
|
4538
4785
|
const report = await this.serviceAppDevService.inspect(target, {
|
|
4539
|
-
|
|
4540
|
-
|
|
4786
|
+
componentId: component,
|
|
4787
|
+
resetData,
|
|
4788
|
+
confirmAppId: confirm
|
|
4541
4789
|
});
|
|
4542
|
-
process.stdout.write(
|
|
4790
|
+
process.stdout.write(json ? `${JSON.stringify(report, null, 2)}\n` : this.format(report));
|
|
4543
4791
|
if (!report.ok) process.exitCode = 1;
|
|
4544
4792
|
};
|
|
4545
4793
|
format = (report) => {
|
|
@@ -4582,11 +4830,23 @@ var LocalUiApiClient = class {
|
|
|
4582
4830
|
headers,
|
|
4583
4831
|
...body ? { body: JSON.stringify(body) } : {}
|
|
4584
4832
|
});
|
|
4585
|
-
|
|
4586
|
-
const payload =
|
|
4587
|
-
if (!
|
|
4833
|
+
const responseBody = await response.text();
|
|
4834
|
+
const payload = this.parsePayload(responseBody);
|
|
4835
|
+
if (!response.ok) {
|
|
4836
|
+
const detail = [payload && !payload.ok ? payload.error?.code : void 0, payload && !payload.ok ? payload.error?.message : void 0].filter(Boolean).join(": ") || responseBody.trim().slice(0, 240);
|
|
4837
|
+
throw new Error(`api request failed with status ${response.status}${detail ? `: ${detail}` : ""}`);
|
|
4838
|
+
}
|
|
4839
|
+
if (!payload) throw new Error("api request returned invalid JSON");
|
|
4840
|
+
if (!payload.ok) throw new Error([payload.error?.code, payload.error?.message].filter(Boolean).join(": ") || "api request failed");
|
|
4588
4841
|
return payload.data;
|
|
4589
4842
|
};
|
|
4843
|
+
parsePayload = (body) => {
|
|
4844
|
+
try {
|
|
4845
|
+
return JSON.parse(body);
|
|
4846
|
+
} catch {
|
|
4847
|
+
return null;
|
|
4848
|
+
}
|
|
4849
|
+
};
|
|
4590
4850
|
getCookie = async () => {
|
|
4591
4851
|
if (this.cookie !== void 0) return this.cookie;
|
|
4592
4852
|
const response = await fetch(`${this.apiBase}/api/auth/bridge`, {
|
|
@@ -4782,12 +5042,15 @@ var AppPublishingService = class {
|
|
|
4782
5042
|
this.bundleService = bundleService;
|
|
4783
5043
|
this.platformTargetService = platformTargetService;
|
|
4784
5044
|
}
|
|
4785
|
-
pack = async (params) =>
|
|
4786
|
-
appDirectory
|
|
4787
|
-
|
|
4788
|
-
|
|
4789
|
-
|
|
4790
|
-
|
|
5045
|
+
pack = async (params) => {
|
|
5046
|
+
const { appDirectory, outputPath, target } = params;
|
|
5047
|
+
return this.bundleService.packAppDirectory({
|
|
5048
|
+
appDirectory,
|
|
5049
|
+
outputPath,
|
|
5050
|
+
mode: "bundle",
|
|
5051
|
+
target: target ? this.platformTargetService.parseTargetKey(target) : void 0
|
|
5052
|
+
});
|
|
5053
|
+
};
|
|
4791
5054
|
validate = async (params) => {
|
|
4792
5055
|
const result = await this.validationService.validate({
|
|
4793
5056
|
appDirectory: params.appDirectory,
|
|
@@ -4846,28 +5109,37 @@ var AppPublishingService = class {
|
|
|
4846
5109
|
//#endregion
|
|
4847
5110
|
//#region src/cli/app/controllers/app-pack-command.controller.ts
|
|
4848
5111
|
var AppPackCommandController = class {
|
|
4849
|
-
constructor(appPublishingService = new AppPublishingService()) {
|
|
5112
|
+
constructor(appPublishingService = new AppPublishingService(), appCheckService) {
|
|
4850
5113
|
this.appPublishingService = appPublishingService;
|
|
5114
|
+
this.appCheckService = appCheckService;
|
|
4851
5115
|
}
|
|
4852
5116
|
pack = async (target, options) => {
|
|
5117
|
+
const { json, out, target: platformTarget } = options;
|
|
4853
5118
|
try {
|
|
5119
|
+
if (this.appCheckService) {
|
|
5120
|
+
const check = await this.appCheckService.check(target);
|
|
5121
|
+
if (!check.ok) {
|
|
5122
|
+
const details = check.issues.filter((issue) => issue.severity === "error").map((issue) => `[${issue.code}] ${issue.message}`).join("; ");
|
|
5123
|
+
throw new Error(`App check failed before pack: ${details}`);
|
|
5124
|
+
}
|
|
5125
|
+
}
|
|
4854
5126
|
const bundle = await this.appPublishingService.pack({
|
|
4855
5127
|
appDirectory: target,
|
|
4856
|
-
outputPath:
|
|
4857
|
-
target:
|
|
5128
|
+
outputPath: out,
|
|
5129
|
+
target: platformTarget
|
|
4858
5130
|
});
|
|
4859
|
-
process.stdout.write(
|
|
5131
|
+
process.stdout.write(json ? `${JSON.stringify({
|
|
4860
5132
|
ok: true,
|
|
4861
5133
|
bundle
|
|
4862
5134
|
}, null, 2)}\n` : [
|
|
4863
5135
|
`Packed ${bundle.metadata.name} ${bundle.metadata.version}.`,
|
|
4864
|
-
bundle.metadata.target ? `Target: ${
|
|
5136
|
+
bundle.metadata.target ? `Target: ${platformTarget}` : "Target: universal",
|
|
4865
5137
|
`Bundle: ${bundle.bundlePath}`,
|
|
4866
5138
|
""
|
|
4867
5139
|
].join("\n"));
|
|
4868
5140
|
} catch (error) {
|
|
4869
5141
|
const message = error instanceof Error ? error.message : String(error);
|
|
4870
|
-
process.stdout.write(
|
|
5142
|
+
process.stdout.write(json ? `${JSON.stringify({
|
|
4871
5143
|
ok: false,
|
|
4872
5144
|
error: { message }
|
|
4873
5145
|
}, null, 2)}\n` : `Mini App pack failed: ${message}\n`);
|
|
@@ -4967,6 +5239,113 @@ var AppValidatePublishCommandController = class {
|
|
|
4967
5239
|
};
|
|
4968
5240
|
};
|
|
4969
5241
|
//#endregion
|
|
5242
|
+
//#region src/cli/app/services/development/app-test.service.ts
|
|
5243
|
+
var AppTestService = class {
|
|
5244
|
+
constructor(appCheckService = new AppCheckService(), serviceAppDevService = new ServiceAppDevService()) {
|
|
5245
|
+
this.appCheckService = appCheckService;
|
|
5246
|
+
this.serviceAppDevService = serviceAppDevService;
|
|
5247
|
+
}
|
|
5248
|
+
test = async (target) => {
|
|
5249
|
+
const appPath = path.resolve(target);
|
|
5250
|
+
const check = await this.appCheckService.check(appPath);
|
|
5251
|
+
if (!check.ok) return {
|
|
5252
|
+
ok: false,
|
|
5253
|
+
target: appPath,
|
|
5254
|
+
steps: [],
|
|
5255
|
+
issues: check.issues.filter((issue) => issue.severity === "error").map(({ code, message }) => ({
|
|
5256
|
+
code,
|
|
5257
|
+
message
|
|
5258
|
+
}))
|
|
5259
|
+
};
|
|
5260
|
+
const bundle = await new AppManifestService().load(appPath);
|
|
5261
|
+
if (!isAppComponentManifestBundle(bundle)) return {
|
|
5262
|
+
ok: false,
|
|
5263
|
+
target: appPath,
|
|
5264
|
+
steps: [],
|
|
5265
|
+
issues: [{
|
|
5266
|
+
code: "app.test.schemaUnsupported",
|
|
5267
|
+
message: "app test requires a schema v2 package."
|
|
5268
|
+
}]
|
|
5269
|
+
};
|
|
5270
|
+
const fixture = await this.loadFixture(appPath);
|
|
5271
|
+
if (!bundle.components.find((component) => component.kind === "service" && component.id === fixture.component)) return {
|
|
5272
|
+
ok: false,
|
|
5273
|
+
target: appPath,
|
|
5274
|
+
component: fixture.component,
|
|
5275
|
+
steps: [],
|
|
5276
|
+
issues: [{
|
|
5277
|
+
code: "app.test.componentMissing",
|
|
5278
|
+
message: `Smoke fixture references an unknown Service Component: ${fixture.component}.`
|
|
5279
|
+
}]
|
|
5280
|
+
};
|
|
5281
|
+
if (fixture.resetData) {
|
|
5282
|
+
const reset = await this.serviceAppDevService.inspect(appPath, {
|
|
5283
|
+
componentId: fixture.component,
|
|
5284
|
+
resetData: true,
|
|
5285
|
+
confirmAppId: fixture.component
|
|
5286
|
+
});
|
|
5287
|
+
if (!reset.ok) return {
|
|
5288
|
+
ok: false,
|
|
5289
|
+
target: appPath,
|
|
5290
|
+
component: fixture.component,
|
|
5291
|
+
steps: [],
|
|
5292
|
+
issues: reset.issues.map(({ code, message }) => ({
|
|
5293
|
+
code,
|
|
5294
|
+
message
|
|
5295
|
+
}))
|
|
5296
|
+
};
|
|
5297
|
+
}
|
|
5298
|
+
const steps = [];
|
|
5299
|
+
for (const step of fixture.steps) {
|
|
5300
|
+
const report = await this.serviceAppDevService.call(appPath, step.action, step.input ?? {}, { componentId: fixture.component });
|
|
5301
|
+
const matches = report.ok && this.matchesSubset(report.result, step.expect ?? {});
|
|
5302
|
+
steps.push({
|
|
5303
|
+
action: step.action,
|
|
5304
|
+
ok: matches,
|
|
5305
|
+
...report.result !== void 0 ? { result: report.result } : {},
|
|
5306
|
+
...!matches ? { message: report.ok ? `Result does not match expected subset: ${JSON.stringify(step.expect ?? {})}` : report.issues.map((issue) => `[${issue.code}] ${issue.message}`).join("; ") } : {}
|
|
5307
|
+
});
|
|
5308
|
+
if (!matches) break;
|
|
5309
|
+
}
|
|
5310
|
+
return {
|
|
5311
|
+
ok: steps.length === fixture.steps.length && steps.every((step) => step.ok),
|
|
5312
|
+
target: appPath,
|
|
5313
|
+
component: fixture.component,
|
|
5314
|
+
steps,
|
|
5315
|
+
issues: []
|
|
5316
|
+
};
|
|
5317
|
+
};
|
|
5318
|
+
loadFixture = async (appPath) => {
|
|
5319
|
+
const fixturePath = path.join(appPath, "tests", "service-smoke.json");
|
|
5320
|
+
const parsed = JSON.parse(await readFile(fixturePath, "utf8"));
|
|
5321
|
+
if (parsed.schemaVersion !== 1 || !parsed.component?.trim() || !Array.isArray(parsed.steps)) throw new Error(`Invalid App smoke fixture: ${fixturePath}.`);
|
|
5322
|
+
return parsed;
|
|
5323
|
+
};
|
|
5324
|
+
matchesSubset = (actual, expected) => {
|
|
5325
|
+
if (!actual || typeof actual !== "object" || Array.isArray(actual)) return false;
|
|
5326
|
+
return Object.entries(expected).every(([key, value]) => JSON.stringify(actual[key]) === JSON.stringify(value));
|
|
5327
|
+
};
|
|
5328
|
+
};
|
|
5329
|
+
//#endregion
|
|
5330
|
+
//#region src/cli/app/controllers/development/app-test-command.controller.ts
|
|
5331
|
+
var AppTestCommandController = class {
|
|
5332
|
+
appTestService;
|
|
5333
|
+
constructor(serviceAppDevService = new ServiceAppDevService()) {
|
|
5334
|
+
this.appTestService = new AppTestService(new AppCheckService(void 0, void 0, serviceAppDevService), serviceAppDevService);
|
|
5335
|
+
}
|
|
5336
|
+
test = async (target, options) => {
|
|
5337
|
+
const report = await this.appTestService.test(target);
|
|
5338
|
+
process.stdout.write(options.json ? `${JSON.stringify(report, null, 2)}\n` : this.format(report));
|
|
5339
|
+
if (!report.ok) process.exitCode = 1;
|
|
5340
|
+
};
|
|
5341
|
+
format = (report) => [
|
|
5342
|
+
`NextClaw App test ${report.ok ? "passed" : "failed"}: ${report.target}`,
|
|
5343
|
+
...report.steps.map((step) => `${step.ok ? "ok" : "failed"} ${step.action}${step.message ? `: ${step.message}` : ""}`),
|
|
5344
|
+
...report.issues.map((issue) => `failed [${issue.code}] ${issue.message}`),
|
|
5345
|
+
""
|
|
5346
|
+
].join("\n");
|
|
5347
|
+
};
|
|
5348
|
+
//#endregion
|
|
4970
5349
|
//#region src/cli/app/services/app-packages/app-marketplace-query.service.ts
|
|
4971
5350
|
const DEFAULT_APPS_MARKETPLACE_API_BASE = "https://apps-registry.nextclaw.io";
|
|
4972
5351
|
var AppMarketplaceQueryService = class {
|
|
@@ -5021,11 +5400,27 @@ var AppPackageLiveService = class {
|
|
|
5021
5400
|
list = async () => await this.requireApiClient().request({ path: "/api/app-packages" });
|
|
5022
5401
|
info = async (appId) => await this.requireApiClient().request({ path: `/api/app-packages/${encodeURIComponent(this.requireId(appId))}` });
|
|
5023
5402
|
listOperations = async () => await this.requireApiClient().request({ path: "/api/app-package-operations" });
|
|
5403
|
+
inspectDependencies = async (appId) => await this.requireApiClient().request({ path: `/api/app-packages/${encodeURIComponent(this.requireId(appId))}/dependencies` });
|
|
5404
|
+
verifyDependencies = async (appId) => await this.requireApiClient().request({ path: `/api/app-packages/${encodeURIComponent(this.requireId(appId))}/dependencies/verify` });
|
|
5405
|
+
setupDependencies = async (appId) => await this.requireApiClient().request({
|
|
5406
|
+
path: `/api/app-packages/${encodeURIComponent(this.requireId(appId))}/dependencies/setup`,
|
|
5407
|
+
method: "POST"
|
|
5408
|
+
});
|
|
5409
|
+
bindDependency = async (appId, input) => await this.requireApiClient().request({
|
|
5410
|
+
path: `/api/app-packages/${encodeURIComponent(this.requireId(appId))}/dependencies/bind`,
|
|
5411
|
+
method: "POST",
|
|
5412
|
+
body: input
|
|
5413
|
+
});
|
|
5414
|
+
unbindDependency = async (appId, input) => await this.requireApiClient().request({
|
|
5415
|
+
path: `/api/app-packages/${encodeURIComponent(this.requireId(appId))}/dependencies/unbind`,
|
|
5416
|
+
method: "POST",
|
|
5417
|
+
body: input
|
|
5418
|
+
});
|
|
5024
5419
|
install = async (source, registryUrl) => await this.requireApiClient().request({
|
|
5025
5420
|
path: "/api/app-package-operations/install",
|
|
5026
5421
|
method: "POST",
|
|
5027
5422
|
body: {
|
|
5028
|
-
source: this.
|
|
5423
|
+
source: this.normalizeInstallSource(source),
|
|
5029
5424
|
registryUrl
|
|
5030
5425
|
}
|
|
5031
5426
|
});
|
|
@@ -5057,6 +5452,11 @@ var AppPackageLiveService = class {
|
|
|
5057
5452
|
};
|
|
5058
5453
|
requireId = (value) => this.requireValue(value, "App id");
|
|
5059
5454
|
requireSource = (value) => this.requireValue(value, "App install source");
|
|
5455
|
+
normalizeInstallSource = (value) => {
|
|
5456
|
+
const source = this.requireSource(value);
|
|
5457
|
+
return this.looksLikeLocalPath(source) ? path.resolve(source) : source;
|
|
5458
|
+
};
|
|
5459
|
+
looksLikeLocalPath = (value) => value.startsWith(".") || path.isAbsolute(value) || value.includes(path.sep) || value.endsWith(".napp");
|
|
5060
5460
|
requireVersion = (value) => this.requireValue(value, "App version");
|
|
5061
5461
|
requireValue = (value, name) => {
|
|
5062
5462
|
const normalized = value.trim();
|
|
@@ -5084,6 +5484,23 @@ var AppPackageCommandController = class {
|
|
|
5084
5484
|
marketplaceInfo = async (selector, options) => this.write(await this.marketplaceService.info(selector), options, this.formatMarketplaceInfo);
|
|
5085
5485
|
list = async (options) => this.write(await this.liveService.list(), options, this.formatList);
|
|
5086
5486
|
info = async (appId, options) => this.write(await this.liveService.info(appId), options, this.formatApp);
|
|
5487
|
+
inspectDependencies = async (appId, options) => this.write(await this.liveService.inspectDependencies(appId), options, this.formatDependencyView);
|
|
5488
|
+
verifyDependencies = async (appId, options) => this.write(await this.liveService.verifyDependencies(appId), options, this.formatDependencyView);
|
|
5489
|
+
setupDependencies = async (appId, options) => this.write(await this.liveService.setupDependencies(appId), options, this.formatDependencyView);
|
|
5490
|
+
bindDependency = async (appId, options) => {
|
|
5491
|
+
const { componentId, requirementKind, requirementId, providerId } = options;
|
|
5492
|
+
this.write(await this.liveService.bindDependency(appId, {
|
|
5493
|
+
componentId,
|
|
5494
|
+
requirementKind,
|
|
5495
|
+
requirementId,
|
|
5496
|
+
providerId
|
|
5497
|
+
}), options, this.formatDependencyView);
|
|
5498
|
+
};
|
|
5499
|
+
unbindDependency = async (appId, options) => this.write(await this.liveService.unbindDependency(appId, {
|
|
5500
|
+
componentId: options.componentId,
|
|
5501
|
+
requirementKind: options.requirementKind,
|
|
5502
|
+
requirementId: options.requirementId
|
|
5503
|
+
}), options, this.formatDependencyView);
|
|
5087
5504
|
operations = async (options) => this.write(await this.liveService.listOperations(), options, this.formatOperations);
|
|
5088
5505
|
install = async (source, options) => this.write(await this.liveService.install(source, options.registry), options, this.formatOperation);
|
|
5089
5506
|
enable = async (appId, options) => this.write(await this.liveService.enable(appId), options, this.formatApp);
|
|
@@ -5121,6 +5538,8 @@ var AppPackageCommandController = class {
|
|
|
5121
5538
|
`${app.name} (${app.id})`,
|
|
5122
5539
|
` version: ${app.activeVersion}`,
|
|
5123
5540
|
` enabled: ${app.enabled ? "yes" : "no"}`,
|
|
5541
|
+
` readiness: ${app.readiness.status}`,
|
|
5542
|
+
...app.readiness.requirements.map((requirement) => ` requirement: ${requirement.kind} ${requirement.title} (${requirement.id})`),
|
|
5124
5543
|
` built-in: ${app.builtIn ? "yes" : "no"}`,
|
|
5125
5544
|
` versions: ${app.installedVersions.join(", ")}`
|
|
5126
5545
|
].join("\n") + "\n";
|
|
@@ -5128,6 +5547,11 @@ var AppPackageCommandController = class {
|
|
|
5128
5547
|
if (result.entries.length === 0) return "App operations: none\n";
|
|
5129
5548
|
return `${result.entries.map((operation) => this.formatOperation(operation).trim()).join("\n\n")}\n`;
|
|
5130
5549
|
};
|
|
5550
|
+
formatDependencyView = (result) => [
|
|
5551
|
+
`Dependency readiness: ${result.readiness.status}`,
|
|
5552
|
+
...result.readiness.requirements.map((requirement) => ` requirement: ${requirement.componentId}/${requirement.kind}/${requirement.id}`),
|
|
5553
|
+
...result.bindings.map((binding) => ` binding: ${binding.componentId}/${binding.requirementKind}/${binding.requirementId} -> ${binding.providerId}`)
|
|
5554
|
+
].join("\n") + "\n";
|
|
5131
5555
|
formatOperation = (operation) => [
|
|
5132
5556
|
`App operation ${operation.id}`,
|
|
5133
5557
|
` action: ${operation.action}`,
|
|
@@ -5143,34 +5567,52 @@ var AppPackageCommandController = class {
|
|
|
5143
5567
|
//#region src/cli/app/register-app-commands.ts
|
|
5144
5568
|
function registerAppCommands(program, options = {}) {
|
|
5145
5569
|
const app = program.command("app").description("Develop, validate, and publish NextClaw apps");
|
|
5146
|
-
const appCheck = new AppCheckCommandController();
|
|
5147
5570
|
const serviceAppDev = new ServiceAppDevService({ portableServiceRunnerPath: options.portableServiceRunnerPath });
|
|
5571
|
+
const appCheckService = new AppCheckService(void 0, void 0, serviceAppDev);
|
|
5572
|
+
const appCheck = new AppCheckCommandController(appCheckService);
|
|
5573
|
+
const appBuild = new AppBuildCommandController();
|
|
5574
|
+
const appCreate = new AppCreateCommandController();
|
|
5575
|
+
const appDoctor = new AppDoctorCommandController();
|
|
5148
5576
|
const appDev = new AppDevCommandController(serviceAppDev);
|
|
5149
5577
|
const appData = new AppDataCommandController();
|
|
5150
5578
|
const appCall = new AppCallCommandController(serviceAppDev);
|
|
5151
5579
|
const appRestart = new AppRestartCommandController();
|
|
5152
|
-
const appPack = new AppPackCommandController();
|
|
5580
|
+
const appPack = new AppPackCommandController(void 0, appCheckService);
|
|
5153
5581
|
const appValidatePublish = new AppValidatePublishCommandController();
|
|
5154
5582
|
const appPublish = new AppPublishCommandController();
|
|
5155
5583
|
const appPackages = new AppPackageCommandController();
|
|
5156
|
-
|
|
5584
|
+
registerAppGuestDevelopmentCommands(app, {
|
|
5585
|
+
appBuild,
|
|
5586
|
+
appCreate,
|
|
5587
|
+
appDoctor,
|
|
5588
|
+
appTest: new AppTestCommandController(serviceAppDev)
|
|
5589
|
+
});
|
|
5590
|
+
app.command("pack <app-dir>").description("Pack a universal or declared platform NextClaw App artifact").option("--target <target-key>", "Pack a declared platform target").requiredOption("--out <path>", "Write the .napp artifact to a specific path").option("--json", "Output JSON", false).action(async (target, opts) => appPack.pack(target, opts));
|
|
5157
5591
|
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("--artifacts <dir>", "Use target-keyed .napp artifacts from a directory").option("--json", "Output JSON", false).action(async (target, opts) => appValidatePublish.validate(target, opts));
|
|
5158
5592
|
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("--artifacts <dir>", "Publish target-keyed .napp artifacts from a directory").option("--allow-warnings", "Submit after reviewing validation warnings", false).option("--json", "Output JSON", false).action(async (target, opts) => appPublish.publish(target, opts));
|
|
5159
|
-
app.command("check <app-dir>").description("Check a Panel App or Service App
|
|
5160
|
-
app.command("dev <
|
|
5593
|
+
app.command("check <app-dir>").description("Check a schema v2 App package, Panel App, or Service App").option("--json", "Output JSON", false).action(async (target, opts) => appCheck.check(target, opts));
|
|
5594
|
+
app.command("dev <app-dir>").description("Start a Service App or schema v2 package and inspect its actions").option("--component <service-id>", "Select a Service Component from a package").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));
|
|
5161
5595
|
const data = app.command("data").description("Inspect and manage NextClaw App data");
|
|
5162
5596
|
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));
|
|
5163
5597
|
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));
|
|
5164
|
-
app.command("call <
|
|
5598
|
+
app.command("call <app-dir> <action-name>").description("Call a Service App or schema v2 package action through the real runtime").option("--component <service-id>", "Select a Service Component from a package").option("--input <json>", "JSON object input for the action").option("--json", "Output JSON", false).action(async (target, actionName, opts) => appCall.call(target, actionName, opts));
|
|
5165
5599
|
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));
|
|
5166
5600
|
registerAppPackageCommands(app, appPackages);
|
|
5167
5601
|
}
|
|
5602
|
+
function registerAppGuestDevelopmentCommands(app, controllers) {
|
|
5603
|
+
const { appBuild, appCreate, appDoctor, appTest } = controllers;
|
|
5604
|
+
app.command("create <app-dir>").description("Create a NextClaw App from a runnable template").option("--template <name>", "Template: rust-wasi, starter, ts-http, or ts-http-lite", "rust-wasi").option("--json", "Output JSON", false).action(async (target, opts) => appCreate.create(target, opts));
|
|
5605
|
+
app.command("doctor").description("Check the App Guest development toolchain").option("--profile <profile>", "Toolchain profile: wasi, wasi-http, or all", "wasi").option("--json", "Output JSON", false).action(async (opts) => appDoctor.doctor(opts));
|
|
5606
|
+
app.command("build <app-dir>").description("Build a NextClaw App Guest into its declared runtime artifact").option("--json", "Output JSON", false).action(async (target, opts) => appBuild.build(target, opts));
|
|
5607
|
+
app.command("test <app-dir>").description("Run a schema v2 App smoke fixture through the real runtime").option("--json", "Output JSON", false).action(async (target, opts) => appTest.test(target, opts));
|
|
5608
|
+
}
|
|
5168
5609
|
function registerAppPackageCommands(app, appPackages) {
|
|
5169
5610
|
const marketplace = app.command("marketplace").description("Browse NextClaw App Marketplace");
|
|
5170
5611
|
marketplace.command("search").description("Search Apps in NextClaw App Marketplace").option("-q, --query <text>", "Search text").option("--tag <tag>", "Filter by tag").option("--cursor <cursor>", "Continue from a marketplace cursor").option("--limit <n>", "Maximum number of items").option("--json", "Output JSON", false).action(async (opts) => appPackages.searchMarketplace(opts));
|
|
5171
5612
|
marketplace.command("info <selector>").description("Show an App Marketplace item").option("--json", "Output JSON", false).action(async (selector, opts) => appPackages.marketplaceInfo(selector, opts));
|
|
5172
5613
|
app.command("list").description("List Apps installed in the running NextClaw host").option("--json", "Output JSON", false).action(async (opts) => appPackages.list(opts));
|
|
5173
5614
|
app.command("info <app-id>").description("Show an App installed in the running NextClaw host").option("--json", "Output JSON", false).action(async (appId, opts) => appPackages.info(appId, opts));
|
|
5615
|
+
registerAppDependencyCommands(app, appPackages);
|
|
5174
5616
|
app.command("operations").description("List App install, update, rollback, and uninstall operations").option("--json", "Output JSON", false).action(async (opts) => appPackages.operations(opts));
|
|
5175
5617
|
app.command("install <source>").description("Install an App through the running NextClaw host").option("--registry <url>", "Registry URL for an App id").option("--json", "Output JSON", false).action(async (source, opts) => appPackages.install(source, opts));
|
|
5176
5618
|
app.command("enable <app-id>").description("Enable an installed App").option("--json", "Output JSON", false).action(async (appId, opts) => appPackages.enable(appId, opts));
|
|
@@ -5179,6 +5621,25 @@ function registerAppPackageCommands(app, appPackages) {
|
|
|
5179
5621
|
app.command("rollback <app-id>").description("Roll back an App to an installed version").requiredOption("--version <version>", "Installed version to activate").option("--json", "Output JSON", false).action(async (appId, opts) => appPackages.rollback(appId, opts));
|
|
5180
5622
|
app.command("uninstall <app-id>").description("Uninstall an App through the running NextClaw host").option("--purge-data", "Permanently delete App data", false).option("--confirm <app-id>", "Confirm the exact App id when purging data").option("--json", "Output JSON", false).action(async (appId, opts) => appPackages.uninstall(appId, opts));
|
|
5181
5623
|
}
|
|
5624
|
+
function registerAppDependencyCommands(app, controller) {
|
|
5625
|
+
const dependencies = app.command("dependencies").description("Inspect and manage an App's capability and resource dependencies");
|
|
5626
|
+
dependencies.command("inspect <app-id>").description("Inspect dependency readiness and available providers").option("--json", "Output JSON", false).action(async (appId, opts) => controller.inspectDependencies(appId, opts));
|
|
5627
|
+
dependencies.command("verify <app-id>").description("Verify dependency readiness").option("--json", "Output JSON", false).action(async (appId, opts) => controller.verifyDependencies(appId, opts));
|
|
5628
|
+
dependencies.command("setup <app-id>").description("Bind dependencies when exactly one compatible provider is available").option("--json", "Output JSON", false).action(async (appId, opts) => controller.setupDependencies(appId, opts));
|
|
5629
|
+
dependencies.command("bind <app-id>").description("Bind one dependency to a trusted installed provider").requiredOption("--component <id>", "Service component id").requiredOption("--kind <kind>", "Requirement kind: capability or resource").requiredOption("--requirement <id>", "Declared requirement id").requiredOption("--provider <id>", "Installed provider id").option("--json", "Output JSON", false).action(async (appId, opts) => controller.bindDependency(appId, {
|
|
5630
|
+
componentId: opts.component,
|
|
5631
|
+
requirementKind: opts.kind,
|
|
5632
|
+
requirementId: opts.requirement,
|
|
5633
|
+
providerId: opts.provider,
|
|
5634
|
+
json: opts.json
|
|
5635
|
+
}));
|
|
5636
|
+
dependencies.command("unbind <app-id>").description("Remove one dependency binding").requiredOption("--component <id>", "Service component id").requiredOption("--kind <kind>", "Requirement kind: capability or resource").requiredOption("--requirement <id>", "Declared requirement id").option("--json", "Output JSON", false).action(async (appId, opts) => controller.unbindDependency(appId, {
|
|
5637
|
+
componentId: opts.component,
|
|
5638
|
+
requirementKind: opts.kind,
|
|
5639
|
+
requirementId: opts.requirement,
|
|
5640
|
+
json: opts.json
|
|
5641
|
+
}));
|
|
5642
|
+
}
|
|
5182
5643
|
//#endregion
|
|
5183
5644
|
//#region src/cli/app/register-project-commands.ts
|
|
5184
5645
|
function registerProjectCommands(program, nextclaw) {
|
|
@@ -5448,4 +5909,4 @@ program.command("usage").description("Show observed LLM usage snapshots, history
|
|
|
5448
5909
|
//#endregion
|
|
5449
5910
|
export { program as nextclawCliProgram };
|
|
5450
5911
|
|
|
5451
|
-
//# sourceMappingURL=nextclaw-cli-app-
|
|
5912
|
+
//# sourceMappingURL=nextclaw-cli-app-BVdP2e0w.js.map
|