@fased/fased 0.1.36 → 0.1.38

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 (240) hide show
  1. package/config/capability-catalog.json +241 -0
  2. package/config/dependency-ownership.json +130 -0
  3. package/config/official-channel-catalog.json +39 -0
  4. package/dist/agents/tools/web-fetch-utils.js +2 -1
  5. package/dist/agents/tools/web-fetch-visibility.js +3 -1
  6. package/dist/brand.js +1 -1
  7. package/dist/browser/pw-session.js +2 -1
  8. package/dist/browser/pw-tools-core.state.js +3 -2
  9. package/dist/browser/runtime-dependency.js +27 -0
  10. package/dist/build-info.json +3 -3
  11. package/dist/canvas-host/a2ui/.bundle.hash +1 -1
  12. package/dist/capabilities/catalog.js +228 -0
  13. package/dist/cli/components-cli.js +105 -0
  14. package/dist/cli/hooks-cli.js +1 -1
  15. package/dist/cli/plugins-cli.js +3 -3
  16. package/dist/cli/program/register.subclis.js +9 -0
  17. package/dist/cli/update-cli/update-command.js +1 -1
  18. package/dist/commands/doctor-capabilities.js +26 -0
  19. package/dist/commands/doctor.js +2 -0
  20. package/dist/control-ui/assets/{app-DXYMrRJ9.js → app-JBrTSsNu.js} +1480 -1465
  21. package/dist/control-ui/assets/{index-iGQkJE0l.js → index-H-2DWAnH.js} +3 -3
  22. package/dist/control-ui/index.html +1 -1
  23. package/dist/gateway/method-scopes.js +1 -0
  24. package/dist/gateway/server-methods/plugins-marketplace.js +1 -1
  25. package/dist/gateway/server-methods/services.js +8 -0
  26. package/dist/gateway/server-methods-list.js +1 -0
  27. package/dist/infra/optional-runtime-dependency.js +46 -0
  28. package/dist/media/image-ops.js +6 -1
  29. package/dist/media/input-files.js +11 -2
  30. package/dist/media/mime.js +6 -1
  31. package/dist/memory/sqlite-vec.js +7 -1
  32. package/dist/plugin-sdk/{accounts-VqAX3HJ0.js → accounts-BwkH5bTD.js} +1 -1
  33. package/dist/plugin-sdk/{accounts-Zgt0NP54.js → accounts-RT6y7n3H.js} +1 -1
  34. package/dist/plugin-sdk/{actions-lB3PCwsz.js → actions-BrU_JMqM.js} +2 -2
  35. package/dist/plugin-sdk/{actions-BI5QViLk.js → actions-CUdVHzeJ.js} +2 -2
  36. package/dist/plugin-sdk/{active-listener-DKZiSaRA.js → active-listener-CE8t5g6l.js} +1 -1
  37. package/dist/plugin-sdk/{active-listener-D6_pdTMC.js → active-listener-Dx1SEGDn.js} +1 -1
  38. package/dist/plugin-sdk/{audio-preflight-Dx8J7XhV.js → audio-preflight-BPK8t0tI.js} +22 -22
  39. package/dist/plugin-sdk/{audio-preflight-CeRstduc.js → audio-preflight-kCa_KGRH.js} +22 -22
  40. package/dist/plugin-sdk/{audit-BImNOQvk.js → audit-DxB2Kml5.js} +11 -11
  41. package/dist/plugin-sdk/{audit-CKWkq0Un.js → audit-sSeRDwJb.js} +11 -11
  42. package/dist/plugin-sdk/channel-plugin-common.js +3 -2
  43. package/dist/plugin-sdk/{chunk-skAJZWCT.js → chunk-KCxbJLsV.js} +1 -1
  44. package/dist/plugin-sdk/{chunk-Cjw4712g.js → chunk-M2d7r-Zt.js} +1 -1
  45. package/dist/plugin-sdk/{chunk-items-Df5811D0.js → chunk-items-DGHUeFuI.js} +1 -1
  46. package/dist/plugin-sdk/command-status.js +22 -22
  47. package/dist/plugin-sdk/{commands-registry-5J619bQ2.js → commands-registry-CdT-nl7v.js} +2 -2
  48. package/dist/plugin-sdk/{commands-registry-CoLAT03J.js → commands-registry-DoWdYAJd.js} +2 -2
  49. package/dist/plugin-sdk/{common-CSTxewqY.js → common-Cpw-vuuM.js} +1 -1
  50. package/dist/plugin-sdk/{common-BiF0sTnY.js → common-DJWrErdE.js} +1 -1
  51. package/dist/plugin-sdk/{config-BxVjLTIv.js → config-C0qggNKQ.js} +7 -7
  52. package/dist/plugin-sdk/{config-TFokWwvO.js → config-CgwTaper.js} +45 -2
  53. package/dist/plugin-sdk/{config-DXlISPlx.js → config-DtqAN2L3.js} +45 -2
  54. package/dist/plugin-sdk/discord.js +2 -1
  55. package/dist/plugin-sdk/{fetch-guard-DPMynms8.js → fetch-guard-BLdujDnt.js} +1 -1
  56. package/dist/plugin-sdk/{fetch-guard-COLmW2TM.js → fetch-guard-DTKgMN9f.js} +1 -1
  57. package/dist/plugin-sdk/{handle-action-C-UndB8E.js → handle-action-B61D85yo.js} +12 -12
  58. package/dist/plugin-sdk/{handle-action-hhufKxmU.js → handle-action-CjcxGzQD.js} +12 -12
  59. package/dist/plugin-sdk/index.js +22 -22
  60. package/dist/plugin-sdk/{login-DL1oBBfL.js → login-CB_nCjX3.js} +3 -3
  61. package/dist/plugin-sdk/{login-Dnxas1dI.js → login-CzUuDd1v.js} +3 -3
  62. package/dist/plugin-sdk/{login-qr-CVQsGWKq.js → login-qr-D7MEpFza.js} +3 -3
  63. package/dist/plugin-sdk/{login-qr-B343JNXJ.js → login-qr-DgxfBFVN.js} +3 -3
  64. package/dist/plugin-sdk/{manager-BytwRIfK.js → manager-9see0IX5.js} +8 -3
  65. package/dist/plugin-sdk/{manager-BZjd_PCt.js → manager-D2kZ0Cps.js} +8 -3
  66. package/dist/plugin-sdk/{markdown-tables-krKiU4cT.js → markdown-tables-CCSD52rA.js} +1 -1
  67. package/dist/plugin-sdk/{markdown-tables-9rJDQ1X-.js → markdown-tables-DWUNvLrc.js} +1 -1
  68. package/dist/plugin-sdk/{media-D7ZkWU-A.js → media-C7YT4DeU.js} +13 -4
  69. package/dist/plugin-sdk/{media-C6C2c9a4.js → media-Cxw-xLGU.js} +13 -4
  70. package/dist/plugin-sdk/{monitor-BsLMfn3U.js → monitor-6lkdOqhQ.js} +26 -26
  71. package/dist/plugin-sdk/{monitor-BCxkQBoh.js → monitor-C6Ohw9BA.js} +26 -26
  72. package/dist/plugin-sdk/{monitor-BzDW50H6.js → monitor-o20KTHIi.js} +29 -29
  73. package/dist/plugin-sdk/{monitor-CVQNDfhs.js → monitor-rOs3mUol.js} +27 -27
  74. package/dist/plugin-sdk/{openresponses-http-BB67Z4QM.js → openresponses-http-BJONnUdy.js} +22 -22
  75. package/dist/plugin-sdk/{openresponses-http-NE76GGhj.js → openresponses-http-DkWqYznu.js} +22 -22
  76. package/dist/plugin-sdk/{ssrf-u0Ul7okY.js → optional-runtime-dependency-szS49voX.js} +46 -2
  77. package/dist/plugin-sdk/{outbound-PGNWU6T-.js → outbound-BCxOxdi-.js} +6 -6
  78. package/dist/plugin-sdk/{outbound-BHWJD9q0.js → outbound-JIwQbpRY.js} +6 -6
  79. package/dist/plugin-sdk/{outbound-attachment-BVzfhqnk.js → outbound-attachment-C-jSFaEB.js} +1 -1
  80. package/dist/plugin-sdk/{outbound-attachment-ClMvNW17.js → outbound-attachment-D6tj5Y5H.js} +1 -1
  81. package/dist/plugin-sdk/{pi-embedded-runner-DQ6UPx6-.js → pi-embedded-runner-DqDy0MWN.js} +72 -60
  82. package/dist/plugin-sdk/{pi-model-discovery-D51L5E9I.js → pi-model-discovery-DadlvsZW.js} +1 -1
  83. package/dist/plugin-sdk/{pi-model-discovery-runtime-DxoFm4Vd.js → pi-model-discovery-runtime-C5ssE6EW.js} +1 -1
  84. package/dist/plugin-sdk/{pi-model-discovery-runtime-DRuh4PNV.js → pi-model-discovery-runtime-DAaOPt64.js} +2 -2
  85. package/dist/plugin-sdk/{pi-model-discovery-runtime-BTWgkx4x.js → pi-model-discovery-runtime-gd77b0UF.js} +2 -2
  86. package/dist/plugin-sdk/{pi-model-discovery-CkH5l9X5.js → pi-model-discovery-yifTIDIn.js} +1 -1
  87. package/dist/plugin-sdk/{pw-ai-DZs6uYRd.js → pw-ai-Boor5vYw.js} +6 -5
  88. package/dist/plugin-sdk/{pw-ai-Cn-EUPZK.js → pw-ai-sH1Pi-Fh.js} +6 -5
  89. package/dist/plugin-sdk/{registry-B4HvWzob.js → registry-DNkJsNsz.js} +4 -3
  90. package/dist/plugin-sdk/{replies-jDMUBsNO.js → replies-fvzN5wnU.js} +2 -2
  91. package/dist/plugin-sdk/{replies-B_LW9c9f.js → replies-zebgZCHX.js} +2 -2
  92. package/dist/plugin-sdk/{resolve-route-_YZHbS-z.js → resolve-route-CNhTJG_2.js} +1 -1
  93. package/dist/plugin-sdk/{resolve-route-DlHzggnG.js → resolve-route-DO0WR4nB.js} +1 -1
  94. package/dist/plugin-sdk/{resolve-utils-zBhVX3Vx.js → resolve-utils-bixHYDBj.js} +1 -1
  95. package/dist/plugin-sdk/{retry-policy-DGLs5ebP.js → retry-policy-BLYEgIwP.js} +1 -1
  96. package/dist/plugin-sdk/{retry-policy-DaodShGa.js → retry-policy-sM01QZT2.js} +1 -1
  97. package/dist/plugin-sdk/{runtime-Dd0sswuW.js → runtime-BPyuAmka.js} +4 -3
  98. package/dist/plugin-sdk/{paths-CBCpJ5bu.js → runtime-dependency-Csswyv5r.js} +27 -3
  99. package/dist/plugin-sdk/{paths-DW-U2r7Y.js → runtime-dependency-Deg_sw2k.js} +36 -12
  100. package/dist/plugin-sdk/sat-runtime.js +1 -0
  101. package/dist/plugin-sdk/{send-8remZhsH.js → send-B09HRfHN.js} +6 -6
  102. package/dist/plugin-sdk/{send-DaaAscWj.js → send-BBYcgXA4.js} +5 -5
  103. package/dist/plugin-sdk/{send-DFTB0cnx.js → send-BQXQA81e.js} +6 -6
  104. package/dist/plugin-sdk/{send-CtMVvMJs.js → send-BzFdvD_i.js} +6 -6
  105. package/dist/plugin-sdk/{send-Di9irjS-.js → send-C3u-F_8s.js} +6 -6
  106. package/dist/plugin-sdk/{send-Dl88ZKq2.js → send-D3836_UN.js} +6 -6
  107. package/dist/plugin-sdk/{send-ifgZCHVB.js → send-D7tr-EsH.js} +5 -5
  108. package/dist/plugin-sdk/{send-DvUpYh0d.js → send-DRGzRdI3.js} +6 -6
  109. package/dist/plugin-sdk/{send-D2jlPeKH.js → send-DfkPKz_0.js} +4 -4
  110. package/dist/plugin-sdk/{send-QSt6nbny.js → send-DsxFXrCO.js} +4 -4
  111. package/dist/plugin-sdk/{session-CknrceVU.js → session-DG_bYisw.js} +2 -2
  112. package/dist/plugin-sdk/{session-CyRTcJ8N.js → session-EIStzK0E.js} +2 -2
  113. package/dist/plugin-sdk/{skill-commands-DHxkTA0s.js → skill-commands-CkgGmWBS.js} +2 -2
  114. package/dist/plugin-sdk/{skill-commands-CLXAG5sU.js → skill-commands-hyZA0LE3.js} +2 -2
  115. package/dist/plugin-sdk/{slack-Br9c6Yzs.js → slack-BErIh0uj.js} +34 -34
  116. package/dist/plugin-sdk/{slack-BpJXkwxF.js → slack-CGTAN4UD.js} +34 -34
  117. package/dist/plugin-sdk/{slack-actions-8tLGt1Ht.js → slack-actions-BQawWGTJ.js} +11 -11
  118. package/dist/plugin-sdk/{slack-actions-CDSSRo8Y.js → slack-actions-DDMszx56.js} +11 -11
  119. package/dist/plugin-sdk/slack.js +2 -1
  120. package/dist/plugin-sdk/src/brand.d.ts +2 -2
  121. package/dist/plugin-sdk/src/browser/runtime-dependency.d.ts +3 -0
  122. package/dist/plugin-sdk/src/infra/optional-runtime-dependency.d.ts +6 -0
  123. package/dist/plugin-sdk/{ssrf-fG4UHKBw.js → ssrf-Cce6LubA.js} +41 -7
  124. package/dist/plugin-sdk/{status-DoxnzyMC.js → status-CCGFb0f2.js} +76 -64
  125. package/dist/plugin-sdk/{sticker-cache-zmJ39ByC.js → sticker-cache-CKAkkCoU.js} +3 -3
  126. package/dist/plugin-sdk/{sticker-cache-D7WcMYsF.js → sticker-cache-wxl1Me2i.js} +3 -3
  127. package/dist/plugin-sdk/{tables-CkxblF0u.js → tables-BD7H5ug-.js} +1 -1
  128. package/dist/plugin-sdk/{tables-RBtf8G4P.js → tables-CJE8kB35.js} +1 -1
  129. package/dist/plugin-sdk/{telegram-actions-DAeYXSLk.js → telegram-actions-BRUNEXMr.js} +25 -25
  130. package/dist/plugin-sdk/{telegram-actions-BDeS3ocd.js → telegram-actions-r86ntIKR.js} +25 -25
  131. package/dist/plugin-sdk/telegram.js +2 -1
  132. package/dist/plugin-sdk/{thread-bindings.lifecycle-DsB94WbE.js → thread-bindings.lifecycle-Djem8FWF.js} +2 -2
  133. package/dist/plugin-sdk/{thread-bindings.lifecycle-DMhV3TsR.js → thread-bindings.lifecycle-gmHv3hc7.js} +2 -2
  134. package/dist/plugin-sdk/{thread-bindings.state-DYWpy7hJ.js → thread-bindings.state-CNT2vxm3.js} +2 -2
  135. package/dist/plugin-sdk/{thread-bindings.state-DG5cBaRn.js → thread-bindings.state-D8kN2X6H.js} +2 -2
  136. package/dist/plugin-sdk/{web-DJwacWQo.js → web-02I0vmdu.js} +26 -26
  137. package/dist/plugin-sdk/{web-IJ4NoGPJ.js → web-CXz56wyu.js} +26 -26
  138. package/dist/plugin-sdk/{whatsapp-actions-DAY8hWpD.js → whatsapp-actions-68Mfs2dJ.js} +12 -12
  139. package/dist/plugin-sdk/{whatsapp-actions-6opfo-oB.js → whatsapp-actions-DRqtzvTO.js} +12 -12
  140. package/dist/plugin-sdk/whatsapp.js +2 -1
  141. package/dist/plugin-sdk/{wsl-B2euLEc2.js → wsl-DcM2PgSS.js} +3 -3
  142. package/dist/plugin-sdk/{wsl-B8eyviuv.js → wsl-PXrdWitE.js} +3 -3
  143. package/dist/plugins/update.js +1 -1
  144. package/dist/tts/tts-core.js +6 -1
  145. package/dist/wizard/onboarding.finalize.js +9 -0
  146. package/extensions/acpx/package.json +1 -1
  147. package/extensions/bluebubbles/package.json +1 -1
  148. package/extensions/copilot-proxy/package.json +1 -1
  149. package/extensions/diagnostics-otel/package.json +1 -1
  150. package/extensions/fased-federation/package.json +1 -1
  151. package/extensions/google-gemini-cli-auth/package.json +1 -1
  152. package/extensions/imessage/package.json +1 -1
  153. package/extensions/irc/package.json +1 -1
  154. package/extensions/line/package.json +1 -1
  155. package/extensions/llm-task/package.json +1 -1
  156. package/extensions/lobster/package.json +1 -1
  157. package/extensions/matrix/CHANGELOG.md +12 -0
  158. package/extensions/matrix/package.json +1 -1
  159. package/extensions/mattermost/package.json +1 -1
  160. package/extensions/memory-core/package.json +2 -2
  161. package/extensions/memory-lancedb/package.json +1 -1
  162. package/extensions/minimax-portal-auth/package.json +1 -1
  163. package/extensions/msteams/CHANGELOG.md +12 -0
  164. package/extensions/msteams/package.json +1 -1
  165. package/extensions/nextcloud-talk/package.json +1 -1
  166. package/extensions/nostr/CHANGELOG.md +12 -0
  167. package/extensions/nostr/package.json +1 -1
  168. package/extensions/open-prose/package.json +1 -1
  169. package/extensions/sat-mining/package.json +1 -1
  170. package/extensions/signal/package.json +1 -1
  171. package/extensions/synology-chat/package.json +1 -1
  172. package/extensions/tlon/package.json +1 -1
  173. package/extensions/twitch/CHANGELOG.md +12 -0
  174. package/extensions/twitch/package.json +1 -1
  175. package/extensions/voice-call/CHANGELOG.md +12 -0
  176. package/extensions/voice-call/package.json +1 -1
  177. package/extensions/zalo/CHANGELOG.md +12 -0
  178. package/extensions/zalo/package.json +1 -1
  179. package/extensions/zalouser/CHANGELOG.md +12 -0
  180. package/extensions/zalouser/package.json +1 -1
  181. package/package.json +22 -15
  182. package/extensions/feishu/fased.plugin.json +0 -10
  183. package/extensions/feishu/index.ts +0 -63
  184. package/extensions/feishu/package.json +0 -33
  185. package/extensions/feishu/skills/feishu-doc/SKILL.md +0 -211
  186. package/extensions/feishu/skills/feishu-doc/references/block-types.md +0 -103
  187. package/extensions/feishu/skills/feishu-drive/SKILL.md +0 -97
  188. package/extensions/feishu/skills/feishu-perm/SKILL.md +0 -119
  189. package/extensions/feishu/skills/feishu-wiki/SKILL.md +0 -111
  190. package/extensions/feishu/src/accounts.ts +0 -159
  191. package/extensions/feishu/src/bitable.ts +0 -713
  192. package/extensions/feishu/src/bot.ts +0 -979
  193. package/extensions/feishu/src/channel.ts +0 -359
  194. package/extensions/feishu/src/client.ts +0 -118
  195. package/extensions/feishu/src/config-schema.ts +0 -226
  196. package/extensions/feishu/src/dedup.ts +0 -54
  197. package/extensions/feishu/src/directory.ts +0 -177
  198. package/extensions/feishu/src/doc-schema.ts +0 -122
  199. package/extensions/feishu/src/docx.ts +0 -998
  200. package/extensions/feishu/src/drive-schema.ts +0 -46
  201. package/extensions/feishu/src/drive.ts +0 -232
  202. package/extensions/feishu/src/dynamic-agent.ts +0 -131
  203. package/extensions/feishu/src/external-keys.ts +0 -19
  204. package/extensions/feishu/src/media.ts +0 -447
  205. package/extensions/feishu/src/mention.ts +0 -133
  206. package/extensions/feishu/src/monitor.ts +0 -434
  207. package/extensions/feishu/src/onboarding.ts +0 -430
  208. package/extensions/feishu/src/outbound.ts +0 -56
  209. package/extensions/feishu/src/perm-schema.ts +0 -52
  210. package/extensions/feishu/src/perm.ts +0 -180
  211. package/extensions/feishu/src/policy.ts +0 -116
  212. package/extensions/feishu/src/probe.ts +0 -44
  213. package/extensions/feishu/src/reactions.ts +0 -160
  214. package/extensions/feishu/src/reply-dispatcher.ts +0 -239
  215. package/extensions/feishu/src/runtime.ts +0 -14
  216. package/extensions/feishu/src/send-result.ts +0 -29
  217. package/extensions/feishu/src/send-target.ts +0 -25
  218. package/extensions/feishu/src/send.ts +0 -313
  219. package/extensions/feishu/src/streaming-card.ts +0 -218
  220. package/extensions/feishu/src/targets.ts +0 -78
  221. package/extensions/feishu/src/tool-account.ts +0 -58
  222. package/extensions/feishu/src/tools-config.ts +0 -21
  223. package/extensions/feishu/src/types.ts +0 -81
  224. package/extensions/feishu/src/typing.ts +0 -80
  225. package/extensions/feishu/src/wiki-schema.ts +0 -55
  226. package/extensions/feishu/src/wiki.ts +0 -237
  227. package/extensions/googlechat/fased.plugin.json +0 -9
  228. package/extensions/googlechat/index.ts +0 -19
  229. package/extensions/googlechat/package.json +0 -37
  230. package/extensions/googlechat/src/accounts.ts +0 -147
  231. package/extensions/googlechat/src/actions.ts +0 -176
  232. package/extensions/googlechat/src/api.ts +0 -282
  233. package/extensions/googlechat/src/auth.ts +0 -137
  234. package/extensions/googlechat/src/channel.ts +0 -591
  235. package/extensions/googlechat/src/monitor.ts +0 -996
  236. package/extensions/googlechat/src/onboarding.ts +0 -344
  237. package/extensions/googlechat/src/runtime.ts +0 -14
  238. package/extensions/googlechat/src/targets.ts +0 -65
  239. package/extensions/googlechat/src/types.config.ts +0 -3
  240. package/extensions/googlechat/src/types.ts +0 -73
