@nextclaw/app-runtime 0.9.13 → 0.9.14

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 (306) hide show
  1. package/README.md +2 -2
  2. package/dist/{main.js → app/main.js} +2 -2
  3. package/dist/app/main.js.map +1 -0
  4. package/dist/{commands → controllers}/create.controller.d.ts +2 -2
  5. package/dist/controllers/create.controller.d.ts.map +1 -0
  6. package/dist/{commands → controllers}/create.controller.js +2 -2
  7. package/dist/controllers/create.controller.js.map +1 -0
  8. package/dist/{commands → controllers}/dev.controller.js +1 -1
  9. package/dist/controllers/dev.controller.js.map +1 -0
  10. package/dist/{commands → controllers}/grant.controller.d.ts +3 -3
  11. package/dist/controllers/grant.controller.d.ts.map +1 -0
  12. package/dist/{commands → controllers}/grant.controller.js +2 -2
  13. package/dist/controllers/grant.controller.js.map +1 -0
  14. package/dist/{commands → controllers}/info.controller.d.ts +2 -2
  15. package/dist/controllers/info.controller.d.ts.map +1 -0
  16. package/dist/{commands → controllers}/info.controller.js +2 -2
  17. package/dist/controllers/info.controller.js.map +1 -0
  18. package/dist/{commands → controllers}/inspect.controller.js +8 -2
  19. package/dist/controllers/inspect.controller.js.map +1 -0
  20. package/dist/{commands → controllers}/install.controller.d.ts +2 -2
  21. package/dist/controllers/install.controller.d.ts.map +1 -0
  22. package/dist/{commands → controllers}/install.controller.js +2 -2
  23. package/dist/controllers/install.controller.js.map +1 -0
  24. package/dist/{commands → controllers}/list.controller.d.ts +2 -2
  25. package/dist/controllers/list.controller.d.ts.map +1 -0
  26. package/dist/{commands → controllers}/list.controller.js +2 -2
  27. package/dist/controllers/list.controller.js.map +1 -0
  28. package/dist/controllers/pack.controller.d.ts +19 -0
  29. package/dist/controllers/pack.controller.d.ts.map +1 -0
  30. package/dist/{commands → controllers}/pack.controller.js +8 -4
  31. package/dist/controllers/pack.controller.js.map +1 -0
  32. package/dist/{commands → controllers}/permissions.controller.d.ts +2 -2
  33. package/dist/controllers/permissions.controller.d.ts.map +1 -0
  34. package/dist/{commands → controllers}/permissions.controller.js +2 -2
  35. package/dist/controllers/permissions.controller.js.map +1 -0
  36. package/dist/{commands → controllers}/publish.controller.d.ts +3 -3
  37. package/dist/controllers/publish.controller.d.ts.map +1 -0
  38. package/dist/{commands → controllers}/publish.controller.js +2 -2
  39. package/dist/controllers/publish.controller.js.map +1 -0
  40. package/dist/{commands → controllers}/registry.controller.d.ts +2 -2
  41. package/dist/controllers/registry.controller.d.ts.map +1 -0
  42. package/dist/{commands → controllers}/registry.controller.js +2 -2
  43. package/dist/controllers/registry.controller.js.map +1 -0
  44. package/dist/{commands → controllers}/revoke.controller.d.ts +2 -2
  45. package/dist/controllers/revoke.controller.d.ts.map +1 -0
  46. package/dist/{commands → controllers}/revoke.controller.js +2 -2
  47. package/dist/controllers/revoke.controller.js.map +1 -0
  48. package/dist/{commands → controllers}/run.controller.js +8 -6
  49. package/dist/controllers/run.controller.js.map +1 -0
  50. package/dist/{commands → controllers}/uninstall.controller.d.ts +2 -2
  51. package/dist/controllers/uninstall.controller.d.ts.map +1 -0
  52. package/dist/{commands → controllers}/uninstall.controller.js +2 -2
  53. package/dist/controllers/uninstall.controller.js.map +1 -0
  54. package/dist/{commands → controllers}/update.controller.d.ts +2 -2
  55. package/dist/controllers/update.controller.d.ts.map +1 -0
  56. package/dist/{commands → controllers}/update.controller.js +2 -2
  57. package/dist/controllers/update.controller.js.map +1 -0
  58. package/dist/{commands → controllers}/validate-publish.controller.d.ts +3 -3
  59. package/dist/controllers/validate-publish.controller.d.ts.map +1 -0
  60. package/dist/{commands → controllers}/validate-publish.controller.js +9 -5
  61. package/dist/controllers/validate-publish.controller.js.map +1 -0
  62. package/dist/index.d.ts +51 -51
  63. package/dist/index.js +46 -45
  64. package/dist/package.js +1 -1
  65. package/dist/{runtime → services}/app-build.service.d.ts +2 -2
  66. package/dist/services/app-build.service.d.ts.map +1 -0
  67. package/dist/{runtime → services}/app-build.service.js +4 -2
  68. package/dist/services/app-build.service.js.map +1 -0
  69. package/dist/{bundle → services}/app-bundle.service.d.ts +18 -6
  70. package/dist/services/app-bundle.service.d.ts.map +1 -0
  71. package/dist/services/app-bundle.service.js +305 -0
  72. package/dist/services/app-bundle.service.js.map +1 -0
  73. package/dist/{permissions → services}/app-grant.service.d.ts +4 -4
  74. package/dist/services/app-grant.service.d.ts.map +1 -0
  75. package/dist/{permissions → services}/app-grant.service.js +7 -4
  76. package/dist/services/app-grant.service.js.map +1 -0
  77. package/dist/{paths → services}/app-home.service.d.ts +1 -1
  78. package/dist/services/app-home.service.d.ts.map +1 -0
  79. package/dist/{paths → services}/app-home.service.js +1 -1
  80. package/dist/services/app-home.service.js.map +1 -0
  81. package/dist/{host → services}/app-host.service.d.ts +2 -2
  82. package/dist/services/app-host.service.d.ts.map +1 -0
  83. package/dist/{host → services}/app-host.service.js +3 -3
  84. package/dist/services/app-host.service.js.map +1 -0
  85. package/dist/services/app-install-source.service.js +72 -0
  86. package/dist/services/app-install-source.service.js.map +1 -0
  87. package/dist/{install → services}/app-installation.service.d.ts +15 -14
  88. package/dist/services/app-installation.service.d.ts.map +1 -0
  89. package/dist/services/app-installation.service.js +332 -0
  90. package/dist/services/app-installation.service.js.map +1 -0
  91. package/dist/services/app-instance.service.d.ts +34 -0
  92. package/dist/services/app-instance.service.d.ts.map +1 -0
  93. package/dist/{host → services}/app-instance.service.js +4 -4
  94. package/dist/services/app-instance.service.js.map +1 -0
  95. package/dist/{manifest → services}/app-manifest.service.d.ts +12 -2
  96. package/dist/services/app-manifest.service.d.ts.map +1 -0
  97. package/dist/services/app-manifest.service.js +271 -0
  98. package/dist/services/app-manifest.service.js.map +1 -0
  99. package/dist/{publish → services}/app-marketplace-client.service.d.ts +2 -2
  100. package/dist/services/app-marketplace-client.service.d.ts.map +1 -0
  101. package/dist/{publish → services}/app-marketplace-client.service.js +2 -2
  102. package/dist/services/app-marketplace-client.service.js.map +1 -0
  103. package/dist/{publish → services}/app-marketplace-metadata.service.d.ts +3 -3
  104. package/dist/services/app-marketplace-metadata.service.d.ts.map +1 -0
  105. package/dist/{publish → services}/app-marketplace-metadata.service.js +1 -1
  106. package/dist/services/app-marketplace-metadata.service.js.map +1 -0
  107. package/dist/{permissions → services}/app-permissions.service.d.ts +3 -3
  108. package/dist/services/app-permissions.service.d.ts.map +1 -0
  109. package/dist/{permissions → services}/app-permissions.service.js +4 -1
  110. package/dist/services/app-permissions.service.js.map +1 -0
  111. package/dist/{publish → services}/app-publish-validation.service.d.ts +9 -7
  112. package/dist/services/app-publish-validation.service.d.ts.map +1 -0
  113. package/dist/{publish → services}/app-publish-validation.service.js +14 -10
  114. package/dist/services/app-publish-validation.service.js.map +1 -0
  115. package/dist/{publish → services}/app-publish.service.d.ts +5 -5
  116. package/dist/services/app-publish.service.d.ts.map +1 -0
  117. package/dist/{publish → services}/app-publish.service.js +6 -6
  118. package/dist/services/app-publish.service.js.map +1 -0
  119. package/dist/{registry → services}/app-registry-config.service.d.ts +3 -3
  120. package/dist/{registry → services}/app-registry-config.service.d.ts.map +1 -1
  121. package/dist/{registry → services}/app-registry-config.service.js +3 -3
  122. package/dist/services/app-registry-config.service.js.map +1 -0
  123. package/dist/{registry → services}/app-registry.service.d.ts +18 -5
  124. package/dist/services/app-registry.service.d.ts.map +1 -0
  125. package/dist/services/app-registry.service.js +210 -0
  126. package/dist/services/app-registry.service.js.map +1 -0
  127. package/dist/{registry → services}/app-remote-registry-client.service.d.ts +6 -2
  128. package/dist/services/app-remote-registry-client.service.d.ts.map +1 -0
  129. package/dist/{registry → services}/app-remote-registry-client.service.js +54 -8
  130. package/dist/services/app-remote-registry-client.service.js.map +1 -0
  131. package/dist/{cli → services}/app-runtime-cli.service.d.ts +1 -1
  132. package/dist/services/app-runtime-cli.service.d.ts.map +1 -0
  133. package/dist/{cli → services}/app-runtime-cli.service.js +19 -19
  134. package/dist/services/app-runtime-cli.service.js.map +1 -0
  135. package/dist/{cli → services}/app-runtime-options.service.d.ts +6 -6
  136. package/dist/services/app-runtime-options.service.d.ts.map +1 -0
  137. package/dist/{cli → services}/app-runtime-options.service.js +3 -9
  138. package/dist/services/app-runtime-options.service.js.map +1 -0
  139. package/dist/{runtime → services}/app-runtime-toolchain.service.d.ts +1 -1
  140. package/dist/services/app-runtime-toolchain.service.d.ts.map +1 -0
  141. package/dist/{runtime → services}/app-runtime-toolchain.service.js +1 -1
  142. package/dist/services/app-runtime-toolchain.service.js.map +1 -0
  143. package/dist/{scaffold → services}/app-scaffold.service.d.ts +1 -1
  144. package/dist/{scaffold → services}/app-scaffold.service.d.ts.map +1 -1
  145. package/dist/{scaffold → services}/app-scaffold.service.js +1 -1
  146. package/dist/{scaffold → services}/app-scaffold.service.js.map +1 -1
  147. package/dist/{scaffold → services}/app-ts-http-lite-scaffold-template.service.d.ts +1 -1
  148. package/dist/{scaffold → services}/app-ts-http-lite-scaffold-template.service.d.ts.map +1 -1
  149. package/dist/{scaffold → services}/app-ts-http-lite-scaffold-template.service.js +1 -1
  150. package/dist/{scaffold → services}/app-ts-http-lite-scaffold-template.service.js.map +1 -1
  151. package/dist/{scaffold → services}/app-ts-http-scaffold-template.service.d.ts +1 -1
  152. package/dist/{scaffold → services}/app-ts-http-scaffold-template.service.d.ts.map +1 -1
  153. package/dist/{scaffold → services}/app-ts-http-scaffold-template.service.js +1 -1
  154. package/dist/{scaffold → services}/app-ts-http-scaffold-template.service.js.map +1 -1
  155. package/dist/{bridge → services}/host-bridge.service.d.ts +2 -2
  156. package/dist/services/host-bridge.service.d.ts.map +1 -0
  157. package/dist/{bridge → services}/host-bridge.service.js +1 -1
  158. package/dist/services/host-bridge.service.js.map +1 -0
  159. package/dist/services/main-runner.service.d.ts +9 -0
  160. package/dist/services/main-runner.service.d.ts.map +1 -0
  161. package/dist/services/main-runner.service.js +6 -0
  162. package/dist/services/main-runner.service.js.map +1 -0
  163. package/dist/{publish → services}/platform-auth-state.service.d.ts +1 -1
  164. package/dist/services/platform-auth-state.service.d.ts.map +1 -0
  165. package/dist/{publish → services}/platform-auth-state.service.js +1 -1
  166. package/dist/services/platform-auth-state.service.js.map +1 -0
  167. package/dist/{ui → services}/ui-server.service.d.ts +3 -3
  168. package/dist/services/ui-server.service.d.ts.map +1 -0
  169. package/dist/{ui → services}/ui-server.service.js +1 -1
  170. package/dist/services/ui-server.service.js.map +1 -0
  171. package/dist/{runtime → services}/wasm-main-runner.service.d.ts +3 -3
  172. package/dist/services/wasm-main-runner.service.d.ts.map +1 -0
  173. package/dist/{runtime → services}/wasm-main-runner.service.js +2 -2
  174. package/dist/services/wasm-main-runner.service.js.map +1 -0
  175. package/dist/{sidecar → services}/wasm-sidecar-client.service.d.ts +1 -1
  176. package/dist/services/wasm-sidecar-client.service.d.ts.map +1 -0
  177. package/dist/{sidecar → services}/wasm-sidecar-client.service.js +1 -1
  178. package/dist/services/wasm-sidecar-client.service.js.map +1 -0
  179. package/dist/{runtime → services}/wasmtime-wasi-http-component.service.d.ts +1 -1
  180. package/dist/services/wasmtime-wasi-http-component.service.d.ts.map +1 -0
  181. package/dist/{runtime → services}/wasmtime-wasi-http-component.service.js +1 -1
  182. package/dist/services/wasmtime-wasi-http-component.service.js.map +1 -0
  183. package/dist/{bundle → types}/app-bundle.types.d.ts +1 -1
  184. package/dist/types/app-bundle.types.d.ts.map +1 -0
  185. package/dist/{install → types}/app-installation.types.d.ts +27 -7
  186. package/dist/types/app-installation.types.d.ts.map +1 -0
  187. package/dist/types/app-manifest.types.d.ts +119 -0
  188. package/dist/types/app-manifest.types.d.ts.map +1 -0
  189. package/dist/types/app-manifest.types.js +11 -0
  190. package/dist/types/app-manifest.types.js.map +1 -0
  191. package/dist/{permissions → types}/app-permissions.types.d.ts +2 -2
  192. package/dist/types/app-permissions.types.d.ts.map +1 -0
  193. package/dist/{publish → types}/app-publish.types.d.ts +4 -4
  194. package/dist/types/app-publish.types.d.ts.map +1 -0
  195. package/dist/{publish → types}/app-publish.types.js +1 -1
  196. package/dist/types/app-publish.types.js.map +1 -0
  197. package/dist/{registry → types}/app-registry.types.d.ts +8 -4
  198. package/dist/types/app-registry.types.d.ts.map +1 -0
  199. package/dist/{registry → types}/app-remote-registry.types.d.ts +3 -3
  200. package/dist/types/app-remote-registry.types.d.ts.map +1 -0
  201. package/dist/{registry → types}/app-remote-registry.types.js +1 -1
  202. package/dist/types/app-remote-registry.types.js.map +1 -0
  203. package/dist/{runtime → types}/main-runner.types.d.ts +3 -3
  204. package/dist/types/main-runner.types.d.ts.map +1 -0
  205. package/package.json +10 -3
  206. package/dist/bridge/host-bridge.service.d.ts.map +0 -1
  207. package/dist/bridge/host-bridge.service.js.map +0 -1
  208. package/dist/bundle/app-bundle.service.d.ts.map +0 -1
  209. package/dist/bundle/app-bundle.service.js +0 -196
  210. package/dist/bundle/app-bundle.service.js.map +0 -1
  211. package/dist/bundle/app-bundle.types.d.ts.map +0 -1
  212. package/dist/cli/app-runtime-cli.service.d.ts.map +0 -1
  213. package/dist/cli/app-runtime-cli.service.js.map +0 -1
  214. package/dist/cli/app-runtime-options.service.d.ts.map +0 -1
  215. package/dist/cli/app-runtime-options.service.js.map +0 -1
  216. package/dist/commands/create.controller.d.ts.map +0 -1
  217. package/dist/commands/create.controller.js.map +0 -1
  218. package/dist/commands/dev.controller.js.map +0 -1
  219. package/dist/commands/grant.controller.d.ts.map +0 -1
  220. package/dist/commands/grant.controller.js.map +0 -1
  221. package/dist/commands/info.controller.d.ts.map +0 -1
  222. package/dist/commands/info.controller.js.map +0 -1
  223. package/dist/commands/inspect.controller.js.map +0 -1
  224. package/dist/commands/install.controller.d.ts.map +0 -1
  225. package/dist/commands/install.controller.js.map +0 -1
  226. package/dist/commands/list.controller.d.ts.map +0 -1
  227. package/dist/commands/list.controller.js.map +0 -1
  228. package/dist/commands/pack.controller.d.ts +0 -17
  229. package/dist/commands/pack.controller.d.ts.map +0 -1
  230. package/dist/commands/pack.controller.js.map +0 -1
  231. package/dist/commands/permissions.controller.d.ts.map +0 -1
  232. package/dist/commands/permissions.controller.js.map +0 -1
  233. package/dist/commands/publish.controller.d.ts.map +0 -1
  234. package/dist/commands/publish.controller.js.map +0 -1
  235. package/dist/commands/registry.controller.d.ts.map +0 -1
  236. package/dist/commands/registry.controller.js.map +0 -1
  237. package/dist/commands/revoke.controller.d.ts.map +0 -1
  238. package/dist/commands/revoke.controller.js.map +0 -1
  239. package/dist/commands/run.controller.js.map +0 -1
  240. package/dist/commands/uninstall.controller.d.ts.map +0 -1
  241. package/dist/commands/uninstall.controller.js.map +0 -1
  242. package/dist/commands/update.controller.d.ts.map +0 -1
  243. package/dist/commands/update.controller.js.map +0 -1
  244. package/dist/commands/validate-publish.controller.d.ts.map +0 -1
  245. package/dist/commands/validate-publish.controller.js.map +0 -1
  246. package/dist/host/app-host.service.d.ts.map +0 -1
  247. package/dist/host/app-host.service.js.map +0 -1
  248. package/dist/host/app-instance.service.d.ts +0 -34
  249. package/dist/host/app-instance.service.d.ts.map +0 -1
  250. package/dist/host/app-instance.service.js.map +0 -1
  251. package/dist/install/app-installation.service.d.ts.map +0 -1
  252. package/dist/install/app-installation.service.js +0 -276
  253. package/dist/install/app-installation.service.js.map +0 -1
  254. package/dist/install/app-installation.types.d.ts.map +0 -1
  255. package/dist/main.js.map +0 -1
  256. package/dist/manifest/app-manifest.service.d.ts.map +0 -1
  257. package/dist/manifest/app-manifest.service.js +0 -147
  258. package/dist/manifest/app-manifest.service.js.map +0 -1
  259. package/dist/manifest/app-manifest.types.d.ts +0 -68
  260. package/dist/manifest/app-manifest.types.d.ts.map +0 -1
  261. package/dist/paths/app-home.service.d.ts.map +0 -1
  262. package/dist/paths/app-home.service.js.map +0 -1
  263. package/dist/permissions/app-grant.service.d.ts.map +0 -1
  264. package/dist/permissions/app-grant.service.js.map +0 -1
  265. package/dist/permissions/app-permissions.service.d.ts.map +0 -1
  266. package/dist/permissions/app-permissions.service.js.map +0 -1
  267. package/dist/permissions/app-permissions.types.d.ts.map +0 -1
  268. package/dist/publish/app-marketplace-client.service.d.ts.map +0 -1
  269. package/dist/publish/app-marketplace-client.service.js.map +0 -1
  270. package/dist/publish/app-marketplace-metadata.service.d.ts.map +0 -1
  271. package/dist/publish/app-marketplace-metadata.service.js.map +0 -1
  272. package/dist/publish/app-publish-validation.service.d.ts.map +0 -1
  273. package/dist/publish/app-publish-validation.service.js.map +0 -1
  274. package/dist/publish/app-publish.service.d.ts.map +0 -1
  275. package/dist/publish/app-publish.service.js.map +0 -1
  276. package/dist/publish/app-publish.types.d.ts.map +0 -1
  277. package/dist/publish/app-publish.types.js.map +0 -1
  278. package/dist/publish/platform-auth-state.service.d.ts.map +0 -1
  279. package/dist/publish/platform-auth-state.service.js.map +0 -1
  280. package/dist/registry/app-registry-config.service.js.map +0 -1
  281. package/dist/registry/app-registry.service.d.ts.map +0 -1
  282. package/dist/registry/app-registry.service.js +0 -118
  283. package/dist/registry/app-registry.service.js.map +0 -1
  284. package/dist/registry/app-registry.types.d.ts.map +0 -1
  285. package/dist/registry/app-remote-registry-client.service.d.ts.map +0 -1
  286. package/dist/registry/app-remote-registry-client.service.js.map +0 -1
  287. package/dist/registry/app-remote-registry.types.d.ts.map +0 -1
  288. package/dist/registry/app-remote-registry.types.js.map +0 -1
  289. package/dist/runtime/app-build.service.d.ts.map +0 -1
  290. package/dist/runtime/app-build.service.js.map +0 -1
  291. package/dist/runtime/app-runtime-toolchain.service.d.ts.map +0 -1
  292. package/dist/runtime/app-runtime-toolchain.service.js.map +0 -1
  293. package/dist/runtime/main-runner.service.d.ts +0 -9
  294. package/dist/runtime/main-runner.service.d.ts.map +0 -1
  295. package/dist/runtime/main-runner.service.js +0 -6
  296. package/dist/runtime/main-runner.service.js.map +0 -1
  297. package/dist/runtime/main-runner.types.d.ts.map +0 -1
  298. package/dist/runtime/wasm-main-runner.service.d.ts.map +0 -1
  299. package/dist/runtime/wasm-main-runner.service.js.map +0 -1
  300. package/dist/runtime/wasmtime-wasi-http-component.service.d.ts.map +0 -1
  301. package/dist/runtime/wasmtime-wasi-http-component.service.js.map +0 -1
  302. package/dist/sidecar/wasm-sidecar-client.service.d.ts.map +0 -1
  303. package/dist/sidecar/wasm-sidecar-client.service.js.map +0 -1
  304. package/dist/ui/ui-server.service.d.ts.map +0 -1
  305. package/dist/ui/ui-server.service.js.map +0 -1
  306. /package/dist/{main.d.ts → app/main.d.ts} +0 -0
