nextclaw 0.45.5 → 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.
Files changed (106) hide show
  1. package/dist/cli/app/index.js +2 -2
  2. package/dist/cli/launcher/index.js +10 -4
  3. package/dist/cli/launcher/index.js.map +1 -1
  4. package/dist/distribution-BAU3XUQJ.js +2 -0
  5. package/dist/{nextclaw-distribution.utils-C6_UWes0.js → distribution-CND3AWXk.js} +1 -1
  6. package/dist/distribution-CND3AWXk.js.map +1 -0
  7. package/dist/{nextclaw-cli-app-Bwa2L2Jv.js → nextclaw-cli-app-BVdP2e0w.js} +535 -74
  8. package/dist/nextclaw-cli-app-BVdP2e0w.js.map +1 -0
  9. package/package.json +9 -9
  10. package/resources/USAGE.md +118 -113
  11. package/ui-dist/assets/{api-DU3C4HfX.js → api-aEu9cEI8.js} +1 -1
  12. package/ui-dist/assets/api-aEu9cEI8.js.gz +0 -0
  13. package/ui-dist/assets/{appearance-settings-page-CoB3e64T.js → appearance-settings-page-z4DaHc_E.js} +1 -1
  14. package/ui-dist/assets/appearance-settings-page-z4DaHc_E.js.gz +0 -0
  15. package/ui-dist/assets/{book-open-DMnwJfVM.js → book-open-DYRapW_h.js} +1 -1
  16. package/ui-dist/assets/{channels-list-page-BhIoYsyo.js → channels-list-page-x07sGqsT.js} +1 -1
  17. package/ui-dist/assets/channels-list-page-x07sGqsT.js.gz +0 -0
  18. package/ui-dist/assets/{config-split-page-DON62DcK.js → config-split-page-DJDp86Ne.js} +1 -1
  19. package/ui-dist/assets/config-split-page-DJDp86Ne.js.gz +0 -0
  20. package/ui-dist/assets/{confirm-dialog-B539oJpr.js → confirm-dialog-BALTnP33.js} +1 -1
  21. package/ui-dist/assets/confirm-dialog-BALTnP33.js.gz +0 -0
  22. package/ui-dist/assets/{createLucideIcon-CLfqNlvk.js → createLucideIcon-CgM0rnsa.js} +1 -1
  23. package/ui-dist/assets/{desktop-capabilities-page-BgGZ1I8q.js → desktop-capabilities-page-4-v3wKLV.js} +1 -1
  24. package/ui-dist/assets/desktop-capabilities-page-4-v3wKLV.js.gz +0 -0
  25. package/ui-dist/assets/{desktop-update-config-TrA-h5OG.js → desktop-update-config-Cl-_XHqU.js} +1 -1
  26. package/ui-dist/assets/desktop-update-config-Cl-_XHqU.js.gz +0 -0
  27. package/ui-dist/assets/{dist-GWWehc4w.js → dist-BjYvUhPL.js} +1 -1
  28. package/ui-dist/assets/dist-BjYvUhPL.js.gz +0 -0
  29. package/ui-dist/assets/{dist-WPei0-v8.js → dist-N9zc5bkM.js} +1 -1
  30. package/ui-dist/assets/dist-N9zc5bkM.js.gz +0 -0
  31. package/ui-dist/assets/{doc-browser-CKpps-rb.js → doc-browser-DwRLokCI.js} +1 -1
  32. package/ui-dist/assets/doc-browser-DwRLokCI.js.gz +0 -0
  33. package/ui-dist/assets/doc-browser-JlXVLNRe.js +1 -0
  34. package/ui-dist/assets/{doc-browser-context-Vx418ptC.js → doc-browser-context-BDuypObS.js} +1 -1
  35. package/ui-dist/assets/doc-browser-context-BDuypObS.js.gz +0 -0
  36. package/ui-dist/assets/{extensions-DQ3IQpiE.js → extensions-DFQXmSwG.js} +1 -1
  37. package/ui-dist/assets/extensions-DFQXmSwG.js.gz +0 -0
  38. package/ui-dist/assets/{form-actions-DJlfyLEH.js → form-actions-CzGmrPAv.js} +1 -1
  39. package/ui-dist/assets/{index-CsL49uK2.js → index-zuqzAvNx.js} +5 -5
  40. package/ui-dist/assets/index-zuqzAvNx.js.gz +0 -0
  41. package/ui-dist/assets/loader-circle-D9etMT9f.js +1 -0
  42. package/ui-dist/assets/{mcp-marketplace-page-CEr3FgVp.js → mcp-marketplace-page-COUopIOc.js} +1 -1
  43. package/ui-dist/assets/mcp-marketplace-page-COUopIOc.js.gz +0 -0
  44. package/ui-dist/assets/mcp-marketplace-page-DxPPmYqh.js +1 -0
  45. package/ui-dist/assets/{model-config-page-BIpU9Bq5.js → model-config-page-g9_-hZqZ.js} +1 -1
  46. package/ui-dist/assets/model-config-page-g9_-hZqZ.js.gz +0 -0
  47. package/ui-dist/assets/{navigation-link-DI0mOIVl.js → navigation-link-Ci4Z7Phi.js} +1 -1
  48. package/ui-dist/assets/navigation-link-Ci4Z7Phi.js.gz +0 -0
  49. package/ui-dist/assets/plus-DKGD04AO.js +1 -0
  50. package/ui-dist/assets/{privacy-settings-page-D9qnS5ZE.js → privacy-settings-page-pNhQQT4r.js} +1 -1
  51. package/ui-dist/assets/privacy-settings-page-pNhQQT4r.js.gz +0 -0
  52. package/ui-dist/assets/{providers-config-page-j03VhVjT.js → providers-config-page-DftW_6KU.js} +1 -1
  53. package/ui-dist/assets/providers-config-page-DftW_6KU.js.gz +0 -0
  54. package/ui-dist/assets/{react-BwGMzqXA.js → react-CVFx225w.js} +1 -1
  55. package/ui-dist/assets/{react-BwGMzqXA.js.gz → react-CVFx225w.js.gz} +0 -0
  56. package/ui-dist/assets/{refresh-cw-DmR6-f9A.js → refresh-cw-C-3_thgr.js} +1 -1
  57. package/ui-dist/assets/remote-DS2TkMPm.js +1 -0
  58. package/ui-dist/assets/{rotate-cw-BUWqEgob.js → rotate-cw-DSFTq_yu.js} +1 -1
  59. package/ui-dist/assets/{runtime-config-page-DcyDsmw-.js → runtime-config-page-Cd9sOYvn.js} +1 -1
  60. package/ui-dist/assets/runtime-config-page-Cd9sOYvn.js.gz +0 -0
  61. package/ui-dist/assets/{save-DdohrEEJ.js → save-CpPr78_S.js} +1 -1
  62. package/ui-dist/assets/{search-config-page-DBUVi0PY.js → search-config-page-BqoDbKpM.js} +1 -1
  63. package/ui-dist/assets/search-config-page-BqoDbKpM.js.gz +0 -0
  64. package/ui-dist/assets/{search-BVeIVVGT.js → search-msqk2-rd.js} +1 -1
  65. package/ui-dist/assets/{secrets-config-page-Bu58SQa1.js → secrets-config-page-GsahsEVw.js} +1 -1
  66. package/ui-dist/assets/secrets-config-page-GsahsEVw.js.gz +0 -0
  67. package/ui-dist/assets/security-config-BnHQyrtG.js +1 -0
  68. package/ui-dist/assets/{select-DvuHOwPH.js → select-DPKCWajZ.js} +1 -1
  69. package/ui-dist/assets/select-DPKCWajZ.js.gz +0 -0
  70. package/ui-dist/assets/skeleton-BsR5rwB7.js +1 -0
  71. package/ui-dist/assets/{tag-chip-BI9NffVf.js → tag-chip-ClBWeQ4p.js} +1 -1
  72. package/ui-dist/assets/x-Dp5GAFmP.js +1 -0
  73. package/ui-dist/index.html +20 -20
  74. package/ui-dist/index.html.gz +0 -0
  75. package/dist/nextclaw-cli-app-Bwa2L2Jv.js.map +0 -1
  76. package/dist/nextclaw-distribution.utils-C6_UWes0.js.map +0 -1
  77. package/ui-dist/assets/api-DU3C4HfX.js.gz +0 -0
  78. package/ui-dist/assets/appearance-settings-page-CoB3e64T.js.gz +0 -0
  79. package/ui-dist/assets/channels-list-page-BhIoYsyo.js.gz +0 -0
  80. package/ui-dist/assets/config-split-page-DON62DcK.js.gz +0 -0
  81. package/ui-dist/assets/confirm-dialog-B539oJpr.js.gz +0 -0
  82. package/ui-dist/assets/desktop-capabilities-page-BgGZ1I8q.js.gz +0 -0
  83. package/ui-dist/assets/desktop-update-config-TrA-h5OG.js.gz +0 -0
  84. package/ui-dist/assets/dist-GWWehc4w.js.gz +0 -0
  85. package/ui-dist/assets/dist-WPei0-v8.js.gz +0 -0
  86. package/ui-dist/assets/doc-browser-CKpps-rb.js.gz +0 -0
  87. package/ui-dist/assets/doc-browser-context-Vx418ptC.js.gz +0 -0
  88. package/ui-dist/assets/doc-browser-y3e2_MXe.js +0 -1
  89. package/ui-dist/assets/extensions-DQ3IQpiE.js.gz +0 -0
  90. package/ui-dist/assets/index-CsL49uK2.js.gz +0 -0
  91. package/ui-dist/assets/loader-circle-DXH2fVRq.js +0 -1
  92. package/ui-dist/assets/mcp-marketplace-page-CEr3FgVp.js.gz +0 -0
  93. package/ui-dist/assets/mcp-marketplace-page-Dj0xiFAV.js +0 -1
  94. package/ui-dist/assets/model-config-page-BIpU9Bq5.js.gz +0 -0
  95. package/ui-dist/assets/navigation-link-DI0mOIVl.js.gz +0 -0
  96. package/ui-dist/assets/plus-C6Kx4b6T.js +0 -1
  97. package/ui-dist/assets/privacy-settings-page-D9qnS5ZE.js.gz +0 -0
  98. package/ui-dist/assets/providers-config-page-j03VhVjT.js.gz +0 -0
  99. package/ui-dist/assets/remote-BPMM79JV.js +0 -1
  100. package/ui-dist/assets/runtime-config-page-DcyDsmw-.js.gz +0 -0
  101. package/ui-dist/assets/search-config-page-DBUVi0PY.js.gz +0 -0
  102. package/ui-dist/assets/secrets-config-page-Bu58SQa1.js.gz +0 -0
  103. package/ui-dist/assets/security-config-CNdun2s-.js +0 -1
  104. package/ui-dist/assets/select-DvuHOwPH.js.gz +0 -0
  105. package/ui-dist/assets/skeleton-u2lc3JJg.js +0 -1
  106. 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 storage = await this.createDevStorage(appPath, loaded.manifest.id, options.resetData === true);
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: "service.runtime.callFailed",
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 and run app dev on that service directory."
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
- buildCallReport = (target, app, actionId, result, issues) => ({
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 = this.parseInput(options.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(options.json));
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(options.json ? `${JSON.stringify(report, null, 2)}\n` : this.format(report));
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
- resetData: options.resetData,
4540
- confirmAppId: options.confirm
4786
+ componentId: component,
4787
+ resetData,
4788
+ confirmAppId: confirm
4541
4789
  });