@@ -0,0 +1,228 @@
1
+ import { resolveFasedAgentPackageRootSync } from "../infra/fased-root.js";
2
+ import { loadConfig } from "../config/io.js";
3
+ import "../config/config.js";
4
+ import { buildPluginMarketplaceReport } from "../plugins/marketplace.js";
5
+ import fs from "node:fs";
6
+ import path from "node:path";
7
+
8
+ //#region src/capabilities/catalog.ts
9
+ const CAPABILITY_STATES = [
10
+ "included",
11
+ "external-required",
12
+ "not-installed",
13
+ "installed",
14
+ "configured",
15
+ "ready",
16
+ "error"
17
+ ];
18
+ const CATALOG_RELATIVE_PATH = path.join("config", "capability-catalog.json");
19
+ const DELIVERY_VALUES = new Set([
20
+ "core",
21
+ "npm-addon",
22
+ "external-runtime"
23
+ ]);
24
+ const CATEGORY_VALUES = new Set([
25
+ "core",
26
+ "crypto",
27
+ "channel",
28
+ "provider",
29
+ "runtime"
30
+ ]);
31
+ function isRecord(value) {
32
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
33
+ }
34
+ function requiredString(record, key, index) {
35
+ const value = record[key];
36
+ if (typeof value !== "string" || !value.trim()) {
37
+ throw new Error(`capability catalog entry ${index} requires ${key}`);
38
+ }
39
+ return value.trim();
40
+ }
41
+ function optionalString(record, key) {
42
+ const value = record[key];
43
+ return typeof value === "string" && value.trim() ? value.trim() : undefined;
44
+ }
45
+ function parseCatalogEntry(value, index) {
46
+ if (!isRecord(value)) {
47
+ throw new Error(`capability catalog entry ${index} must be an object`);
48
+ }
49
+ const delivery = requiredString(value, "delivery", index);
50
+ if (!DELIVERY_VALUES.has(delivery)) {
51
+ throw new Error(`capability catalog entry ${index} has invalid delivery ${delivery}`);
52
+ }
53
+ const category = requiredString(value, "category", index);
54
+ if (!CATEGORY_VALUES.has(category)) {
55
+ throw new Error(`capability catalog entry ${index} has invalid category ${category}`);
56
+ }
57
+ const externalKind = optionalString(value, "externalKind");
58
+ if (externalKind && externalKind !== "browser" && externalKind !== "model-server" && externalKind !== "local-embeddings") {
59
+ throw new Error(`capability catalog entry ${index} has invalid externalKind ${externalKind}`);
60
+ }
61
+ return {
62
+ id: requiredString(value, "id", index),
63
+ label: requiredString(value, "label", index),
64
+ category,
65
+ delivery,
66
+ description: requiredString(value, "description", index),
67
+ docsPath: requiredString(value, "docsPath", index),
68
+ surface: requiredString(value, "surface", index),
69
+ ...optionalString(value, "packageName") ? { packageName: optionalString(value, "packageName") } : {},
70
+ ...optionalString(value, "pluginId") ? { pluginId: optionalString(value, "pluginId") } : {},
71
+ ...optionalString(value, "channelId") ? { channelId: optionalString(value, "channelId") } : {},
72
+ ...optionalString(value, "providerId") ? { providerId: optionalString(value, "providerId") } : {},
73
+ ...externalKind ? { externalKind } : {},
74
+ ...value.restartRequired === true ? { restartRequired: true } : {}
75
+ };
76
+ }
77
+ function resolveCatalogPath() {
78
+ const roots = [
79
+ resolveFasedAgentPackageRootSync({ moduleUrl: import.meta.url }),
80
+ resolveFasedAgentPackageRootSync({ cwd: process.cwd() }),
81
+ process.cwd()
82
+ ].filter((entry, index, all) => Boolean(entry) && all.indexOf(entry) === index);
83
+ for (const root of roots) {
84
+ const candidate = path.join(root, CATALOG_RELATIVE_PATH);
85
+ if (fs.existsSync(candidate)) {
86
+ return candidate;
87
+ }
88
+ }
89
+ throw new Error(`Fased capability catalog not found (${CATALOG_RELATIVE_PATH})`);
90
+ }
91
+ function loadCapabilityCatalog() {
92
+ const payload = JSON.parse(fs.readFileSync(resolveCatalogPath(), "utf8"));
93
+ if (!isRecord(payload) || !Array.isArray(payload.entries)) {
94
+ throw new Error("capability catalog must contain an entries array");
95
+ }
96
+ const entries = payload.entries.map(parseCatalogEntry);
97
+ const ids = new Set();
98
+ for (const entry of entries) {
99
+ if (ids.has(entry.id)) {
100
+ throw new Error(`duplicate capability catalog id: ${entry.id}`);
101
+ }
102
+ ids.add(entry.id);
103
+ }
104
+ return entries;
105
+ }
106
+ function configRecord(value) {
107
+ return isRecord(value) ? value : {};
108
+ }
109
+ function channelConfigured(config, channelId) {
110
+ const channel = configRecord(configRecord(config.channels)[channelId]);
111
+ return Object.keys(channel).length > 0;
112
+ }
113
+ function providerConfigured(config, providerId) {
114
+ return Boolean(config.models?.providers?.[providerId]);
115
+ }
116
+ function browserConfigured(config) {
117
+ const browser = configRecord(config.browser);
118
+ return browser.enabled === true || typeof browser.executablePath === "string" || typeof browser.cdpUrl === "string" || Object.keys(configRecord(browser.profiles)).length > 0;
119
+ }
120
+ function localMemoryConfigured(config) {
121
+ return config.agents?.defaults?.memorySearch?.provider === "local";
122
+ }
123
+ function actionForState(state) {
124
+ switch (state) {
125
+ case "not-installed": return "install";
126
+ case "external-required": return "connect";
127
+ case "installed":
128
+ case "included": return "configure";
129
+ case "configured": return "test";
130
+ case "error": return "repair";
131
+ case "ready": return "none";
132
+ }
133
+ }
134
+ function resolveCoreCapability(entry, plugins) {
135
+ if (entry.pluginId) {
136
+ const plugin = plugins.plugins.find((candidate) => candidate.id === entry.pluginId);
137
+ if (plugin?.status === "error") {
138
+ return {
139
+ state: "error",
140
+ detail: plugin.error ?? `${entry.label} failed to load.`
141
+ };
142
+ }
143
+ }
144
+ return {
145
+ state: "included",
146
+ detail: "Included in the Fased core runtime."
147
+ };
148
+ }
149
+ function resolveAddonCapability(entry, config, plugins) {
150
+ const plugin = plugins.plugins.find((candidate) => candidate.id === entry.pluginId || candidate.name === entry.packageName || candidate.channels.includes(entry.channelId ?? entry.id));
151
+ const installRecord = entry.pluginId ? config.plugins?.installs?.[entry.pluginId] : undefined;
152
+ const installed = Boolean(installRecord || plugin?.hasInstallRecord || plugin?.managed || plugin?.loaded && plugin.origin !== "bundled");
153
+ if (!installed) {
154
+ return {
155
+ state: "not-installed",
156
+ detail: `${entry.packageName ?? entry.label} is available as an optional npm add-on.`
157
+ };
158
+ }
159
+ if (plugin?.status === "error") {
160
+ return {
161
+ state: "error",
162
+ detail: plugin.error ?? `${entry.label} failed to load.`
163
+ };
164
+ }
165
+ const configured = entry.channelId ? channelConfigured(config, entry.channelId) : false;
166
+ if (configured && plugin?.loaded && plugin.enabled) {
167
+ return {
168
+ state: "configured",
169
+ detail: "Add-on is installed and configured. Run its live channel check for readiness."
170
+ };
171
+ }
172
+ return {
173
+ state: "installed",
174
+ detail: plugin?.loaded ? "Add-on is installed. Complete its configuration." : "Add-on is installed and requires a Gateway restart or enable step."
175
+ };
176
+ }
177
+ function resolveExternalCapability(entry, config) {
178
+ const configured = entry.providerId ? providerConfigured(config, entry.providerId) : entry.externalKind === "browser" ? browserConfigured(config) : entry.externalKind === "local-embeddings" ? localMemoryConfigured(config) : false;
179
+ if (configured) {
180
+ return {
181
+ state: "configured",
182
+ detail: "Fased is configured to use this external runtime. Test the connection for readiness."
183
+ };
184
+ }
185
+ return {
186
+ state: "external-required",
187
+ detail: "Install or run this runtime outside Fased, then connect it from the listed surface."
188
+ };
189
+ }
190
+ function summarize(entries) {
191
+ return {
192
+ total: entries.length,
193
+ coreIncluded: entries.filter((entry) => entry.delivery === "core" && entry.state === "included").length,
194
+ optionalInstalled: entries.filter((entry) => entry.delivery === "npm-addon" && (entry.state === "installed" || entry.state === "configured" || entry.state === "ready")).length,
195
+ optionalConfigured: entries.filter((entry) => entry.state === "configured").length,
196
+ externalRequired: entries.filter((entry) => entry.state === "external-required").length,
197
+ errors: entries.filter((entry) => entry.state === "error").length
198
+ };
199
+ }
200
+ function buildCapabilityReadinessReport(params) {
201
+ const config = params?.config ?? loadConfig();
202
+ const pluginReport = params?.pluginReport ?? buildPluginMarketplaceReport({ config });
203
+ const entries = loadCapabilityCatalog().map((entry) => {
204
+ const resolved = entry.delivery === "core" ? resolveCoreCapability(entry, pluginReport) : entry.delivery === "npm-addon" ? resolveAddonCapability(entry, config, pluginReport) : resolveExternalCapability(entry, config);
205
+ return {
206
+ ...entry,
207
+ ...resolved,
208
+ action: actionForState(resolved.state)
209
+ };
210
+ });
211
+ return {
212
+ entries,
213
+ summary: summarize(entries)
214
+ };
215
+ }
216
+ function formatCapabilityReadinessSummary(report) {
217
+ const { summary } = report;
218
+ return [
219
+ `Core included: ${summary.coreIncluded}`,
220
+ `Add-ons installed: ${summary.optionalInstalled}`,
221
+ `Configured: ${summary.optionalConfigured}`,
222
+ `External required: ${summary.externalRequired}`,
223
+ `Errors: ${summary.errors}`
224
+ ].join(" · ");
225
+ }
226
+
227
+ //#endregion
228
+ export { buildCapabilityReadinessReport, formatCapabilityReadinessSummary, loadCapabilityCatalog };
@@ -0,0 +1,105 @@
1
+ import { theme } from "../terminal/theme.js";
2
+ import { defaultRuntime } from "../runtime.js";
3
+ import { installPluginFromNpmSpec } from "../plugins/install.js";
4
+ import { loadConfig, writeConfigFile } from "../config/io.js";
5
+ import "../config/config.js";
6
+ import { formatDocsLink } from "../terminal/links.js";
7
+ import { buildNpmResolutionInstallFields } from "../plugins/installs.js";
8
+ import { finalizeInstalledPluginConfig } from "../plugins/lifecycle.js";
9
+ import { buildCapabilityReadinessReport, formatCapabilityReadinessSummary, loadCapabilityCatalog } from "../capabilities/catalog.js";
10
+ import { renderTable } from "../terminal/table.js";
11
+
12
+ //#region src/cli/components-cli.ts
13
+ function registerComponentsCli(program) {
14
+ const components = program.command("components").description("Show core, add-on, and external component readiness").option("--json", "Output JSON", false).addHelpText("after", () => `\n${theme.muted("Docs:")} ${formatDocsLink("/install/components", "docs.fased.ai/install/components")}\n`).action((options) => {
15
+ renderComponentReport(options);
16
+ });
17
+ components.command("list").description("Show core, add-on, and external component readiness").option("--json", "Output JSON", false).action((options) => {
18
+ renderComponentReport(options);
19
+ });
20
+ components.command("install").description("Install a cataloged optional component").argument("<id>", "Component id from `fased components`").action(async (id) => {
21
+ const entry = loadCapabilityCatalog().find((candidate) => candidate.id === id);
22
+ if (!entry) {
23
+ throw new Error(`Unknown component: ${id}. Run \`fased components\` to list choices.`);
24
+ }
25
+ if (entry.delivery !== "npm-addon" || !entry.packageName || !entry.pluginId) {
26
+ throw new Error(`${entry.label} is delivered as ${entry.delivery} and cannot be installed as an add-on. See ${entry.docsPath}.`);
27
+ }
28
+ const result = await installPluginFromNpmSpec({ spec: entry.packageName });
29
+ if (!result.ok) {
30
+ throw new Error(result.error);
31
+ }
32
+ const config = loadConfig();
33
+ const finalized = finalizeInstalledPluginConfig({
34
+ config,
35
+ pluginId: result.pluginId,
36
+ refreshManifestRegistry: true,
37
+ installRecord: {
38
+ source: "npm",
39
+ spec: entry.packageName,
40
+ installPath: result.targetDir,
41
+ version: result.version,
42
+ ...buildNpmResolutionInstallFields(result.npmResolution)
43
+ }
44
+ });
45
+ await writeConfigFile(finalized.config);
46
+ for (const warning of finalized.slotWarnings) {
47
+ defaultRuntime.log(theme.warn(warning));
48
+ }
49
+ defaultRuntime.log(`Installed component: ${entry.label} (${entry.packageName})`);
50
+ if (entry.restartRequired !== false) {
51
+ defaultRuntime.log("Restart the gateway to apply the component.");
52
+ }
53
+ });
54
+ }
55
+ function renderComponentReport(options) {
56
+ const report = buildCapabilityReadinessReport();
57
+ if (options.json) {
58
+ defaultRuntime.log(JSON.stringify(report, null, 2));
59
+ return;
60
+ }
61
+ const width = Math.max(80, (process.stdout.columns ?? 120) - 1);
62
+ defaultRuntime.log(theme.heading("Fased components"));
63
+ defaultRuntime.log(theme.muted(formatCapabilityReadinessSummary(report)));
64
+ defaultRuntime.log(renderTable({
65
+ width,
66
+ columns: [
67
+ {
68
+ key: "Component",
69
+ header: "Component",
70
+ minWidth: 18,
71
+ flex: true
72
+ },
73
+ {
74
+ key: "Category",
75
+ header: "Category",
76
+ minWidth: 10
77
+ },
78
+ {
79
+ key: "Delivery",
80
+ header: "Delivery",
81
+ minWidth: 16
82
+ },
83
+ {
84
+ key: "State",
85
+ header: "State",
86
+ minWidth: 18
87
+ },
88
+ {
89
+ key: "Next",
90
+ header: "Next",
91
+ minWidth: 10
92
+ }
93
+ ],
94
+ rows: report.entries.map((entry) => ({
95
+ Component: entry.label,
96
+ Category: entry.category,
97
+ Delivery: entry.delivery,
98
+ State: entry.state,
99
+ Next: entry.action
100
+ }))
101
+ }).trimEnd());
102
+ }
103
+
104
+ //#endregion
105
+ export { registerComponentsCli };
@@ -7,8 +7,8 @@ import { resolveArchiveKind } from "../infra/archive.js";
7
7
  import { loadConfig, writeConfigFile } from "../config/io.js";