@@ -1,12 +1,12 @@
1
- import { AppManifestService } from "../manifest/app-manifest.service.js";
2
- import { AppBundleService } from "../bundle/app-bundle.service.js";
1
+ import { AppManifestService } from "./app-manifest.service.js";
2
+ import { AppBundleService } from "./app-bundle.service.js";
3
3
  import { AppMarketplaceClientService } from "./app-marketplace-client.service.js";
4
4
  import { AppMarketplaceMetadataService } from "./app-marketplace-metadata.service.js";
5
5
  import { PlatformAuthStateService } from "./platform-auth-state.service.js";
6
- import { readFile } from "node:fs/promises";
7
6
  import { createHash } from "node:crypto";
7
+ import { readFile } from "node:fs/promises";
8
8
  import path from "node:path";
9
- //#region src/publish/app-publish.service.ts
9
+ //#region src/services/app-publish.service.ts
10
10
  const DEFAULT_PLATFORM_API_BASE = "https://ai-gateway-api.nextclaw.io";
11
11
  var AppPublishService = class {
12
12
  constructor(manifestService = new AppManifestService(), bundleService = new AppBundleService(), metadataService = new AppMarketplaceMetadataService(), marketplaceClient = new AppMarketplaceClientService(), authStateService = new PlatformAuthStateService()) {
@@ -18,9 +18,9 @@ var AppPublishService = class {
18
18
  }
19
19
  publish = async (params) => {
20
20
  const { appDirectory: inputAppDirectory, metadataPath, apiBaseUrl, token } = params;
21
- const distributionMode = params.mode ?? "source";
22
21
  const appDirectory = path.resolve(inputAppDirectory);
23
22
  const manifestBundle = await this.manifestService.load(appDirectory);
23
+ const distributionMode = params.mode ?? (manifestBundle.manifest.schemaVersion === 2 ? "bundle" : "source");
24
24
  const metadata = await this.metadataService.load({
25
25
  appDirectory,
26
26
  manifest: manifestBundle.manifest,
@@ -58,7 +58,7 @@ var AppPublishService = class {
58
58
  publisher: actor.publisher,
59
59
  distributionMode,
60
60
  manifest: manifestBundle.manifest,
61
- permissions: manifestBundle.manifest.permissions ?? {},
61
+ permissions: manifestBundle.manifest.schemaVersion === 1 ? manifestBundle.manifest.permissions ?? {} : {},
62
62
  bundleBase64: bundleBytes.toString("base64"),
63
63
  bundleSha256,
64
64
  files: publishFiles.map((file) => ({
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-publish.service.js","names":[],"sources":["../../src/services/app-publish.service.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { AppBundleService } from \"#app-runtime/services/app-bundle.service.js\";\nimport type { AppDistributionMode } from \"#app-runtime/types/app-bundle.types.js\";\nimport { AppManifestService } from \"#app-runtime/services/app-manifest.service.js\";\nimport { AppMarketplaceClientService } from \"./app-marketplace-client.service.js\";\nimport { AppMarketplaceMetadataService } from \"./app-marketplace-metadata.service.js\";\nimport type { AppPublishPayload, AppPublishResult } from \"#app-runtime/types/app-publish.types.js\";\nimport { PlatformAuthStateService } from \"./platform-auth-state.service.js\";\n\nconst DEFAULT_PLATFORM_API_BASE = \"https://ai-gateway-api.nextclaw.io\";\n\ntype ResolvedPublishActor = {\n token: string;\n publisher: NonNullable<AppPublishPayload[\"publisher\"]>;\n};\n\nexport class AppPublishService {\n constructor(\n private readonly manifestService: AppManifestService = new AppManifestService(),\n private readonly bundleService: AppBundleService = new AppBundleService(),\n private readonly metadataService: AppMarketplaceMetadataService = new AppMarketplaceMetadataService(),\n private readonly marketplaceClient: AppMarketplaceClientService = new AppMarketplaceClientService(),\n private readonly authStateService: PlatformAuthStateService = new PlatformAuthStateService(),\n ) {}\n\n publish = async (params: {\n appDirectory: string;\n metadataPath?: string;\n apiBaseUrl?: string;\n token?: string;\n mode?: AppDistributionMode;\n }): Promise<AppPublishResult> => {\n const { appDirectory: inputAppDirectory, metadataPath, apiBaseUrl, token } = params;\n const appDirectory = path.resolve(inputAppDirectory);\n const manifestBundle = await this.manifestService.load(appDirectory);\n const distributionMode = params.mode ??\n (manifestBundle.manifest.schemaVersion === 2 ? \"bundle\" : \"source\");\n const metadata = await this.metadataService.load({\n appDirectory,\n manifest: manifestBundle.manifest,\n metadataPath,\n });\n const actor = await this.resolvePublishActor({\n apiBaseUrl,\n explicitToken: token,\n appId: manifestBundle.manifest.id,\n });\n const bundle = await this.bundleService.packAppDirectory({\n appDirectory,\n mode: distributionMode,\n });\n const bundleBytes = Buffer.from(await readFile(bundle.bundlePath));\n const bundleSha256 = createHash(\"sha256\").update(bundleBytes).digest(\"hex\");\n const publishFiles = await this.metadataService.collectPublishFiles({\n appDirectory,\n metadataPath,\n });\n const payload: AppPublishPayload = {\n slug: metadata.slug,\n appId: manifestBundle.manifest.id,\n name: manifestBundle.manifest.name,\n version: manifestBundle.manifest.version,\n summary: metadata.summary,\n summaryI18n: metadata.summaryI18n,\n description: metadata.description ?? manifestBundle.manifest.description,\n descriptionI18n: metadata.descriptionI18n,\n author: metadata.author,\n tags: metadata.tags,\n sourceRepo: metadata.sourceRepo,\n homepage: metadata.homepage,\n featured: metadata.featured ?? false,\n publisher: actor.publisher,\n distributionMode,\n manifest: manifestBundle.manifest,\n permissions: manifestBundle.manifest.schemaVersion === 1\n ? manifestBundle.manifest.permissions ?? {}\n : {},\n bundleBase64: bundleBytes.toString(\"base64\"),\n bundleSha256,\n files: publishFiles.map((file) => ({\n path: file.path,\n contentBase64: file.bytes.toString(\"base64\"),\n })),\n };\n const result = await this.marketplaceClient.publish({\n payload,\n apiBaseUrl,\n token: actor.token,\n });\n return {\n ...result,\n distribution: {\n path: bundle.bundlePath,\n sha256: bundleSha256,\n mode: distributionMode,\n },\n };\n };\n\n private resolvePublishActor = async (params: {\n apiBaseUrl?: string;\n explicitToken?: string;\n appId: string;\n }): Promise<ResolvedPublishActor> => {\n const explicitToken = params.explicitToken?.trim();\n const envAdminToken = process.env.NEXTCLAW_MARKETPLACE_ADMIN_TOKEN?.trim();\n if (explicitToken) {\n const token = explicitToken;\n if (!token) {\n throw new Error(\"缺少 publish token。\");\n }\n const me = await this.fetchCurrentPlatformUser({\n token,\n platformApiBase: this.resolvePlatformApiBase(),\n });\n return {\n token,\n publisher: this.buildUserPublisher(me, params.appId),\n };\n }\n\n const authState = this.authStateService.readCurrentAuthState();\n const platformToken = authState.token?.trim();\n if (platformToken) {\n const me = await this.fetchCurrentPlatformUser({\n token: platformToken,\n platformApiBase: this.resolvePlatformApiBase(authState.apiBaseUrl),\n });\n return {\n token: platformToken,\n publisher: this.buildUserPublisher(me, params.appId),\n };\n }\n\n if (envAdminToken) {\n return {\n token: envAdminToken,\n publisher: this.buildOfficialPublisher(),\n };\n }\n\n throw new Error(\"发布需要 NextClaw 平台登录态。请先运行 nextclaw login,或传入 --token。\");\n };\n\n private fetchCurrentPlatformUser = async (params: {\n token: string;\n platformApiBase: string;\n }): Promise<{\n id: string;\n username: string | null;\n role: \"admin\" | \"user\";\n }> => {\n const response = await fetch(`${params.platformApiBase}/platform/auth/me`, {\n headers: {\n authorization: `Bearer ${params.token}`,\n accept: \"application/json\",\n },\n });\n const payload = await response.json().catch(() => null);\n if (!response.ok) {\n const message =\n typeof payload === \"object\" &&\n payload &&\n \"error\" in payload &&\n typeof (payload as { error?: { message?: unknown } }).error?.message === \"string\"\n ? (payload as { error: { message: string } }).error.message\n : `${response.status} ${response.statusText}`;\n throw new Error(`读取 NextClaw 登录态失败:${message}`);\n }\n const user = typeof payload === \"object\" &&\n payload &&\n \"data\" in payload &&\n typeof (payload as { data?: { user?: unknown } }).data?.user === \"object\" &&\n (payload as { data: { user: Record<string, unknown> } }).data.user\n ? (payload as { data: { user: Record<string, unknown> } }).data.user\n : null;\n const id = typeof user?.id === \"string\" ? user.id.trim() : \"\";\n const username = typeof user?.username === \"string\" ? user.username.trim() : \"\";\n const role = user?.role === \"admin\" ? \"admin\" : \"user\";\n if (!id) {\n throw new Error(\"平台登录态缺少用户 id。\");\n }\n return {\n id,\n username: username || null,\n role,\n };\n };\n\n private resolvePlatformApiBase = (configuredApiBase?: string): string => {\n const source = configuredApiBase?.trim() || process.env.NEXTCLAW_PLATFORM_API_BASE?.trim() || DEFAULT_PLATFORM_API_BASE;\n const normalized = new URL(source);\n normalized.pathname = normalized.pathname.replace(/\\/v1\\/?$/, \"/\");\n return normalized.toString().replace(/\\/+$/, \"\");\n };\n\n private buildUserPublisher = (\n user: { id: string; username: string | null; role: \"admin\" | \"user\" },\n appId: string,\n ): NonNullable<AppPublishPayload[\"publisher\"]> => {\n const isOfficialScope = appId.startsWith(\"nextclaw.\");\n if (isOfficialScope && user.role === \"admin\") {\n return this.buildOfficialPublisher();\n }\n if (!user.username) {\n throw new Error(\"当前 NextClaw 账号还没有 username,无法发布个人 scope app。请先在平台账号页设置用户名。\");\n }\n return {\n id: user.username,\n name: user.username,\n url: `https://platform.nextclaw.io/account`,\n };\n };\n\n private buildOfficialPublisher = (): NonNullable<AppPublishPayload[\"publisher\"]> => {\n return {\n id: \"nextclaw\",\n name: \"NextClaw\",\n url: \"https://nextclaw.io\",\n };\n };\n}\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
- import { AppHomeService } from "../paths/app-home.service.js";
2
- import { AppRegistryConfigSnapshot } from "./app-remote-registry.types.js";
1
+ import { AppHomeService } from "./app-home.service.js";
2
+ import { AppRegistryConfigSnapshot } from "../types/app-remote-registry.types.js";
3
3
 
4
- //#region src/registry/app-registry-config.service.d.ts
4
+ //#region src/services/app-registry-config.service.d.ts
5
5
  declare class AppRegistryConfigService {
6
6
  private readonly appHomeService;
7
7
  constructor(appHomeService?: AppHomeService);
@@ -1 +1 @@
1
- {"version":3,"file":"app-registry-config.service.d.ts","names":[],"sources":["../../src/registry/app-registry-config.service.ts"],"mappings":";;;;cAQa,wBAAA;EAAA,iBAEQ,cAAA;cAAA,cAAA,GAAgB,cAAA;EAGnC,WAAA,QAAwB,OAAA,CAAQ,yBAAA;EAwBhC,cAAA,GAAwB,WAAA,aAAsB,OAAA,CAAQ,yBAAA;EAwBtD,gBAAA,QAA6B,OAAA,CAAQ,yBAAA;EAAA,QAS7B,kBAAA;EAAA,QAiBA,WAAA;EAAA,QAwBA,oBAAA;AAAA"}
1
+ {"version":3,"file":"app-registry-config.service.d.ts","names":[],"sources":["../../src/services/app-registry-config.service.ts"],"mappings":";;;;cAQa,wBAAA;EAAA,iBAEQ,cAAA;cAAA,cAAA,GAAgB,cAAA;EAGnC,WAAA,QAAwB,OAAA,CAAQ,yBAAA;EAwBhC,cAAA,GAAwB,WAAA,aAAsB,OAAA,CAAQ,yBAAA;EAwBtD,gBAAA,QAA6B,OAAA,CAAQ,yBAAA;EAAA,QAS7B,kBAAA;EAAA,QAiBA,WAAA;EAAA,QAwBA,oBAAA;AAAA"}
@@ -1,7 +1,7 @@
1
- import { AppHomeService } from "../paths/app-home.service.js";
2
- import { DEFAULT_APP_REGISTRY_URL } from "./app-remote-registry.types.js";
1
+ import { AppHomeService } from "./app-home.service.js";
2
+ import { DEFAULT_APP_REGISTRY_URL } from "../types/app-remote-registry.types.js";
3
3
  import { readFile, rm, writeFile } from "node:fs/promises";
4
- //#region src/registry/app-registry-config.service.ts
4
+ //#region src/services/app-registry-config.service.ts
5
5
  var AppRegistryConfigService = class {
6
6
  constructor(appHomeService = new AppHomeService()) {
7
7
  this.appHomeService = appHomeService;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-registry-config.service.js","names":[],"sources":["../../src/services/app-registry-config.service.ts"],"sourcesContent":["import { readFile, rm, writeFile } from \"node:fs/promises\";\nimport { AppHomeService } from \"#app-runtime/services/app-home.service.js\";\nimport {\n DEFAULT_APP_REGISTRY_URL,\n type AppRegistryConfig,\n type AppRegistryConfigSnapshot,\n} from \"#app-runtime/types/app-remote-registry.types.js\";\n\nexport class AppRegistryConfigService {\n constructor(\n private readonly appHomeService: AppHomeService = new AppHomeService(),\n ) {}\n\n getSnapshot = async (): Promise<AppRegistryConfigSnapshot> => {\n const envUrl = process.env.NEXTCLAW_APP_REGISTRY?.trim();\n if (envUrl) {\n return {\n defaultUrl: DEFAULT_APP_REGISTRY_URL,\n currentUrl: this.normalizeRegistryUrl(envUrl),\n source: \"env\",\n };\n }\n const config = await this.loadOptionalConfig();\n if (config) {\n return {\n defaultUrl: DEFAULT_APP_REGISTRY_URL,\n currentUrl: this.normalizeRegistryUrl(config.registry.url),\n source: \"config\",\n };\n }\n return {\n defaultUrl: DEFAULT_APP_REGISTRY_URL,\n currentUrl: DEFAULT_APP_REGISTRY_URL,\n source: \"default\",\n };\n };\n\n setRegistryUrl = async (registryUrl: string): Promise<AppRegistryConfigSnapshot> => {\n const normalized = this.normalizeRegistryUrl(registryUrl);\n await this.appHomeService.ensureBaseDirectories();\n await writeFile(\n this.appHomeService.getConfigPath(),\n `${JSON.stringify(\n {\n schemaVersion: 1,\n registry: {\n url: normalized,\n },\n } satisfies AppRegistryConfig,\n null,\n 2,\n )}\\n`,\n \"utf-8\",\n );\n return {\n defaultUrl: DEFAULT_APP_REGISTRY_URL,\n currentUrl: normalized,\n source: \"config\",\n };\n };\n\n resetRegistryUrl = async (): Promise<AppRegistryConfigSnapshot> => {\n await rm(this.appHomeService.getConfigPath(), { force: true });\n return {\n defaultUrl: DEFAULT_APP_REGISTRY_URL,\n currentUrl: DEFAULT_APP_REGISTRY_URL,\n source: \"default\",\n };\n };\n\n private loadOptionalConfig = async (): Promise<AppRegistryConfig | undefined> => {\n try {\n const raw = await readFile(this.appHomeService.getConfigPath(), \"utf-8\");\n return this.parseConfig(JSON.parse(raw) as unknown);\n } catch (error) {\n if (\n typeof error === \"object\" &&\n error !== null &&\n \"code\" in error &&\n error.code === \"ENOENT\"\n ) {\n return undefined;\n }\n throw error;\n }\n };\n\n private parseConfig = (rawConfig: unknown): AppRegistryConfig => {\n if (!rawConfig || typeof rawConfig !== \"object\" || Array.isArray(rawConfig)) {\n throw new Error(\"config.json 必须是对象。\");\n }\n const candidate = rawConfig as Record<string, unknown>;\n if (candidate.schemaVersion !== 1) {\n throw new Error(\"当前只支持 config schemaVersion = 1。\");\n }\n const registry = candidate.registry;\n if (!registry || typeof registry !== \"object\" || Array.isArray(registry)) {\n throw new Error(\"config.registry 必须是对象。\");\n }\n const registryUrl = (registry as Record<string, unknown>).url;\n if (typeof registryUrl !== \"string\" || !registryUrl.trim()) {\n throw new Error(\"config.registry.url 必须是非空字符串。\");\n }\n return {\n schemaVersion: 1,\n registry: {\n url: this.normalizeRegistryUrl(registryUrl),\n },\n };\n };\n\n private normalizeRegistryUrl = (registryUrl: string): string => {\n let normalized: URL;\n try {\n normalized = new URL(registryUrl);\n } catch {\n throw new Error(`非法 registry URL:${registryUrl}`);\n }\n if (normalized.protocol !== \"http:\" && normalized.protocol !== \"https:\") {\n throw new Error(`registry URL 只支持 http/https:${registryUrl}`);\n }\n const text = normalized.toString();\n return text.endsWith(\"/\") ? text : `${text}/`;\n };\n}\n"],"mappings":";;;;AAQA,IAAa,2BAAb,MAAsC;CACpC,YACE,iBAAkD,IAAI,gBAAgB,EACtE;AADiB,OAAA,iBAAA;;CAGnB,cAAc,YAAgD;EAC5D,MAAM,SAAS,QAAQ,IAAI,uBAAuB,MAAM;AACxD,MAAI,OACF,QAAO;GACL,YAAY;GACZ,YAAY,KAAK,qBAAqB,OAAO;GAC7C,QAAQ;GACT;EAEH,MAAM,SAAS,MAAM,KAAK,oBAAoB;AAC9C,MAAI,OACF,QAAO;GACL,YAAY;GACZ,YAAY,KAAK,qBAAqB,OAAO,SAAS,IAAI;GAC1D,QAAQ;GACT;AAEH,SAAO;GACL,YAAY;GACZ,YAAY;GACZ,QAAQ;GACT;;CAGH,iBAAiB,OAAO,gBAA4D;EAClF,MAAM,aAAa,KAAK,qBAAqB,YAAY;AACzD,QAAM,KAAK,eAAe,uBAAuB;AACjD,QAAM,UACJ,KAAK,eAAe,eAAe,EACnC,GAAG,KAAK,UACN;GACE,eAAe;GACf,UAAU,EACR,KAAK,YACN;GACF,EACD,MACA,EACD,CAAC,KACF,QACD;AACD,SAAO;GACL,YAAY;GACZ,YAAY;GACZ,QAAQ;GACT;;CAGH,mBAAmB,YAAgD;AACjE,QAAM,GAAG,KAAK,eAAe,eAAe,EAAE,EAAE,OAAO,MAAM,CAAC;AAC9D,SAAO;GACL,YAAY;GACZ,YAAY;GACZ,QAAQ;GACT;;CAGH,qBAA6B,YAAoD;AAC/E,MAAI;GACF,MAAM,MAAM,MAAM,SAAS,KAAK,eAAe,eAAe,EAAE,QAAQ;AACxE,UAAO,KAAK,YAAY,KAAK,MAAM,IAAI,CAAY;WAC5C,OAAO;AACd,OACE,OAAO,UAAU,YACjB,UAAU,QACV,UAAU,SACV,MAAM,SAAS,SAEf;AAEF,SAAM;;;CAIV,eAAuB,cAA0C;AAC/D,MAAI,CAAC,aAAa,OAAO,cAAc,YAAY,MAAM,QAAQ,UAAU,CACzE,OAAM,IAAI,MAAM,qBAAqB;EAEvC,MAAM,YAAY;AAClB,MAAI,UAAU,kBAAkB,EAC9B,OAAM,IAAI,MAAM,kCAAkC;EAEpD,MAAM,WAAW,UAAU;AAC3B,MAAI,CAAC,YAAY,OAAO,aAAa,YAAY,MAAM,QAAQ,SAAS,CACtE,OAAM,IAAI,MAAM,yBAAyB;EAE3C,MAAM,cAAe,SAAqC;AAC1D,MAAI,OAAO,gBAAgB,YAAY,CAAC,YAAY,MAAM,CACxD,OAAM,IAAI,MAAM,gCAAgC;AAElD,SAAO;GACL,eAAe;GACf,UAAU,EACR,KAAK,KAAK,qBAAqB,YAAY,EAC5C;GACF;;CAGH,wBAAgC,gBAAgC;EAC9D,IAAI;AACJ,MAAI;AACF,gBAAa,IAAI,IAAI,YAAY;UAC3B;AACN,SAAM,IAAI,MAAM,mBAAmB,cAAc;;AAEnD,MAAI,WAAW,aAAa,WAAW,WAAW,aAAa,SAC7D,OAAM,IAAI,MAAM,+BAA+B,cAAc;EAE/D,MAAM,OAAO,WAAW,UAAU;AAClC,SAAO,KAAK,SAAS,IAAI,GAAG,OAAO,GAAG,KAAK"}
@@ -1,14 +1,16 @@
1
- import { AppPermissions } from "../manifest/app-manifest.types.js";
2
- import { AppDocumentGrantMap } from "../permissions/app-permissions.types.js";
3
- import { AppHomeService } from "../paths/app-home.service.js";
4
- import { AppInstallSourceKind, AppRegistry, AppRegistryAppRecord, AppRegistryInstalledVersion } from "./app-registry.types.js";
1
+ import { AppPermissions, AppResolvedComponent } from "../types/app-manifest.types.js";
2
+ import { AppDocumentGrantMap } from "../types/app-permissions.types.js";
3
+ import { AppHomeService } from "./app-home.service.js";
4
+ import { AppInstallSourceKind, AppRegistry, AppRegistryAppRecord, AppRegistryInstalledVersion } from "../types/app-registry.types.js";
5
5
 
6
- //#region src/registry/app-registry.service.d.ts
6
+ //#region src/services/app-registry.service.d.ts
7
7
  declare class AppRegistryService {
8
8
  private readonly appHomeService;
9
+ private static readonly mutationQueues;
9
10
  constructor(appHomeService?: AppHomeService);
10
11
  load: () => Promise<AppRegistry>;
11
12
  save: (registry: AppRegistry) => Promise<void>;
13
+ private saveUnlocked;
12
14
  listApps: () => Promise<AppRegistryAppRecord[]>;
13
15
  getApp: (appId: string) => Promise<AppRegistryAppRecord | undefined>;
14
16
  getActiveVersion: (appId: string) => Promise<AppRegistryInstalledVersion | undefined>;
@@ -28,12 +30,23 @@ declare class AppRegistryService {
28
30
  bundleUrl?: string;
29
31
  sha256?: string;
30
32
  publisher?: AppRegistryInstalledVersion["publisher"];
33
+ manifestSchemaVersion: 1 | 2;
34
+ components?: AppResolvedComponent[];
35
+ primaryPanelId?: string;
36
+ enabled?: boolean;
31
37
  }) => Promise<AppRegistryAppRecord>;
38
+ setEnabled: (appId: string, enabled: boolean) => Promise<AppRegistryAppRecord>;
39
+ activateVersion: (appId: string, version: string) => Promise<AppRegistryAppRecord>;
32
40
  updateGrants: (appId: string, grants: AppDocumentGrantMap) => Promise<AppRegistryAppRecord>;
33
41
  setDocumentGrant: (appId: string, scopeId: string, directoryPath: string) => Promise<AppRegistryAppRecord>;
34
42
  removeDocumentGrant: (appId: string, scopeId: string) => Promise<boolean>;
35
43
  removeApp: (appId: string) => Promise<AppRegistryAppRecord | undefined>;
44
+ private updateApp;
45
+ private withMutation;
36
46
  private parseRegistry;
47
+ private assertRecord;
48
+ private requireString;
49
+ private isMissingFileError;
37
50
  }
38
51
  //#endregion
39
52
  export { AppRegistryService };
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-registry.service.d.ts","names":[],"sources":["../../src/services/app-registry.service.ts"],"mappings":";;;;;;cAYa,kBAAA;EAAA,iBAGkB,cAAA;EAAA,wBAFL,cAAA;cAEK,cAAA,GAAgB,cAAA;EAE7C,IAAA,QAAiB,OAAA,CAAQ,WAAA;EAazB,IAAA,GAAc,QAAA,EAAU,WAAA,KAAc,OAAA;EAAA,QAI9B,YAAA;EAsBR,QAAA,QAAqB,OAAA,CAAQ,oBAAA;EAK7B,MAAA,GAAgB,KAAA,aAAgB,OAAA,CAAQ,oBAAA;EAKxC,gBAAA,GAA0B,KAAA,aAAgB,OAAA,CAAQ,2BAAA;EAKlD,kBAAA,GAA4B,MAAA;IAC1B,KAAA;IACA,IAAA;IACA,WAAA;IACA,OAAA;IACA,gBAAA;IACA,aAAA;IACA,UAAA,EAAY,oBAAA;IACZ,SAAA;IACA,WAAA;IACA,gBAAA,GAAmB,2BAAA;IACnB,WAAA,EAAa,cAAA;IACb,WAAA;IACA,SAAA;IACA,MAAA;IACA,SAAA,GAAY,2BAAA;IACZ,qBAAA;IACA,UAAA,GAAa,oBAAA;IACb,cAAA;IACA,OAAA;EAAA,MACE,OAAA,CAAQ,oBAAA;EAsCZ,UAAA,GAAoB,KAAA,UAAe,OAAA,cAAmB,OAAA,CAAQ,oBAAA;EAI9D,eAAA,GACE,KAAA,UACA,OAAA,aACC,OAAA,CAAQ,oBAAA;EASX,YAAA,GACE,KAAA,UACA,MAAA,EAAQ,mBAAA,KACP,OAAA,CAAQ,oBAAA;EAOX,gBAAA,GACE,KAAA,UACA,OAAA,UACA,aAAA,aACC,OAAA,CAAQ,oBAAA;EAIX,mBAAA,GAA6B,KAAA,UAAe,OAAA,aAAkB,OAAA;EAc9D,SAAA,GAAmB,KAAA,aAAgB,OAAA,CAAQ,oBAAA;EAAA,QAanC,SAAA;EAAA,QAiBA,YAAA;EAAA,QAcA,aAAA;EAAA,QAyCA,YAAA;EAAA,QAOA,aAAA;EAAA,QAOA,kBAAA;AAAA"}
@@ -0,0 +1,210 @@
1
+ import { AppHomeService } from "./app-home.service.js";
2
+ import { open, readFile, rename, rm } from "node:fs/promises";
3
+ import path from "node:path";
4
+ //#region src/services/app-registry.service.ts
5
+ var AppRegistryService = class AppRegistryService {
6
+ static mutationQueues = /* @__PURE__ */ new Map();
7
+ constructor(appHomeService = new AppHomeService()) {
8
+ this.appHomeService = appHomeService;
9
+ }
10
+ load = async () => {
11
+ await this.appHomeService.ensureBaseDirectories();
12
+ try {
13
+ const raw = await readFile(this.appHomeService.getRegistryPath(), "utf-8");
14
+ return this.parseRegistry(JSON.parse(raw));
15
+ } catch (error) {
16
+ if (this.isMissingFileError(error)) return {
17
+ schemaVersion: 1,
18
+ apps: {}
19
+ };
20
+ throw error;
21
+ }
22
+ };
23
+ save = async (registry) => {
24
+ await this.withMutation(async () => await this.saveUnlocked(registry));
25
+ };
26
+ saveUnlocked = async (registry) => {
27
+ await this.appHomeService.ensureBaseDirectories();
28
+ const registryPath = this.appHomeService.getRegistryPath();
29
+ const temporaryPath = path.join(path.dirname(registryPath), `.${path.basename(registryPath)}.${process.pid}.${Date.now()}.tmp`);
30
+ const handle = await open(temporaryPath, "wx", 384);
31
+ try {
32
+ await handle.writeFile(`${JSON.stringify(registry, null, 2)}\n`, "utf-8");
33
+ await handle.sync();
34
+ } finally {
35
+ await handle.close();
36
+ }
37
+ try {
38
+ await rename(temporaryPath, registryPath);
39
+ } catch (error) {
40
+ await rm(temporaryPath, { force: true });
41
+ throw error;
42
+ }
43
+ };
44
+ listApps = async () => {
45
+ const registry = await this.load();
46
+ return Object.values(registry.apps).sort((left, right) => left.appId.localeCompare(right.appId));
47
+ };
48
+ getApp = async (appId) => {
49
+ return (await this.load()).apps[appId];
50
+ };
51
+ getActiveVersion = async (appId) => {
52
+ const appRecord = await this.getApp(appId);
53
+ return appRecord?.installedVersions[appRecord.activeVersion];
54
+ };
55
+ upsertInstallation = async (params) => {
56
+ return await this.withMutation(async () => {
57
+ const registry = await this.load();
58
+ const currentRecord = registry.apps[params.appId];
59
+ const nextRecord = {
60
+ appId: params.appId,
61
+ name: params.name,
62
+ description: params.description,
63
+ activeVersion: params.version,
64
+ enabled: params.enabled ?? currentRecord?.enabled ?? params.manifestSchemaVersion === 1,
65
+ dataDirectory: params.dataDirectory,
66
+ installedVersions: {
67
+ ...currentRecord?.installedVersions ?? {},
68
+ [params.version]: {
69
+ version: params.version,
70
+ installDirectory: params.installDirectory,
71
+ sourceKind: params.sourceKind,
72
+ sourceRef: params.sourceRef,
73
+ installedAt: params.installedAt,
74
+ distributionMode: params.distributionMode,
75
+ permissions: params.permissions,
76
+ registryUrl: params.registryUrl,
77
+ bundleUrl: params.bundleUrl,
78
+ sha256: params.sha256,
79
+ publisher: params.publisher,
80
+ manifestSchemaVersion: params.manifestSchemaVersion,
81
+ components: params.components,
82
+ primaryPanelId: params.primaryPanelId
83
+ }
84
+ },
85
+ grants: currentRecord?.grants ?? {}
86
+ };
87
+ registry.apps[params.appId] = nextRecord;
88
+ await this.saveUnlocked(registry);
89
+ return nextRecord;
90
+ });
91
+ };
92
+ setEnabled = async (appId, enabled) => {
93
+ return await this.updateApp(appId, (record) => ({
94
+ ...record,
95
+ enabled
96
+ }));
97
+ };
98
+ activateVersion = async (appId, version) => {
99
+ return await this.updateApp(appId, (record) => {
100
+ if (!record.installedVersions[version]) throw new Error(`应用 ${appId} 未安装版本 ${version}。`);
101
+ return {
102
+ ...record,
103
+ activeVersion: version
104
+ };
105
+ });
106
+ };
107
+ updateGrants = async (appId, grants) => {
108
+ return await this.updateApp(appId, (record) => ({
109
+ ...record,
110
+ grants: {
111
+ ...record.grants,
112
+ ...grants
113
+ }
114
+ }));
115
+ };
116
+ setDocumentGrant = async (appId, scopeId, directoryPath) => {
117
+ return await this.updateGrants(appId, { [scopeId]: directoryPath });
118
+ };
119
+ removeDocumentGrant = async (appId, scopeId) => {
120
+ let removed = false;
121
+ await this.updateApp(appId, (record) => {
122
+ if (!(scopeId in record.grants)) return record;
123
+ const grants = { ...record.grants };
124
+ delete grants[scopeId];
125
+ removed = true;
126
+ return {
127
+ ...record,
128
+ grants
129
+ };
130
+ });
131
+ return removed;
132
+ };
133
+ removeApp = async (appId) => {
134
+ return await this.withMutation(async () => {
135
+ const registry = await this.load();
136
+ const appRecord = registry.apps[appId];
137
+ if (!appRecord) return;
138
+ delete registry.apps[appId];
139
+ await this.saveUnlocked(registry);
140
+ return appRecord;
141
+ });
142
+ };
143
+ updateApp = async (appId, update) => {
144
+ return await this.withMutation(async () => {
145
+ const registry = await this.load();
146
+ const appRecord = registry.apps[appId];
147
+ if (!appRecord) throw new Error(`未找到已安装应用:${appId}`);
148
+ const nextRecord = update(appRecord);
149
+ registry.apps[appId] = nextRecord;
150
+ await this.saveUnlocked(registry);
151
+ return nextRecord;
152
+ });
153
+ };
154
+ withMutation = async (operation) => {
155
+ const registryPath = path.resolve(this.appHomeService.getRegistryPath());
156
+ const current = (AppRegistryService.mutationQueues.get(registryPath) ?? Promise.resolve()).catch(() => void 0).then(operation);
157
+ AppRegistryService.mutationQueues.set(registryPath, current);
158
+ try {
159
+ return await current;
160
+ } finally {
161
+ if (AppRegistryService.mutationQueues.get(registryPath) === current) AppRegistryService.mutationQueues.delete(registryPath);
162
+ }
163
+ };
164
+ parseRegistry = (rawRegistry) => {
165
+ const candidate = this.assertRecord(rawRegistry, "registry.json");
166
+ if (candidate.schemaVersion !== 1) throw new Error("当前只支持 registry schemaVersion = 1。");
167
+ const rawApps = this.assertRecord(candidate.apps, "registry.apps");
168
+ const apps = {};
169
+ for (const [appId, rawApp] of Object.entries(rawApps)) {
170
+ const app = this.assertRecord(rawApp, `registry.apps.${appId}`);
171
+ const installedVersions = {};
172
+ const rawVersions = this.assertRecord(app.installedVersions, `registry.apps.${appId}.installedVersions`);
173
+ for (const [version, rawVersion] of Object.entries(rawVersions)) {
174
+ const versionRecord = this.assertRecord(rawVersion, `registry.apps.${appId}.installedVersions.${version}`);
175
+ installedVersions[version] = {
176
+ ...versionRecord,
177
+ version,
178
+ manifestSchemaVersion: versionRecord.manifestSchemaVersion === 2 ? 2 : 1
179
+ };
180
+ }
181
+ const activeVersion = this.requireString(app.activeVersion, `registry.apps.${appId}.activeVersion`);
182
+ if (!installedVersions[activeVersion]) throw new Error(`registry.apps.${appId} 缺少 activeVersion ${activeVersion}。`);
183
+ apps[appId] = {
184
+ ...app,
185
+ appId,
186
+ enabled: typeof app.enabled === "boolean" ? app.enabled : true,
187
+ activeVersion,
188
+ installedVersions,
189
+ grants: app.grants && typeof app.grants === "object" ? app.grants : {}
190
+ };
191
+ }
192
+ return {
193
+ schemaVersion: 1,
194
+ apps
195
+ };
196
+ };
197
+ assertRecord = (value, field) => {
198
+ if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error(`${field} 必须是对象。`);
199
+ return value;
200
+ };
201
+ requireString = (value, field) => {
202
+ if (typeof value !== "string" || !value.trim()) throw new Error(`${field} 必须是非空字符串。`);
203
+ return value;
204
+ };
205
+ isMissingFileError = (error) => typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
206
+ };
207
+ //#endregion
208
+ export { AppRegistryService };
209
+
210
+ //# sourceMappingURL=app-registry.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-registry.service.js","names":[],"sources":["../../src/services/app-registry.service.ts"],"sourcesContent":["import { open, readFile, rename, rm } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport type { AppPermissions, AppResolvedComponent } from \"#app-runtime/types/app-manifest.types.js\";\nimport type { AppDocumentGrantMap } from \"#app-runtime/types/app-permissions.types.js\";\nimport { AppHomeService } from \"#app-runtime/services/app-home.service.js\";\nimport type {\n AppInstallSourceKind,\n AppRegistry,\n AppRegistryAppRecord,\n AppRegistryInstalledVersion,\n} from \"#app-runtime/types/app-registry.types.js\";\n\nexport class AppRegistryService {\n private static readonly mutationQueues = new Map<string, Promise<unknown>>();\n\n constructor(private readonly appHomeService: AppHomeService = new AppHomeService()) {}\n\n load = async (): Promise<AppRegistry> => {\n await this.appHomeService.ensureBaseDirectories();\n try {\n const raw = await readFile(this.appHomeService.getRegistryPath(), \"utf-8\");\n return this.parseRegistry(JSON.parse(raw) as unknown);\n } catch (error) {\n if (this.isMissingFileError(error)) {\n return { schemaVersion: 1, apps: {} };\n }\n throw error;\n }\n };\n\n save = async (registry: AppRegistry): Promise<void> => {\n await this.withMutation(async () => await this.saveUnlocked(registry));\n };\n\n private saveUnlocked = async (registry: AppRegistry): Promise<void> => {\n await this.appHomeService.ensureBaseDirectories();\n const registryPath = this.appHomeService.getRegistryPath();\n const temporaryPath = path.join(\n path.dirname(registryPath),\n `.${path.basename(registryPath)}.${process.pid}.${Date.now()}.tmp`,\n );\n const handle = await open(temporaryPath, \"wx\", 0o600);\n try {\n await handle.writeFile(`${JSON.stringify(registry, null, 2)}\\n`, \"utf-8\");\n await handle.sync();\n } finally {\n await handle.close();\n }\n try {\n await rename(temporaryPath, registryPath);\n } catch (error) {\n await rm(temporaryPath, { force: true });\n throw error;\n }\n };\n\n listApps = async (): Promise<AppRegistryAppRecord[]> => {\n const registry = await this.load();\n return Object.values(registry.apps).sort((left, right) => left.appId.localeCompare(right.appId));\n };\n\n getApp = async (appId: string): Promise<AppRegistryAppRecord | undefined> => {\n const registry = await this.load();\n return registry.apps[appId];\n };\n\n getActiveVersion = async (appId: string): Promise<AppRegistryInstalledVersion | undefined> => {\n const appRecord = await this.getApp(appId);\n return appRecord?.installedVersions[appRecord.activeVersion];\n };\n\n upsertInstallation = async (params: {\n appId: string;\n name: string;\n description?: string;\n version: string;\n installDirectory: string;\n dataDirectory: string;\n sourceKind: AppInstallSourceKind;\n sourceRef: string;\n installedAt: string;\n distributionMode?: AppRegistryInstalledVersion[\"distributionMode\"];\n permissions: AppPermissions;\n registryUrl?: string;\n bundleUrl?: string;\n sha256?: string;\n publisher?: AppRegistryInstalledVersion[\"publisher\"];\n manifestSchemaVersion: 1 | 2;\n components?: AppResolvedComponent[];\n primaryPanelId?: string;\n enabled?: boolean;\n }): Promise<AppRegistryAppRecord> => {\n return await this.withMutation(async () => {\n const registry = await this.load();\n const currentRecord = registry.apps[params.appId];\n const nextRecord: AppRegistryAppRecord = {\n appId: params.appId,\n name: params.name,\n description: params.description,\n activeVersion: params.version,\n enabled: params.enabled ?? currentRecord?.enabled ?? params.manifestSchemaVersion === 1,\n dataDirectory: params.dataDirectory,\n installedVersions: {\n ...(currentRecord?.installedVersions ?? {}),\n [params.version]: {\n version: params.version,\n installDirectory: params.installDirectory,\n sourceKind: params.sourceKind,\n sourceRef: params.sourceRef,\n installedAt: params.installedAt,\n distributionMode: params.distributionMode,\n permissions: params.permissions,\n registryUrl: params.registryUrl,\n bundleUrl: params.bundleUrl,\n sha256: params.sha256,\n publisher: params.publisher,\n manifestSchemaVersion: params.manifestSchemaVersion,\n components: params.components,\n primaryPanelId: params.primaryPanelId,\n },\n },\n grants: currentRecord?.grants ?? {},\n };\n registry.apps[params.appId] = nextRecord;\n await this.saveUnlocked(registry);\n return nextRecord;\n });\n };\n\n setEnabled = async (appId: string, enabled: boolean): Promise<AppRegistryAppRecord> => {\n return await this.updateApp(appId, (record) => ({ ...record, enabled }));\n };\n\n activateVersion = async (\n appId: string,\n version: string,\n ): Promise<AppRegistryAppRecord> => {\n return await this.updateApp(appId, (record) => {\n if (!record.installedVersions[version]) {\n throw new Error(`应用 ${appId} 未安装版本 ${version}。`);\n }\n return { ...record, activeVersion: version };\n });\n };\n\n updateGrants = async (\n appId: string,\n grants: AppDocumentGrantMap,\n ): Promise<AppRegistryAppRecord> => {\n return await this.updateApp(appId, (record) => ({\n ...record,\n grants: { ...record.grants, ...grants },\n }));\n };\n\n setDocumentGrant = async (\n appId: string,\n scopeId: string,\n directoryPath: string,\n ): Promise<AppRegistryAppRecord> => {\n return await this.updateGrants(appId, { [scopeId]: directoryPath });\n };\n\n removeDocumentGrant = async (appId: string, scopeId: string): Promise<boolean> => {\n let removed = false;\n await this.updateApp(appId, (record) => {\n if (!(scopeId in record.grants)) {\n return record;\n }\n const grants = { ...record.grants };\n delete grants[scopeId];\n removed = true;\n return { ...record, grants };\n });\n return removed;\n };\n\n removeApp = async (appId: string): Promise<AppRegistryAppRecord | undefined> => {\n return await this.withMutation(async () => {\n const registry = await this.load();\n const appRecord = registry.apps[appId];\n if (!appRecord) {\n return undefined;\n }\n delete registry.apps[appId];\n await this.saveUnlocked(registry);\n return appRecord;\n });\n };\n\n private updateApp = async (\n appId: string,\n update: (record: AppRegistryAppRecord) => AppRegistryAppRecord,\n ): Promise<AppRegistryAppRecord> => {\n return await this.withMutation(async () => {\n const registry = await this.load();\n const appRecord = registry.apps[appId];\n if (!appRecord) {\n throw new Error(`未找到已安装应用:${appId}`);\n }\n const nextRecord = update(appRecord);\n registry.apps[appId] = nextRecord;\n await this.saveUnlocked(registry);\n return nextRecord;\n });\n };\n\n private withMutation = async <T>(operation: () => Promise<T>): Promise<T> => {\n const registryPath = path.resolve(this.appHomeService.getRegistryPath());\n const previous = AppRegistryService.mutationQueues.get(registryPath) ?? Promise.resolve();\n const current = previous.catch(() => undefined).then(operation);\n AppRegistryService.mutationQueues.set(registryPath, current);\n try {\n return await current;\n } finally {\n if (AppRegistryService.mutationQueues.get(registryPath) === current) {\n AppRegistryService.mutationQueues.delete(registryPath);\n }\n }\n };\n\n private parseRegistry = (rawRegistry: unknown): AppRegistry => {\n const candidate = this.assertRecord(rawRegistry, \"registry.json\");\n if (candidate.schemaVersion !== 1) {\n throw new Error(\"当前只支持 registry schemaVersion = 1。\");\n }\n const rawApps = this.assertRecord(candidate.apps, \"registry.apps\");\n const apps: Record<string, AppRegistryAppRecord> = {};\n for (const [appId, rawApp] of Object.entries(rawApps)) {\n const app = this.assertRecord(rawApp, `registry.apps.${appId}`) as Partial<AppRegistryAppRecord>;\n const installedVersions: Record<string, AppRegistryInstalledVersion> = {};\n const rawVersions = this.assertRecord(\n app.installedVersions,\n `registry.apps.${appId}.installedVersions`,\n );\n for (const [version, rawVersion] of Object.entries(rawVersions)) {\n const versionRecord = this.assertRecord(\n rawVersion,\n `registry.apps.${appId}.installedVersions.${version}`,\n ) as Partial<AppRegistryInstalledVersion>;\n installedVersions[version] = {\n ...(versionRecord as AppRegistryInstalledVersion),\n version,\n manifestSchemaVersion: versionRecord.manifestSchemaVersion === 2 ? 2 : 1,\n };\n }\n const activeVersion = this.requireString(app.activeVersion, `registry.apps.${appId}.activeVersion`);\n if (!installedVersions[activeVersion]) {\n throw new Error(`registry.apps.${appId} 缺少 activeVersion ${activeVersion}。`);\n }\n apps[appId] = {\n ...(app as AppRegistryAppRecord),\n appId,\n enabled: typeof app.enabled === \"boolean\" ? app.enabled : true,\n activeVersion,\n installedVersions,\n grants: app.grants && typeof app.grants === \"object\" ? app.grants : {},\n };\n }\n return { schemaVersion: 1, apps };\n };\n\n private assertRecord = (value: unknown, field: string): Record<string, unknown> => {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) {\n throw new Error(`${field} 必须是对象。`);\n }\n return value as Record<string, unknown>;\n };\n\n private requireString = (value: unknown, field: string): string => {\n if (typeof value !== \"string\" || !value.trim()) {\n throw new Error(`${field} 必须是非空字符串。`);\n }\n return value;\n };\n\n private isMissingFileError = (error: unknown): boolean =>\n typeof error === \"object\" && error !== null &&\n \"code\" in error && (error as { code?: unknown }).code === \"ENOENT\";\n}\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
- import { AppRemoteRegistryResolution } from "./app-remote-registry.types.js";
1
+ import { AppRemoteRegistryResolution } from "../types/app-remote-registry.types.js";
2
2
  import { AppRegistryConfigService } from "./app-registry-config.service.js";
3
3
 
4
- //#region src/registry/app-remote-registry-client.service.d.ts
4
+ //#region src/services/app-remote-registry-client.service.d.ts
5
5
  declare class AppRemoteRegistryClientService {
6
6
  private readonly configService;
7
7
  constructor(configService?: AppRegistryConfigService);
@@ -22,6 +22,10 @@ declare class AppRemoteRegistryClientService {
22
22
  private readRequiredString;
23
23
  private readOptionalString;
24
24
  private normalizeBundleFileName;
25
+ private readGetResponse;
26
+ private isTransientHttpStatus;
27
+ private isTransientNetworkError;
28
+ private waitBeforeRetry;
25
29
  private normalizeRegistryUrl;
26
30
  }
27
31
  //#endregion
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-remote-registry-client.service.d.ts","names":[],"sources":["../../src/services/app-remote-registry-client.service.ts"],"mappings":";;;;cAaa,8BAAA;EAAA,iBAEQ,aAAA;cAAA,aAAA,GAAe,wBAAA;EAGlC,OAAA,GAAiB,MAAA;IACf,KAAA;IACA,OAAA;IACA,WAAA;EAAA,MACE,OAAA,CAAQ,2BAAA;EAoCZ,cAAA,GAAwB,MAAA;IACtB,UAAA,EAAY,2BAAA;IACZ,eAAA;EAAA,MACE,OAAA;IACF,UAAA;EAAA;EAAA,QAkCM,aAAA;EAAA,QAsCA,YAAA;EAAA,QAwDA,cAAA;EAAA,QAkBA,kBAAA;EAAA,QAOA,kBAAA;EAAA,QAUA,uBAAA;EAAA,QAIA,eAAA;EAAA,QA6CA,qBAAA;EAAA,QAIA,uBAAA;EAAA,QAIA,eAAA;EAAA,QAMA,oBAAA;AAAA"}
@@ -1,8 +1,10 @@
1
1
  import { AppRegistryConfigService } from "./app-registry-config.service.js";
2
- import { writeFile } from "node:fs/promises";
3
2
  import { createHash } from "node:crypto";
3
+ import { writeFile } from "node:fs/promises";
4
4
  import path from "node:path";
5
- //#region src/registry/app-remote-registry-client.service.ts
5
+ //#region src/services/app-remote-registry-client.service.ts
6
+ const MAX_REMOTE_BUNDLE_BYTES = 25 * 1024 * 1024;
7
+ const MAX_REGISTRY_GET_ATTEMPTS = 3;
6
8
  var AppRemoteRegistryClientService = class {
7
9
  constructor(configService = new AppRegistryConfigService()) {
8
10
  this.configService = configService;
@@ -11,9 +13,8 @@ var AppRemoteRegistryClientService = class {
11
13
  const { appId, version: requestedVersion, registryUrl: overrideRegistryUrl } = params;
12
14
  const registryUrl = overrideRegistryUrl ? this.normalizeRegistryUrl(overrideRegistryUrl) : (await this.configService.getSnapshot()).currentUrl;
13
15
  const metadataUrl = new URL(encodeURIComponent(appId), registryUrl).toString();
14
- const response = await fetch(metadataUrl);
15
- if (!response.ok) throw new Error(`无法从 registry 拉取 ${appId} metadata:${response.status} ${response.statusText}`);
16
- const document = this.parseDocument(await response.json(), appId);
16
+ const rawDocument = await this.readGetResponse(metadataUrl, `无法从 registry 拉取 ${appId} metadata`, async (response) => await response.json());
17
+ const document = this.parseDocument(rawDocument, appId);
17
18
  const version = requestedVersion ?? document["dist-tags"].latest;
18
19
  const versionRecord = document.versions[version];
19
20
  if (!versionRecord) throw new Error(`registry ${registryUrl} 未提供 ${appId}@${version}。`);
@@ -32,9 +33,13 @@ var AppRemoteRegistryClientService = class {
32
33
  };
33
34
  downloadBundle = async (params) => {
34
35
  const { resolution, targetDirectory } = params;
35
- const response = await fetch(resolution.bundleUrl);
36
- if (!response.ok) throw new Error(`无法下载 bundle:${resolution.bundleUrl} (${response.status} ${response.statusText})`);
37
- const bundleBytes = Buffer.from(await response.arrayBuffer());
36
+ const bundleBytes = await this.readGetResponse(resolution.bundleUrl, `无法下载 bundle:${resolution.bundleUrl}`, async (response) => {
37
+ const contentLength = Number(response.headers.get("content-length"));
38
+ if (Number.isFinite(contentLength) && contentLength > MAX_REMOTE_BUNDLE_BYTES) throw new Error(`远程 bundle 超过 ${MAX_REMOTE_BUNDLE_BYTES} bytes 上限。`);
39
+ const bytes = Buffer.from(await response.arrayBuffer());
40
+ if (bytes.byteLength > MAX_REMOTE_BUNDLE_BYTES) throw new Error(`远程 bundle 超过 ${MAX_REMOTE_BUNDLE_BYTES} bytes 上限。`);
41
+ return bytes;
42
+ });
38
43
  const actualSha256 = createHash("sha256").update(bundleBytes).digest("hex");
39
44
  if (actualSha256 !== resolution.sha256) throw new Error(`bundle checksum 校验失败:期望 ${resolution.sha256},实际 ${actualSha256}`);
40
45
  const bundlePath = path.join(targetDirectory, `${this.normalizeBundleFileName(resolution.appId)}-${resolution.version}.napp`);
@@ -102,6 +107,47 @@ var AppRemoteRegistryClientService = class {
102
107
  normalizeBundleFileName = (appId) => {
103
108
  return appId.replace(/[^a-zA-Z0-9._-]+/g, "-");
104
109
  };
110
+ readGetResponse = async (url, operation, read) => {
111
+ let lastError;
112
+ for (let attempt = 1; attempt <= MAX_REGISTRY_GET_ATTEMPTS; attempt += 1) {
113
+ try {
114
+ const response = await fetch(url);
115
+ if (!response.ok) {
116
+ if (this.isTransientHttpStatus(response.status) && attempt < MAX_REGISTRY_GET_ATTEMPTS) {
117
+ await response.body?.cancel();
118
+ await this.waitBeforeRetry(attempt);
119
+ continue;
120
+ }
121
+ throw new Error(`${operation}:${response.status} ${response.statusText}`);
122
+ }
123
+ try {
124
+ return await read(response);
125
+ } catch (error) {
126
+ if (!this.isTransientNetworkError(error) || attempt === MAX_REGISTRY_GET_ATTEMPTS) throw error;
127
+ lastError = error;
128
+ }
129
+ } catch (error) {
130
+ if (!this.isTransientNetworkError(error) || attempt === MAX_REGISTRY_GET_ATTEMPTS) {
131
+ if (this.isTransientNetworkError(error)) throw new Error(`${operation}失败(已尝试 ${MAX_REGISTRY_GET_ATTEMPTS} 次)。`, { cause: error });
132
+ throw error;
133
+ }
134
+ lastError = error;
135
+ }
136
+ await this.waitBeforeRetry(attempt);
137
+ }
138
+ throw new Error(`${operation}失败(已尝试 ${MAX_REGISTRY_GET_ATTEMPTS} 次)。`, { cause: lastError });
139
+ };
140
+ isTransientHttpStatus = (status) => {
141
+ return status === 408 || status === 429 || status >= 500;
142
+ };
143
+ isTransientNetworkError = (error) => {
144
+ return error instanceof TypeError;
145
+ };
146
+ waitBeforeRetry = async (attempt) => {
147
+ await new Promise((resolve) => {
148
+ setTimeout(resolve, attempt * 100);
149
+ });
150
+ };
105
151
  normalizeRegistryUrl = (registryUrl) => {
106
152
  let normalized;
107
153
  try {
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-remote-registry-client.service.js","names":[],"sources":["../../src/services/app-remote-registry-client.service.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { AppRegistryConfigService } from \"./app-registry-config.service.js\";\nimport type {\n AppRemoteRegistryDocument,\n AppRemoteRegistryResolution,\n AppRemoteRegistryVersion,\n} from \"#app-runtime/types/app-remote-registry.types.js\";\n\nconst MAX_REMOTE_BUNDLE_BYTES = 25 * 1024 * 1024;\nconst MAX_REGISTRY_GET_ATTEMPTS = 3;\n\nexport class AppRemoteRegistryClientService {\n constructor(\n private readonly configService: AppRegistryConfigService = new AppRegistryConfigService(),\n ) {}\n\n resolve = async (params: {\n appId: string;\n version?: string;\n registryUrl?: string;\n }): Promise<AppRemoteRegistryResolution> => {\n const { appId, version: requestedVersion, registryUrl: overrideRegistryUrl } = params;\n const registryUrl = overrideRegistryUrl\n ? this.normalizeRegistryUrl(overrideRegistryUrl)\n : (await this.configService.getSnapshot()).currentUrl;\n const metadataUrl = new URL(encodeURIComponent(appId), registryUrl).toString();\n const rawDocument = await this.readGetResponse(\n metadataUrl,\n `无法从 registry 拉取 ${appId} metadata`,\n async (response) => await response.json(),\n );\n const document = this.parseDocument(rawDocument, appId);\n const version = requestedVersion ?? document[\"dist-tags\"].latest;\n const versionRecord = document.versions[version];\n if (!versionRecord) {\n throw new Error(`registry ${registryUrl} 未提供 ${appId}@${version}。`);\n }\n return {\n registryUrl,\n metadataUrl,\n appId,\n version,\n description: versionRecord.description ?? document.description,\n publisher: versionRecord.publisher,\n permissions: versionRecord.permissions,\n distributionMode: versionRecord.dist.kind === \"source\" ? \"source\" : \"bundle\",\n bundleUrl: new URL(\n versionRecord.dist.artifact ??\n versionRecord.dist.source ??\n versionRecord.dist.bundle,\n metadataUrl,\n ).toString(),\n sha256: versionRecord.dist.sha256,\n };\n };\n\n downloadBundle = async (params: {\n resolution: AppRemoteRegistryResolution;\n targetDirectory: string;\n }): Promise<{\n bundlePath: string;\n }> => {\n const { resolution, targetDirectory } = params;\n const bundleBytes = await this.readGetResponse(\n resolution.bundleUrl,\n `无法下载 bundle:${resolution.bundleUrl}`,\n async (response) => {\n const contentLength = Number(response.headers.get(\"content-length\"));\n if (Number.isFinite(contentLength) && contentLength > MAX_REMOTE_BUNDLE_BYTES) {\n throw new Error(`远程 bundle 超过 ${MAX_REMOTE_BUNDLE_BYTES} bytes 上限。`);\n }\n const bytes = Buffer.from(await response.arrayBuffer());\n if (bytes.byteLength > MAX_REMOTE_BUNDLE_BYTES) {\n throw new Error(`远程 bundle 超过 ${MAX_REMOTE_BUNDLE_BYTES} bytes 上限。`);\n }\n return bytes;\n },\n );\n const actualSha256 = createHash(\"sha256\").update(bundleBytes).digest(\"hex\");\n if (actualSha256 !== resolution.sha256) {\n throw new Error(\n `bundle checksum 校验失败:期望 ${resolution.sha256},实际 ${actualSha256}`,\n );\n }\n const bundlePath = path.join(\n targetDirectory,\n `${this.normalizeBundleFileName(resolution.appId)}-${resolution.version}.napp`,\n );\n await writeFile(bundlePath, bundleBytes);\n return {\n bundlePath,\n };\n };\n\n private parseDocument = (\n rawDocument: unknown,\n appId: string,\n ): AppRemoteRegistryDocument => {\n if (!rawDocument || typeof rawDocument !== \"object\" || Array.isArray(rawDocument)) {\n throw new Error(`registry ${appId} metadata 必须是对象。`);\n }\n const candidate = rawDocument as Record<string, unknown>;\n const name = this.readRequiredString(candidate.name, \"name\");\n if (name !== appId) {\n throw new Error(`registry 返回的包名与请求不一致:请求 ${appId},收到 ${name}`);\n }\n const distTags = candidate[\"dist-tags\"];\n if (!distTags || typeof distTags !== \"object\" || Array.isArray(distTags)) {\n throw new Error(\"registry metadata 缺少 dist-tags。\");\n }\n const versions = candidate.versions;\n if (!versions || typeof versions !== \"object\" || Array.isArray(versions)) {\n throw new Error(\"registry metadata 缺少 versions。\");\n }\n return {\n name,\n description: this.readOptionalString(candidate.description, \"description\"),\n \"dist-tags\": {\n latest: this.readRequiredString(\n (distTags as Record<string, unknown>).latest,\n \"dist-tags.latest\",\n ),\n },\n versions: Object.fromEntries(\n Object.entries(versions).map(([version, rawVersion]) => [\n version,\n this.parseVersion(rawVersion, name, version),\n ]),\n ),\n };\n };\n\n private parseVersion = (\n rawVersion: unknown,\n appId: string,\n version: string,\n ): AppRemoteRegistryVersion => {\n if (!rawVersion || typeof rawVersion !== \"object\" || Array.isArray(rawVersion)) {\n throw new Error(`registry metadata 的 versions.${version} 必须是对象。`);\n }\n const candidate = rawVersion as Record<string, unknown>;\n const name = this.readRequiredString(candidate.name, `versions.${version}.name`);\n const parsedVersion = this.readRequiredString(\n candidate.version,\n `versions.${version}.version`,\n );\n if (name !== appId || parsedVersion !== version) {\n throw new Error(`registry metadata 的 versions.${version} 与 app id/version 不一致。`);\n }\n const dist = candidate.dist;\n if (!dist || typeof dist !== \"object\" || Array.isArray(dist)) {\n throw new Error(`registry metadata 的 versions.${version}.dist 必须是对象。`);\n }\n const distCandidate = dist as Record<string, unknown>;\n return {\n name,\n version: parsedVersion,\n description: this.readOptionalString(\n candidate.description,\n `versions.${version}.description`,\n ),\n publisher: this.parsePublisher(candidate.publisher, version),\n permissions: candidate.permissions as AppRemoteRegistryVersion[\"permissions\"],\n dist: {\n kind:\n distCandidate.kind === \"source\" || distCandidate.kind === \"bundle\"\n ? distCandidate.kind\n : undefined,\n artifact: this.readOptionalString(\n distCandidate.artifact,\n `versions.${version}.dist.artifact`,\n ),\n bundle: this.readRequiredString(\n distCandidate.bundle,\n `versions.${version}.dist.bundle`,\n ),\n source: this.readOptionalString(\n distCandidate.source,\n `versions.${version}.dist.source`,\n ),\n sha256: this.readRequiredString(\n distCandidate.sha256,\n `versions.${version}.dist.sha256`,\n ),\n },\n };\n };\n\n private parsePublisher = (\n rawPublisher: unknown,\n version: string,\n ): AppRemoteRegistryVersion[\"publisher\"] => {\n if (rawPublisher === undefined) {\n return undefined;\n }\n if (!rawPublisher || typeof rawPublisher !== \"object\" || Array.isArray(rawPublisher)) {\n throw new Error(`versions.${version}.publisher 必须是对象。`);\n }\n const candidate = rawPublisher as Record<string, unknown>;\n return {\n id: this.readRequiredString(candidate.id, `versions.${version}.publisher.id`),\n name: this.readRequiredString(candidate.name, `versions.${version}.publisher.name`),\n url: this.readOptionalString(candidate.url, `versions.${version}.publisher.url`),\n };\n };\n\n private readRequiredString = (value: unknown, fieldName: string): string => {\n if (typeof value !== \"string\" || !value.trim()) {\n throw new Error(`${fieldName} 必须是非空字符串。`);\n }\n return value.trim();\n };\n\n private readOptionalString = (\n value: unknown,\n fieldName: string,\n ): string | undefined => {\n if (value === undefined) {\n return undefined;\n }\n return this.readRequiredString(value, fieldName);\n };\n\n private normalizeBundleFileName = (appId: string): string => {\n return appId.replace(/[^a-zA-Z0-9._-]+/g, \"-\");\n };\n\n private readGetResponse = async <T>(\n url: string,\n operation: string,\n read: (response: Response) => Promise<T>,\n ): Promise<T> => {\n let lastError: unknown;\n for (let attempt = 1; attempt <= MAX_REGISTRY_GET_ATTEMPTS; attempt += 1) {\n try {\n const response = await fetch(url);\n if (!response.ok) {\n if (this.isTransientHttpStatus(response.status) && attempt < MAX_REGISTRY_GET_ATTEMPTS) {\n await response.body?.cancel();\n await this.waitBeforeRetry(attempt);\n continue;\n }\n throw new Error(`${operation}:${response.status} ${response.statusText}`);\n }\n try {\n return await read(response);\n } catch (error) {\n if (!this.isTransientNetworkError(error) || attempt === MAX_REGISTRY_GET_ATTEMPTS) {\n throw error;\n }\n lastError = error;\n }\n } catch (error) {\n if (!this.isTransientNetworkError(error) || attempt === MAX_REGISTRY_GET_ATTEMPTS) {\n if (this.isTransientNetworkError(error)) {\n throw new Error(\n `${operation}失败(已尝试 ${MAX_REGISTRY_GET_ATTEMPTS} 次)。`,\n { cause: error },\n );\n }\n throw error;\n }\n lastError = error;\n }\n await this.waitBeforeRetry(attempt);\n }\n throw new Error(\n `${operation}失败(已尝试 ${MAX_REGISTRY_GET_ATTEMPTS} 次)。`,\n { cause: lastError },\n );\n };\n\n private isTransientHttpStatus = (status: number): boolean => {\n return status === 408 || status === 429 || status >= 500;\n };\n\n private isTransientNetworkError = (error: unknown): boolean => {\n return error instanceof TypeError;\n };\n\n private waitBeforeRetry = async (attempt: number): Promise<void> => {\n await new Promise<void>((resolve) => {\n setTimeout(resolve, attempt * 100);\n });\n };\n\n private normalizeRegistryUrl = (registryUrl: string): string => {\n let normalized: URL;\n try {\n normalized = new URL(registryUrl);\n } catch {\n throw new Error(`非法 registry URL:${registryUrl}`);\n }\n if (normalized.protocol !== \"http:\" && normalized.protocol !== \"https:\") {\n throw new Error(`registry URL 只支持 http/https:${registryUrl}`);\n }\n const text = normalized.toString();\n return text.endsWith(\"/\") ? text : `${text}/`;\n };\n}\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { AppRuntimeOptionsService } from "./app-runtime-options.service.js";
2
2
 
3
- //#region src/cli/app-runtime-cli.service.d.ts
3
+ //#region src/services/app-runtime-cli.service.d.ts
4
4
  declare class AppRuntimeCliService {
5
5
  private readonly optionsService;
6
6
  constructor(optionsService?: AppRuntimeOptionsService);
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-runtime-cli.service.d.ts","names":[],"sources":["../../src/services/app-runtime-cli.service.ts"],"mappings":";;;cAqBa,oBAAA;EAAA,iBAEQ,cAAA;cAAA,cAAA,GAAgB,wBAAA;EAGnC,GAAA,QAAgB,OAAA;EAAA,QAkBR,QAAA;EAAA,QA8DA,YAAA;EAAA,QAWA,aAAA;EAAA,QAUA,WAAA;EAAA,QAsBA,YAAA;EAAA,QAwBA,SAAA;EAAA,QAcA,SAAA;EAAA,QAcA,UAAA;EAAA,QAYA,aAAA;EAAA,QAcA,qBAAA;EAAA,QAYA,aAAA;EAAA,QAWA,YAAA;EAAA,QAYA,eAAA;EAAA,QAWA,UAAA;EAAA,QAQA,UAAA;EAAA,QAUA,cAAA;EAAA,QA8CA,iBAAA;EAAA,QAUA,WAAA;EAAA,QAWA,YAAA;EAAA,QAWA,UAAA;EAAA,QAwBA,KAAA;AAAA"}
@@ -1,24 +1,24 @@
1
1
  import { version } from "../package.js";
2
- import { CreateCommand } from "../commands/create.controller.js";
3
- import { AppRuntimeToolchainService } from "../runtime/app-runtime-toolchain.service.js";
4
- import { AppBuildService } from "../runtime/app-build.service.js";
5
- import { RunCommand } from "../commands/run.controller.js";
6
- import { DevCommand } from "../commands/dev.controller.js";
7
- import { GrantCommand } from "../commands/grant.controller.js";
8
- import { InfoCommand } from "../commands/info.controller.js";
9
- import { InspectCommand } from "../commands/inspect.controller.js";
10
- import { InstallCommand } from "../commands/install.controller.js";
11
- import { ListCommand } from "../commands/list.controller.js";
12
- import { PackCommand } from "../commands/pack.controller.js";
13
- import { PermissionsCommand } from "../commands/permissions.controller.js";
14
- import { PublishCommand } from "../commands/publish.controller.js";
15
- import { RegistryCommand } from "../commands/registry.controller.js";
16
- import { RevokeCommand } from "../commands/revoke.controller.js";
17
- import { UninstallCommand } from "../commands/uninstall.controller.js";
18
- import { UpdateCommand } from "../commands/update.controller.js";
19
- import { ValidatePublishCommand } from "../commands/validate-publish.controller.js";
2
+ import { CreateCommand } from "../controllers/create.controller.js";
3
+ import { AppRuntimeToolchainService } from "./app-runtime-toolchain.service.js";
4
+ import { AppBuildService } from "./app-build.service.js";
5
+ import { RunCommand } from "../controllers/run.controller.js";
6
+ import { DevCommand } from "../controllers/dev.controller.js";
7
+ import { GrantCommand } from "../controllers/grant.controller.js";
8
+ import { InfoCommand } from "../controllers/info.controller.js";
9
+ import { InspectCommand } from "../controllers/inspect.controller.js";
10
+ import { InstallCommand } from "../controllers/install.controller.js";
11
+ import { ListCommand } from "../controllers/list.controller.js";
12
+ import { PackCommand } from "../controllers/pack.controller.js";
13
+ import { PermissionsCommand } from "../controllers/permissions.controller.js";
14
+ import { PublishCommand } from "../controllers/publish.controller.js";
15
+ import { RegistryCommand } from "../controllers/registry.controller.js";
16
+ import { RevokeCommand } from "../controllers/revoke.controller.js";
17
+ import { UninstallCommand } from "../controllers/uninstall.controller.js";
18
+ import { UpdateCommand } from "../controllers/update.controller.js";
19
+ import { ValidatePublishCommand } from "../controllers/validate-publish.controller.js";
20
20
  import { AppRuntimeOptionsService } from "./app-runtime-options.service.js";
21
- //#region src/cli/app-runtime-cli.service.ts
21
+ //#region src/services/app-runtime-cli.service.ts
22
22
  var AppRuntimeCliService = class {
23
23
  constructor(optionsService = new AppRuntimeOptionsService()) {
24
24
  this.optionsService = optionsService;