4542
- process.stdout.write(options.json ? `${JSON.stringify(report, null, 2)}\n` : this.format(report));
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
- if (!response.ok) throw new Error(`api request failed with status ${response.status}`);
4586
- const payload = await response.json();
4587
- if (!payload.ok) throw new Error(payload.error?.message ?? "api request failed");
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) => this.bundleService.packAppDirectory({
4786
- appDirectory: params.appDirectory,
4787
- outputPath: params.outputPath,
4788
- mode: "bundle",
4789
- target: this.platformTargetService.parseTargetKey(params.target)
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: options.out,
4857
- target: options.target
5128
+ outputPath: out,
5129
+ target: platformTarget
4858
5130
  });
4859
- process.stdout.write(options.json ? `${JSON.stringify({
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: ${options.target}` : "Target: universal",
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(options.json ? `${JSON.stringify({
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.requireSource(source),
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
- app.command("pack <app-dir>").description("Pack one declared platform target as a NextClaw App artifact").requiredOption("--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));
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 directory").option("--json", "Output JSON", false).action(async (target, opts) => appCheck.check(target, opts));
5160
- 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));
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 <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));
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-Bwa2L2Jv.js.map
5912
+ //# sourceMappingURL=nextclaw-cli-app-BVdP2e0w.js.map