8
8
  import { promptYesNo } from "./prompt.js";
9
9
  import { formatDocsLink } from "../terminal/links.js";
10
- import { renderTable } from "../terminal/table.js";
11
10
  import { buildPluginStatusReport } from "../plugins/status.js";
11
+ import { renderTable } from "../terminal/table.js";
12
12
  import { loadWorkspaceHookEntries } from "../hooks/workspace.js";
13
13
  import { buildWorkspaceHookStatus } from "../hooks/hooks-status.js";
14
14
  import { installHooksFromNpmSpec, installHooksFromPath, resolveHookInstallDir } from "../hooks/install.js";
@@ -8,14 +8,14 @@ import { loadConfig, writeConfigFile } from "../config/io.js";
8
8
  import "../config/config.js";
9
9
  import { promptYesNo } from "./prompt.js";
10
10
  import { formatDocsLink } from "../terminal/links.js";
11
- import { renderTable } from "../terminal/table.js";
11
+ import { findBundledPluginByNpmSpec } from "../plugins/bundled-sources.js";
12
12
  import { setPluginEnabledInConfig } from "../plugins/toggle-config.js";
13
13
  import { enablePluginInConfig } from "../plugins/enable.js";
14
14
  import { buildPluginStatusReport } from "../plugins/status.js";
15
- import { findBundledPluginByNpmSpec } from "../plugins/bundled-sources.js";
16
15
  import { applySlotSelectionForPlugin, buildPluginLifecycleReport, buildPluginUninstallPreview, executePluginUninstallLifecycle, executePluginUpdateLifecycle, finalizeInstalledPluginConfig, resolvePluginLifecycleEntry } from "../plugins/lifecycle.js";
17
- import { buildPluginMarketplaceUpdateReview, formatPluginMarketplaceUpdateReviewWarnings } from "../plugins/marketplace-update-review.js";
18
16
  import { buildPluginMarketplaceReport } from "../plugins/marketplace.js";
17
+ import { renderTable } from "../terminal/table.js";
18
+ import { buildPluginMarketplaceUpdateReview, formatPluginMarketplaceUpdateReviewWarnings } from "../plugins/marketplace-update-review.js";
19
19
  import { getPluginRuntimeSessionReadGrant, setPluginRuntimeSessionReadGrant } from "../plugins/runtime-helper-grants.js";
20
20
  import { resolvePinnedNpmInstallRecordForCli } from "./npm-resolution.js";
21
21
  import { formatPluginSourceForTable, resolvePluginSourceRoots } from "../plugins/source-display.js";
@@ -318,6 +318,15 @@ const entries = [
318
318
  mod.registerSkillsCli(program);
319
319
  }
320
320
  },
321
+ {
322
+ name: "components",
323
+ description: "Show core, add-on, and external component readiness",
324
+ hasSubcommands: false,
325
+ register: async (program) => {
326
+ const mod = await import("../components-cli.js");
327
+ mod.registerComponentsCli(program);
328
+ }
329
+ },
321
330
  {
322
331
  name: "update",
323
332
  description: "Update FasedAgent and inspect update channel status",
@@ -14,10 +14,10 @@ import { renderRestartDiagnostics, terminateStaleGatewayPids, waitForGatewayHeal
14
14
  import { runDaemonRestart } from "../daemon-cli/lifecycle.js";
15
15
  import "../../daemon-cli.js";
16
16
  import { installCompletion } from "../completion-cli.js";
17
+ import { syncPluginsForUpdateChannel, updateNpmInstalledPlugins } from "../../plugins/update.js";
17
18
  import { DEFAULT_GIT_CHANNEL, DEFAULT_PACKAGE_CHANNEL, channelToNpmTag, normalizeUpdateChannel } from "../../infra/update-channels.js";
18
19
  import { cleanupGlobalRenameDirs, globalInstallArgs, resolveGlobalPackageRoot, resolveNodeModulesRootForPackageRoot } from "../../infra/update-global.js";
19
20
  import { checkUpdateStatus, compareSemverStrings, resolveNpmChannelTag } from "../../infra/update-check.js";
20
- import { syncPluginsForUpdateChannel, updateNpmInstalledPlugins } from "../../plugins/update.js";
21
21
  import { readPackageName, readPackageVersion } from "../../infra/package-json.js";
22
22
  import { runGatewayUpdate } from "../../infra/update-runner.js";
23
23
  import { checkShellCompletionStatus, ensureCompletionCacheExists } from "../../commands/doctor-completion.js";
@@ -0,0 +1,26 @@
1
+ import { buildCapabilityReadinessReport } from "../capabilities/catalog.js";
2
+ import { note } from "../terminal/note.js";
3
+
4
+ //#region src/commands/doctor-capabilities.ts
5
+ function buildDoctorCapabilityLines(report) {
6
+ const optionalAvailable = report.entries.filter((entry) => entry.delivery === "npm-addon" && entry.state === "not-installed").length;
7
+ const lines = [
8
+ `Core included: ${report.summary.coreIncluded}`,
9
+ `Add-ons installed: ${report.summary.optionalInstalled}`,
10
+ `External runtimes configured: ${report.entries.filter((entry) => entry.delivery === "external-runtime" && entry.state === "configured").length}`
11
+ ];
12
+ if (optionalAvailable > 0) {
13
+ lines.push(`Optional add-ons available: ${optionalAvailable} (not an error)`);
14
+ }
15
+ for (const entry of report.entries.filter((candidate) => candidate.state === "error")) {
16
+ lines.push(`ERROR ${entry.label}: ${entry.detail}`);
17
+ }
18
+ return lines;
19
+ }
20
+ function noteCapabilityReadiness(config) {
21
+ const report = buildCapabilityReadinessReport({ config });
22
+ note(buildDoctorCapabilityLines(report).join("\n"), "Components");
23
+ }
24
+
25
+ //#endregion
26
+ export { noteCapabilityReadiness };
@@ -21,6 +21,7 @@ import "./doctor-state-migrations.js";
21
21
  import { loadAndMaybeMigrateDoctorConfig } from "./doctor-config-flow.js";
22
22
  import { ensureSystemdUserLingerInteractive } from "./systemd-linger.js";
23
23
  import { maybeRemoveDeprecatedCliAuthProfiles, maybeRepairAnthropicOAuthProfileId, noteAuthProfileHealth } from "./doctor-auth.js";
24
+ import { noteCapabilityReadiness } from "./doctor-capabilities.js";
24
25
  import { doctorShellCompletion } from "./doctor-completion.js";
25
26
  import { maybeRepairGatewayDaemon } from "./doctor-gateway-daemon-flow.js";
26
27
  import { checkGatewayHealth } from "./doctor-gateway-health.js";
@@ -207,6 +208,7 @@ async function doctorCommand(runtime = defaultRuntime, options = {}) {
207
208
  }
208
209
  noteWorkspaceStatus(cfg);
209
210
  await noteMemorySearchHealth(cfg, { quietOptional: options.deep !== true });
211
+ noteCapabilityReadiness(cfg);
210
212
  await doctorShellCompletion(runtime, prompter, { nonInteractive: options.nonInteractive });
211
213
  const { healthOk } = await checkGatewayHealth({
212
214
  runtime,